2017/06/01

Roh HyunWoo

ericroh93@gmail.com

This is a copula summary for those who just start learning the concept “copula”. This note is utterly based on the reference below: https://datascienceplus.com/modelling-dependence-with-copulas/
http://www.di.fc.ul.pt/~jpn/r/copula/index.html
http://eranraviv.com/correlation-and-correlation-structure-2-copulas/
http://www.danielberg.no/presentations/astin08.pdf http://www.danielberg.no/presentations/Trondheim06.pdf http://firsttimeprogrammer.blogspot.kr/2015/02/how-to-fit-copula-model-in-r.html

Motivation

Attractive features

Dependence Concepts (Linear Correlation)

There are few more such as Concordance(Kendall’s tau and Spearman’s rho) and Tail dependence.

Linear Correlation

  • Sensitive to outliers
  • Measure the “average dependence” between X and Y
  • Invariant under only strictly increasing linear transformations
  • May be misleading in situations where multivariate df is not elliptical

In all cases, linear correlation is around 0.7
- Gumbel copula: theta = 2.0
- Clayton copula: theta = 2.2
- t copula: v = 4, p = 0.71

All four distributions below have linear correlation equal to 0.7

set.seed(100)
U1 = normalCopula(0.7,dim=2)
x <- qnorm(rCopula(500, U1))
U2 = claytonCopula(2.2, dim=2)
y <- qnorm(rCopula(500,U2))
U3 <- gumbelCopula(2.0, dim=2)
z = qnorm(rCopula(500,U3))
U4 <- tCopula(0.71,df=4,dim=2)
t = qnorm(rCopula(500,U4))
par(mfrow=c(2,2))
plot(x[,1],x[,2])
plot(y[,1],y[,2])
plot(z[,1],z[,2])
plot(t[,1],t[,2])
par(mfrow=c(1,1))

[Illustration of the potential pitfalls of the linear correaltion coefficient.]

Overview

RV가 normal이 아닌 혹은 서로 다른 분포를 따를때, 예를들어 t분포와 log-normal분포를 따를 때는 계산된 Pearson 상관계수의 값은 그 의미를 상실하게 된다. 이러한 경우 다른 Marginal dist를 가지는 RV들의 결합분포를 구해내는 것이 목적인 ‘코플라’ 라는 함수를 사용할 수 있다. 대신 그 결합분포에 대해서는 가우시안(=아르키미디안), 굼벨, t 등 어떤 분포를 따를 것인지 가정을 해주어야 하며 그 각각의 분포에 맞는 코풀라 함수가 적용되어 그 함수식의 parameter를 수치해석적으로 추정하게 된다.

Joint Distribution이 왜 중요하냐면, Joint distribution 안에는 dependence structure 정보 뿐만 아니라 각 변수들의 individual behavior 라 할 수 있는 marginal distribution이 주는 정보들이 전부다 들어있기 때문이다. 코플라는 Joint distribution에서 각각의 marginal 이 주는 정보를 제외한 나머지라고 이해할 수 있다. 다시말해서 Joint distribution을 Marginal distribution과 코풀라로 구해지는 dependence structure로 나눌 수 있다. 따라서 dependence structure를 다르게 모델링 하면 같은 marginal distribution을 사용하더라도 그것들의 Joint distribution이 다르게 나온다. 마찬가지로 같은 dependence structure를 가지더라도 marginal distribution이 다르면 다른 joint distribution이 나온다.

예를들어 pair of random variable X, Y를 생각해보자. 그리고 X,Y 각각의 누적 분포함수인 CDF를 F, G라 하고 결합 누적분포(Joint cdf)는 H라고 하자. 또한 X,Y의 확률 밀도함수(pdf)는 f,g라 하고 결합확률밀도 함수는 h라 하자. 그러면 다음의 식에 의해서 joint distribution h 는 각각의 marginal의 밀도함수의 곱(product)과 적절한 코플라 density로 decompose할 수 있다.

h(x,y) = c(F(x),G(y))f(x)g(y) <=> c(F(x),G(y)) = h(x,y) / f(x)g(y)

여기서 c는 코플라 밀도 함수를 의미한다. 위 식을 통해서 알수있는 것은 두개의 RV가 독립일때는 c(F(x),G(y)) 는 1이고 결합밀도함수는 각각의 확률밀도함수의 곱으로 나타낼 수 있게 된다. 만약에 두개의 RV가 같이 움직(co-vary)이면 h(x,y)가 단순 곱 f(x)g(y)보다 커져서 c(F(x),G(y))가 1보다 커지게 되고 반대로 RV가 반대로 움직이면 copula density가 1보다 작아지게 된다. That is, the copula pdf is the ratio of the joint pdf to what it would have been under independence. So we can also interpret the copula as the adjustment that we need to make to convert the independence pdf into the joint pdf.

cop2 <- normalCopula(param=c(0.7), dim=2, dispstr="un") # 2D copula to plot
par(mfrow=c(1,2))
persp(cop2, dCopula, main="Density", xlab="u1", ylab="u2", theta=35)
persp(cop2, pCopula, main="CDF",     xlab="u1", ylab="u2", theta=35)
par(mfrow=c(1,1))

ind.cop <- indepCopula(dim = 2)
par(mfrow=c(1,2))
persp(ind.cop, dCopula, main="Density", xlab="u1", ylab="u2", theta=35)
persp(ind.cop, pCopula, main="CDF",     xlab="u1", ylab="u2", theta=35)

Introduction

We generate n samples from a multivariate normal distribution of 3 random variables given the covariance matrix sigma using the MASS package.

#install.packages("MASS")
library(MASS)  # use: mvrnorm
set.seed(100)
m <- 3
n <- 2000
sigma <- matrix(c(1, 0.5, 0.2,
                  0.5, 1, -0.7,
                  0.2, -0.7, 1), 
                nrow=3)
x <- mvrnorm(n,mu=rep(0, m),Sigma=sigma,empirical=T)
colnames(x) <- paste0("x", 1:m)
cor(x,method='spearman') # check sample correlation
          x1         x2         x3
x1 1.0000000  0.4794505  0.1922127
x2 0.4794505  1.0000000 -0.6852345
x3 0.1922127 -0.6852345  1.0000000

Function mvrnorm is nice to generate correlated random variables but there is a restriction, the marginal functions are normal distributions as we can see below

Now we check the samples correaltion using cor() and pairplot

We are going to set method=‘spearman’ in order to use Spearman’s Rho instead of the ‘pearson’ method in the cor() function (this is not strictly necessary in our example since we are going to use an elliptical copula, if you use non-elliptical copulas you may want to use either Spearman’s Rho or Kendall’s Tau).

#install.packages("psych")
library(psych) # use: pairs.panels
pairs.panels(x)

What if we have non-normal marginals?

Many empirical evidence have skewed or heavy tailed marginals. What if we would like to create a similar correlated random variables but with arbitrary marginals, say, a Gamma(2,1), a Beta(2,2), and a t(νν=5) distribution?

Here’s a possible algorithm to do that:

1. Generate the variables xi from a Gaussian multivariate (as we did)

2. Remembering the probability integral transformation:

Transforms a set of dependent variables into a new set of independent U(0, 1) variables, given the multivariate distribution.

X∼FX⇒U=FX(X)∼U(0,1)

transform xi using the Gaussian cdf, Φ (in R is called pnorm), ui=Φ(xi), where ui have marginal uniform distributions but are still correlated as variables xi.

3. Apply, for each variable uiui, the inverse cdf of the required distribution that we wish as the marginal (eg, in R, the inverse of pnorm is qnorm), that is zi = F-1(ui)

now comes the magic trick: recall that if X is a random variable with distribution F then F(X) is uniformly distributed in the interval [0, 1]. In our toy example we already know the distribution FF for each of the three random variables so this part is quick and straightforward.

u <- pnorm(x)
pairs.panels(u)

Important Fact

“CDF로 transformation (PIT)을 하면 기존 marginal의 정보는 사라지고 dependence에 대한 정보만 남게된다.”

Note that each distribution is uniform in the [0,1] interval. Note also that the correlation the same, in fact, the transformation we applied did not change the correlation structure between the random variables. Basically we are left only with what is often referred as the dependence structure.

3D of vector u

By using the rgl library we can plot a nice 3D representation of the vector u. This is very nice for presentation purposes, you can easily rotate around the graph in case you would like to check a different perspective: go nuts! Just click on the picture and drag the mouse around.

library(rgl)
plot3d(u[,1],u[,2],u[,3],pch=20,col='navyblue')

Let’s do the 3rd step, which is to apply the inverse cdf of required distribution that we wish as the marginal.

z1 <- qgamma(u[,1], shape=2,  scale=1)
z2 <-  qbeta(u[,2], shape1=2, shape2=2)
z3 <-     qt(u[,3], df=5)
z  <- cbind(z1,z2,z3)
cor(z, meth='spearman')
          z1         z2         z3
z1 1.0000000  0.4794505  0.1922127
z2 0.4794505  1.0000000 -0.6852345
z3 0.1922127 -0.6852345  1.0000000
pairs.panels(z)

And now we got the targeted margins with distribution that we wanted. What is worth noticing is that by starting from a multivariate normal sample we have build a sample with the desired and fixed dependence structure and, basically, arbitrary marginals. I would like to emphasize that I chose totally arbitrary marginals. I could have chosen any marginal as long as it was a continuous function.

Using copula package

Let’s replicate the process above using copulas

library(copula)
set.seed(100)
# constructs an elliptical copula
myCop <- normalCopula(param=c(0.5,0.2,-0.7), dim=3, dispstr="un")
# creates a multivariate distribution via copula
myMvd <- mvdc(copula=myCop, margins=c("gamma", "beta", "t"),
              paramMargins=list(list(shape=2,  scale=1),
                                list(shape1=2, shape2=2), 
                                list(df=5)) )

Now that we have specified the dependence structure through the copula (a normal copula) and set the marginals, the mvdc() function generates the desired distribution.
Then we can generate random samples using the rmvdc() function.

z2 <- rMvdc(n, myMvd)
colnames(z2) <- paste0("z",1:m)
pairs.panels(z2)

Construction of Copula Model

  1. Determine marginal distribution
  2. Define an appropriate Copula function so as to well describe marginal distribution’s dependent structure.

There are two key quetions when building multivariate financial time series Copula model, which are univariate financial time series distribution model’s establishment and Copula function’s determination.

Marginal distribution of Financial Time series

  1. ARCH/GARCH(Autoregressive conditional heteroscedasticity)
  2. SV(stochastic volatility) As is known to all,financial time series’ conditional distribution presents lots of features such as time varying volatility, volatility clustering, skewness, kurtosis, fat tail, etc. ARCH and SV both can suitably portray financial time series’ characters and marginal distribution.

Common Binary Copula Function

Copula is accumulated distribution function making random variables’ marginal distributions connecting each other. Comparing with linearly dependent coefficient, the consistency and relational measurement derived from Copula function can catch variables’ nonlinear correlation. And different from other joint distribution modeling methods, Copula model is much more flexible and does not limit the choice of marginal distribution. Because, when the assumptions of binary or multivariate normal distribution are rejected, Copula provides different kinds of marginal distributions and functions to replace the former one so as to achieve best fitting results. Moreover, Copula model can get the parameters of correlation measurements and specific functions by organically combining random variables’ dependent degrees and patterns.

  1. Normal Copula
  2. Gumbel Copula
  3. Clayton Copula
  4. Frank Copula
    ……

A simple example of application

Now for the real world example,

We are going to fit a copula to the returns of two stocks and try to simulate the returns using the copula. From the reference website, they provide cleaned return data from two stocks. You can download it from below website. http://datascienceplus.com/wp-content/uploads/2015/10/yahoo_r.csv http://datascienceplus.com/wp-content/uploads/2015/10/cree_r.csv

Let’s load the returns in R

cree <- read.csv('cree_r.csv',header=F)$V2
yahoo <- read.csv('yahoo_r.csv',header=F)$V2

Before going straight to the copula fitting process, let’s check the correlation between the two stock returns and plot the regression line:

plot(cree,yahoo,pch='.')
abline(lm(yahoo~cree),col='red',lwd=1)

cor(cree,yahoo,method='spearman')
[1] 0.4023584

In the first example above I chose a normal copula model without much thinking, however, when applying these models to real data one should carefully think at what could suit the data better. For instance, many copulas are better suited for modelling asymetrical correlation other emphasize tail correlation a lot, and so on. My guess for stock returns is that a t-copula should be fine, however a guess is certainly not enough. Fortunately, the VineCopula package offers a great function which tells us what copula we should use. Essentially the VineCopula library allows us to perform copula selection using BIC and AIC through the function BiCopSelect()

library(VineCopula)
u <- pobs(as.matrix(cbind(cree,yahoo)))[,1]
v <- pobs(as.matrix(cbind(cree,yahoo)))[,2]
selectedCopula <- BiCopSelect(u,v,familyset=NA)
selectedCopula
Bivariate copula: t (par = 0.44, par2 = 3.84, tau = 0.29) 

Note that I fed into the selection algorithm the pseudo observations using the pobs() function. Pseudo observations are the observations in the [0,1] interval. The fitting algorithm indeed selected a t-copula (encoded as 2 in the $family reference) and estimated the parameters for us. By typing ?BiCopSelect() you can actually see the encoding for each copula.

Let’s try to fit the suggested model using the copula package and double check the parameters fitting.

t.cop <- tCopula(dim=2)
set.seed(500)
m <- pobs(as.matrix(cbind(cree,yahoo)))
fit <- fitCopula(t.cop,m,method='ml')
coef(fit)
   rho.1       df 
0.435630 3.844527 

It is nice to see that the parameters of the fitted copula are the same as those suggested by the BiCopSelect() function. Let’s take a look at the density of the copula we have just estimated

rho <- coef(fit)[1]
df <- coef(fit)[2]
persp(tCopula(dim=2,rho,df=df),dCopula)

Now we only need to build the copula and sample from it 3965 random samples. This is the plot of the samples contained in the vector u

u <- rCopula(3965,tCopula(dim=2,rho,df=df))
plot(u[,1],u[,2],pch='.',col='blue')

cor(u,method='spearman')
          [,1]      [,2]
[1,] 1.0000000 0.3839496
[2,] 0.3839496 1.0000000

The random samples from the copula look a little bit close to the independence case, but that is fine since the correlation between the returns is low. Note that the generated samples have the same correlation as the data. The t-copula emphasizes extreme results: it is usually good for modelling phenomena where there is high correlation in the extreme values (the tails of the distribution). Note also that it is symmetrical, this might be an issue for our application, however we are going to neglect this. Now we are facing the hard bit: modelling the marginals. We are going to assume normally distributed returns for simplicity even though it is well known to be a far from sound assumption. We therefore estimate the parameters of the marginals

cree_mu <- mean(cree)
cree_sd <- sd(cree)
yahoo_mu <- mean(yahoo)
yahoo_sd <- sd(yahoo)

Let’s plot the fittings against the histogram in order to gather a visual take on what we are doing:

hist(cree,breaks=80,main='Cree returns',freq=F,density=30,col='cyan',ylim=c(0,20),xlim=c(-0.2,0.3))
lines(seq(-0.5,0.5,0.01),dnorm(seq(-0.5,0.5,0.01),cree_mu,cree_sd),col='red',lwd=2)
legend('topright',c('Fitted normal'),col=c('red'),lwd=2)

hist(yahoo,breaks=80,main='Yahoo returns',density=30,col='cyan',freq=F,ylim=c(0,20),xlim=c(-0.2,0.2))
lines(seq(-0.5,0.5,0.01),dnorm(seq(-0.5,0.5,0.01),yahoo_mu,yahoo_sd),col='red',lwd=2)
legend('topright',c('Fitted normal'),col=c('red'),lwd=2)

Optionally at the beginning we could have plot the data with the distribution for each random variable as below

# Plot data with histograms
xhist <- hist(cree, breaks=80, plot=FALSE)
yhist <- hist(yahoo, breaks=80, plot=FALSE) 
top <- max(c(xhist$counts, yhist$counts)) 
xrange <- c(-0.15,0.15)
yrange <- c(-0.15,0.15)
nf <- layout(matrix(c(2,0,1,3),2,2,byrow=TRUE), c(3,1), c(1,3), TRUE) 
par(mar=c(3,3,1,1)) 
plot(cree,yahoo, xlim=xrange, ylim=yrange, xlab="", ylab="") 
par(mar=c(0,3,1,1)) 
barplot(xhist$counts, axes=FALSE, ylim=c(0, top), space=0) 
par(mar=c(3,0,1,1)) 
barplot(yhist$counts, axes=FALSE, xlim=c(0, top), space=0, horiz=TRUE)

Do not let the nice graphs fool you, under the assumptions for the distributions of the returns we are neglecting some extreme returns located in the tails of the distribution, but in this example we are interested in the joint behaviour, so that is what we are going to analyse soon.

Now we apply the copula in the mvdc() function and then use rmvdc() to get our simulated observations from the generated multivariate distribution. Finally, we compare the simulated results with the original data.

copula_dist <- mvdc(copula=tCopula(rho,dim=2,df=df), margins=c("norm","norm"),
                    paramMargins=list(list(mean=cree_mu, sd=cree_sd),
                                      list(mean=yahoo_mu, sd=yahoo_sd)))
sim <- rMvdc(3965, copula_dist)

Good, now we have simulated data, let’s make a visual comparison

plot(cree,yahoo,main='Returns')
points(sim[,1],sim[,2],col='red')
legend('bottomright',c('Observed','Simulated'),col=c('black','red'),pch=21)

This is the final scatterplot of the data under the assumption of normal marginals and t-copula for the dependence structure:

As you can see, the t-copula leads to results close to the real observations, although there are fewer extreme returns than in the real data and we missed some of the most extreme result. If we were interested in modelling the risk associated with these stocks then this would be a major red flag to be addressed with further model calibration.

Since the purpose of this example is simply to show how to fit a copula and generate random observations using real data, weare going to stop here.

Simulating with different parameters

Just as a curiosity let me show you how the simulated values from the copula would have changed by tweaking the df parameter in the t-copula. Let’s try df=1 and df=8

set.seed(4258)
copula_dist <- mvdc(copula=tCopula(rho,dim=2,df=1), margins=c("norm","norm"),
                    paramMargins=list(list(mean=cree_mu, sd=cree_sd),
                                      list(mean=yahoo_mu, sd=yahoo_sd)))
sim <- rMvdc(3965,copula_dist)
plot(cree,yahoo,main='Returns')
points(sim[,1],sim[,2],col='red')
legend('bottomright',c('Observed','Simulated df=1'),col=c('black','red'),pch=21)

cor(sim[,1],sim[,2],method='spearman')
[1] 0.3929509
copula_dist <- mvdc(copula=tCopula(rho,dim=2,df=8), margins=c("norm","norm"),
                    paramMargins=list(list(mean=cree_mu, sd=cree_sd),
                                      list(mean=yahoo_mu, sd=yahoo_sd)))
sim <- rMvdc(3965,copula_dist)
plot(cree,yahoo,main='Returns')
points(sim[,1],sim[,2],col='red')
legend('bottomright',c('Observed','Simulated df=8'),col=c('black','red'),pch=21)

cor(sim[,1],sim[,2],method='spearman')
[1] 0.4412581

Note how different are the dependence structures that we have obtained although Spearman’s Rho is similar. Clearly the parameter df is very important in determining the shape of the distribution. As df increases, the t-copula tends to a gaussian copula.

Few more examples

The dataset For the purpose of this example I used a simple dataset of returns for stock x and y (x.txt and y.txt). You can download the dataset by clicking here. https://www.dropbox.com/s/unxj0wgd7t2b0cc/dataset.zip?dl=0

First of all we need to load the data and convert it into a matrix format. Optionally one can plot the data. Remember to load the copula package with

x <- read.table("C:\\x.txt")
y <- read.table("C:\\y.txt")
mat <- matrix(nrow=100,ncol=2)
for(i in 1:100)
{
    mat[i,1] <- x[,1][i]
    mat[i,2] <- y[,1][i]
}
# Actual observations
plot(mat[,1],mat[,2],main="Returns",xlab="x",ylab="y",col="blue")

Now we have our data loaded, we can clearly see that there is some kind of positive correlation.

The next step is the fitting. In order to fit the data we need to choose a copula model. The model should be chose based on the structure of data and other factors. As a first approximation, we may say that our data shows a mild positive correlation therefore a copula which can replicate such mild correlation should be fine. Be aware that you can easily mess up with copula models and this visual approach is not always the best option. Anyway I choose to use a normal copula from the package. The fitting process anyway is identical for the other types of copula.

Let’s fit the data

# Normal copula
normal.cop <- normalCopula(dim=2)
fit.cop<- fitCopula(normal.cop,pobs(mat),method="ml")
# Coefficients
rho <- coef(fit.cop)
print(rho)
    rho.1 
0.7387389 

Note that the data must be fed through the function pobs() which converts the real observations into pseudo observations into the unit square [0,1]. Note also that we are using the “ml” method (maximum likelihood method) however other methods are available such as “itau”.

The parameter of the fitted copula, rho, in our case is equal to 0.7387409. Let’s simulate some pseudo observations

By plotting the pseudo and simulated observations we can see how the simulation with the copula matches the pseudo observations

# Pseudo observations
p_obs <- pobs(mat)
plot(p_obs, col="blue")

plot(p_obs[,1],p_obs[,2],main="Pseudo/simulated observations: BLUE/RED",xlab="u",ylab="v",col="blue")
# Simulate data
set.seed(100)
u1 = rCopula(500,normalCopula(coef(fit.cop),dim=2))
points(u1[,1],u1[,2],col="red")

LS0tDQp0aXRsZTogIkNvcHVsYSBJbnRyb2R1Y3Rpb24gXyBIeXVud29vIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCjIwMTcvMDYvMDEgDQoNCiMjI1JvaCBIeXVuV29vICANCiMjI2VyaWNyb2g5M0BnbWFpbC5jb20NCg0KVGhpcyBpcyBhIGNvcHVsYSBzdW1tYXJ5IGZvciB0aG9zZSB3aG8ganVzdCBzdGFydCBsZWFybmluZyB0aGUgY29uY2VwdCAiY29wdWxhIi4NClRoaXMgbm90ZSBpcyB1dHRlcmx5IGJhc2VkIG9uIHRoZSByZWZlcmVuY2UgYmVsb3c6DQpodHRwczovL2RhdGFzY2llbmNlcGx1cy5jb20vbW9kZWxsaW5nLWRlcGVuZGVuY2Utd2l0aC1jb3B1bGFzLyAgDQpodHRwOi8vd3d3LmRpLmZjLnVsLnB0L35qcG4vci9jb3B1bGEvaW5kZXguaHRtbCAgDQpodHRwOi8vZXJhbnJhdml2LmNvbS9jb3JyZWxhdGlvbi1hbmQtY29ycmVsYXRpb24tc3RydWN0dXJlLTItY29wdWxhcy8gIA0KaHR0cDovL3d3dy5kYW5pZWxiZXJnLm5vL3ByZXNlbnRhdGlvbnMvYXN0aW4wOC5wZGYNCmh0dHA6Ly93d3cuZGFuaWVsYmVyZy5uby9wcmVzZW50YXRpb25zL1Ryb25kaGVpbTA2LnBkZg0KaHR0cDovL2ZpcnN0dGltZXByb2dyYW1tZXIuYmxvZ3Nwb3Qua3IvMjAxNS8wMi9ob3ctdG8tZml0LWNvcHVsYS1tb2RlbC1pbi1yLmh0bWwNCg0KIyNNb3RpdmF0aW9uDQotIEZvciBzZXZlcmFsIGFwcGxpY2F0aW9ucyBlbXBpcmljYWwgZXZpZGVuY2UgaGFzIHByb3ZlZCBtdWx0aW5vcm1hbCBkaXN0cmlidXRpb24gaW5hZGVxdWF0ZSBmb3Igc2VydmVyYWwgcmVhc29ucw0KLSBNb3N0IHRyYWRpdGlvbmFsIHJlcHJlc2VudGF0aW9ucyBvZiBkZXBlbmRlbmNlIGFyZSBiYXNlZCBvbiB0aGUgbGluZWFyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IC0gcmVzdHJpY3RlZCB0byBtdWx0aXZhcmlhdGUgZWxsaXB0aWNhbCBkaXN0cmlidXRpb25zLiBlLmcuIHBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQgYmFzZWQgb24gdGhlIG11bHRpLW5vcm1hbCBhc3N1bXB0aW9uIGlzIGFsc28gdmVyeSBsaW1pdGVkLiBDb3B1bGEgcmVwcmVzZW50YXRpb25zIG9mIGRlcGVuZGVuY2UgYXJlIGZyZWUgb2Ygc3VjaCBsaW1pdGF0aW9ucy4NCi0gRmluYW5jaWFsIG1hcmtldHMgd2Ugb2Z0ZW4gc2VlIG5vbi1saW5lYXIgZGVwZW5kZW5jeSBzdHJ1Y3R1cmVzDQotIEVtcGlyaWNhbCBtYXJnaW5hbCBkaXN0cmlidXRpb25zIGFyZSBlLmcuIHNrZXdlZCBhbmQgaGVhdnktdGFpbGVkLiBNb3N0bHkgZm91bmQgaW4gZmluYW5jaWFsIGRhdGENCi0gUG9zc2liaWxpdGllcyBvZiBleHRyZW1lIGNvLW1vdmVtZW50cywgaW4gY29udHJhc3QgdG8gdGhlIG11bHRpbm9ybWFsIGRpc3RyaWJ1dGlvbg0KDQojI0F0dHJhY3RpdmUgZmVhdHVyZXMNCi0gV2l0aG91dCB0aGUgbGltaXRhdGlvbiBvZiBtYXJnaW5hbCBkaXN0cmlidXRpb24gc2VsZWN0aW9uLCBpdCBpcyBwb3NzaWJsZSB0byBjb25zdHJ1Y3QgbXVjaCBtb3JlIGZsZXhpYmxlIG11bHRpdmFyaWF0ZSBkaXN0cmlidXRpb24uIEEgY29wdWxhIGRlc2NyaWJlcyBob3cgdGhlIG1hcmdpbmFscyBhcmUgdGllZCB0b2dldGhlciBpbiB0aGUgam9pbnQgZGlzdHJpYnV0aW9uDQotIFBvc3NpYmxlIHRvIHNlcGFyYXRlbHkgc3R1ZHkgcmFuZG9tIHZhcmlhYmxlJ3MgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucyBhbmQgY29ycmVsYXRpb24gc3RydWN0dXJlLiBGb3Igc29tZSBjb21iaW5hdGlvbiBvZiBtYXJnaW5hbHMgdGhlcmUgYXJlIG5vIGJ1aWx0LWluIGZ1bmN0aW9ucyB0byBnZW5lcmF0ZSB0aGUgZGVzaXJlZCBtdWx0aXZhcmlhdGUgZGlzdHJpYnV0aW9uLiBGb3IgaW5zdGFuY2UsIGluIFIgaXQgaXMgZWFzeSB0byBnZW5lcmF0ZSByYW5kb20gc2FtcGxlcyBmcm9tIGEgbXVsdGl2YXJpYXRlIG5vcm1hbCBkaXN0cmlidXRpb24sIGhvd2V2ZXIgaXQgaXMgbm90IHNvIGVhc3kgdG8gZG8gdGhlIHNhbWUgd2l0aCBzYXkgYSBkaXN0cmlidXRpb24gd2hvc2UgbWFyZ2lucyBhcmUgQmV0YSwgR2FtbWEgYW5kIFNUdWRlbnQsIHJlc3BlY3RpdmVseS4NCi0gQSBjb3B1bGEgaXMgaW52YXJpYW50IHVuZGVyIHN0cmljdGx5IGluY3JlYXNpbmcgdHJhbnNmb3JtYXRpb25zIC8gSWYgZG9pbmcgbm9ubGluZWFyIG1vbm90b25lIGluY3JlYXNpbmcgdHJhbnNmb3JtYXRpb24gdG8gdmFyaWFibGVzLCB0aGUgdmFsdWVzIG9mIGxpbmVhciBkZXBlbmRlbnQgY29lZmZpY2llbnRzIHdpbGwgY2hhbmdlLCBidXQgdGhlIGNvbnNpc3RlbmN5IGFuZCBjb3JyZWxhdGlvbiBtZWFzdXJlbWVudHMgZGVyaXZlZCBmcm9tIENvcHVsYSBmdW5jdGlvbiB3aWxsIG5vdCBjaGFuZ2Ugc28gdGhhdCBpdCBjYW4gY2F0Y2ggdmFyaWFibGVzJyBub25saW5lYXIgYW5kIGRpc3N5bW1ldHJpY2FsIGNvcnJlbGF0aW9ucy4NCi0gTW9zdCB0cmFkaXRpb25hbCBtZWFzdXJlcyBvZiBkZXBlbmRlbmNlIGFyZSBtZWFzdXJlcyBvZiBwYWlyd2lzZSBkZXBlbmRlbmNlLiBDb3B1bGFzIG1lYXN1cmUgdGhlIGRlcGVuZGVuY2UgYmV0d2VlbiBhbGwgcmFuZG9tIHZhcmlhYmxlcy4NCg0KIyMgRGVwZW5kZW5jZSBDb25jZXB0cyAoTGluZWFyIENvcnJlbGF0aW9uKQ0KVGhlcmUgYXJlIGZldyBtb3JlIHN1Y2ggYXMgQ29uY29yZGFuY2UoS2VuZGFsbCdzIHRhdSBhbmQgU3BlYXJtYW4ncyByaG8pIGFuZCBUYWlsIGRlcGVuZGVuY2UuDQoNCiMjIyBMaW5lYXIgQ29ycmVsYXRpb24NCi0gU2Vuc2l0aXZlIHRvIG91dGxpZXJzDQotIE1lYXN1cmUgdGhlICJhdmVyYWdlIGRlcGVuZGVuY2UiIGJldHdlZW4gWCBhbmQgWQ0KLSBJbnZhcmlhbnQgdW5kZXIgb25seSBzdHJpY3RseSBpbmNyZWFzaW5nIGxpbmVhciB0cmFuc2Zvcm1hdGlvbnMNCi0gTWF5IGJlIG1pc2xlYWRpbmcgaW4gc2l0dWF0aW9ucyB3aGVyZSBtdWx0aXZhcmlhdGUgZGYgaXMgbm90IGVsbGlwdGljYWwNCg0KSW4gYWxsIGNhc2VzLCBsaW5lYXIgY29ycmVsYXRpb24gaXMgYXJvdW5kIDAuNyAgDQotIEd1bWJlbCBjb3B1bGE6IHRoZXRhID0gMi4wICANCi0gQ2xheXRvbiBjb3B1bGE6IHRoZXRhID0gMi4yICANCi0gdCBjb3B1bGE6IHYgPSA0LCBwID0gMC43MSAgDQoNCiMjQWxsIGZvdXIgZGlzdHJpYnV0aW9ucyBiZWxvdyBoYXZlIGxpbmVhciBjb3JyZWxhdGlvbiBlcXVhbCB0byAwLjcNCmBgYHtyfQ0Kc2V0LnNlZWQoMTAwKQ0KVTEgPSBub3JtYWxDb3B1bGEoMC43LGRpbT0yKQ0KeCA8LSBxbm9ybShyQ29wdWxhKDUwMCwgVTEpKQ0KVTIgPSBjbGF5dG9uQ29wdWxhKDIuMiwgZGltPTIpDQp5IDwtIHFub3JtKHJDb3B1bGEoNTAwLFUyKSkNClUzIDwtIGd1bWJlbENvcHVsYSgyLjAsIGRpbT0yKQ0KeiA9IHFub3JtKHJDb3B1bGEoNTAwLFUzKSkNClU0IDwtIHRDb3B1bGEoMC43MSxkZj00LGRpbT0yKQ0KdCA9IHFub3JtKHJDb3B1bGEoNTAwLFU0KSkNCnBhcihtZnJvdz1jKDIsMikpDQpwbG90KHhbLDFdLHhbLDJdKQ0KcGxvdCh5WywxXSx5WywyXSkNCnBsb3QoelssMV0selssMl0pDQpwbG90KHRbLDFdLHRbLDJdKQ0KcGFyKG1mcm93PWMoMSwxKSkNCmBgYA0KDQpbSWxsdXN0cmF0aW9uIG9mIHRoZSBwb3RlbnRpYWwgcGl0ZmFsbHMgb2YgdGhlIGxpbmVhciBjb3JyZWFsdGlvbiBjb2VmZmljaWVudC5dDQoNCiMjIE92ZXJ2aWV3DQoNClJWsKEgbm9ybWFswMwgvsa00SDIpMC6ILytt84gtNm4pSC60Mb3uKYgtfu4pranLCC/ubimtem+7iB0utDG97/NIGxvZy1ub3JtYWy60Mb3uKYgtfu4piC2p7TCILDou+q1yCBQZWFyc29uILvzsPyw6Lz2wMcgsKrAuiCx1yDAx7nMuKYgu/O9x8fPsNQgtci02S4gwMy3r8fRILDmv+wgtNm4pSBNYXJnaW5hbCBkaXN0uKYgsKHB9rTCIFJWtenAxyCw4cfVutDG97imILG4x9izu7TCILDNwMwguPHA+8DOIKGuxNrHw7bzoa8gtvO0wiDH1Lz2uKYgu+e/68fSILz2IMDWtNkuILTrvcUgsdcgsOHH1brQxve/oSC068fYvK20wiCwob/svcO+yCg9vsa4o8Wwucy18L7IKSwgscC6pywgdCC17iC+7rayILrQxve4piC1+7imILDNwM7B9iCwocGkwLsgx9jB1r7uvt8gx8+45yCx1yCworCiwMcgutDG97+hILjCtMIgxNrHrrbzIMfUvPawoSDA+7/rtce+7iCx1yDH1Lz2vcTAxyBwYXJhbWV0ZXK4piC89sShx9i8rsD7wLi3ziDD38Gkx8+w1CC1yLTZLiANCg0KSm9pbnQgRGlzdHJpYnV0aW9uwMwgv9Ygwd+/5MfPs8S46SwgSm9pbnQgZGlzdHJpYnV0aW9uIL7Iv6G0wiBkZXBlbmRlbmNlDQpzdHJ1Y3R1cmUgwaS6uCC707i4IL7GtM+28yCwoiC6r7z2tenAxyBpbmRpdmlkdWFsIGJlaGF2aW9yILbzIMfSILz2IMDWtMIgbWFyZ2luYWwgZGlzdHJpYnV0aW9uwMwgwda0wiDBpLq4tenAzCDA/LrOtNkgtem+7sDWseIgtqe5rsDMtNkuIMTax8O287TCIEpvaW50IGRpc3RyaWJ1dGlvbr+hvK0gsKKwosDHIG1hcmdpbmFsIMDMIMHWtMIgwaS6uLimIMGmv9zH0SCzqrjTwfa287DtIMDMx9jH0iC89iDA1rTZLiC02b3DuLvH2LytIEpvaW50IGRpc3RyaWJ1dGlvbsC7IE1hcmdpbmFsIGRpc3RyaWJ1dGlvbrD6IMTax66287fOILG4x9jB9rTCIGRlcGVuZGVuY2Ugc3RydWN0dXJlt84gs6q0rSC89iDA1rTZLiC1+7bzvK0gZGVwZW5kZW5jZSBzdHJ1Y3R1cmW4piC02bijsNQguPC1qLi1IMfPuOkgsLDAuiBtYXJnaW5hbCBkaXN0cmlidXRpb27AuyC757/rx8+09bbztbUNCrHXsM216cDHIEpvaW50IGRpc3RyaWJ1dGlvbsDMILTZuKOw1CCzqr/CtNkuILi2wvmwocH2t84gsLDAuiBkZXBlbmRlbmNlIA0Kc3RydWN0dXJluKYgsKHB9rT1tvO1tSBtYXJnaW5hbCBkaXN0cmlidXRpb27AzCC02bijuOkgtNm4pSBqb2ludCANCmRpc3RyaWJ1dGlvbsDMILOqv8K02S4gDQoNCr+5uKa16b7uIHBhaXIgb2YgcmFuZG9tIHZhcmlhYmxlIFgsIFm4piC7/bCix9i6uMDaLiCx17iusO0gWCxZILCisKLAxyC0qcD7ILrQxvfH1Lz2wM4gQ0RGuKYgRiwgR7bzIMfPsO0gsOHH1SC0qcD7utDG9yhKb2ludCBjZGYptMIgSLbzsO0gx8/A2i4gtsfH0SBYLFnAxyDIrrf8ILnQtbXH1Lz2KHBkZim0wiBmLGe28yDHz7DtILDhx9XIrrf8udC1tSDH1Lz2tMIgaLbzIMfPwNouILHXt6+46SC02cC9wMcgvcS/oSDAx8fYvK0gam9pbnQgZGlzdHJpYnV0aW9uIGggtMIgsKKwosDHIG1hcmdpbmFswMcgudC1tcfUvPbAxyCw9ihwcm9kdWN0KbD6IMD7wP3H0SDE2sfDtvMgZGVuc2l0ebfOIGRlY29tcG9zZcfSILz2IMDWtNkuIA0KDQpoKHgseSkgPSBjKEYoeCksRyh5KSlmKHgpZyh5KQ0KPD0+IGMoRih4KSxHKHkpKSA9IGgoeCx5KSAvIGYoeClnKHkpDQoNCr+pseK8rSBjtMIgxNrHw7bzILnQtbUgx9S89rimIMDHuczH0bTZLiDApyC9xMC7IMXrx9i8rSC+y7z2wNa0wiCwzcC6ILXOsLPAxyBSVrChILW2uLPAz7antMIgYyhGKHgpLEcoeSkpILTCIDHAzLDtILDhx9W50LW1x9S89rTCILCisKLAxyDIrrf8udC1tcfUvPbAxyCw9sC4t84gs6rFuLO+ILz2IMDWsNQgtci02S4guLi+4L+hILXOsLPAxyBSVrChILCwwMwgv/LB9yhjby12YXJ5KcDMuOkgaCh4LHkpsKEgtNy8+CCw9iBmKHgpZyh5Kbq4tNkgxL/BrrytIGMoRih4KSxHKHkpKbChIDG6uLTZIMS/wfaw1CC1x7DtILndtOu3ziBSVrChILndtOu3ziC/8sH3wMy46SBjb3B1bGEgZGVuc2l0ebChIDG6uLTZIMDbvsbB9rDUILXItNkuIFRoYXQgaXMsIHRoZSBjb3B1bGEgcGRmIGlzIHRoZSByYXRpbyBvZiB0aGUgam9pbnQgcGRmIHRvIHdoYXQgaXQgd291bGQgaGF2ZSBiZWVuIHVuZGVyIGluZGVwZW5kZW5jZS4gU28gd2UgY2FuIGFsc28gaW50ZXJwcmV0IHRoZSBjb3B1bGEgYXMgdGhlIGFkanVzdG1lbnQgdGhhdCB3ZSBuZWVkIHRvIG1ha2UgdG8gY29udmVydCB0aGUgaW5kZXBlbmRlbmNlIHBkZiBpbnRvIHRoZSBqb2ludCBwZGYuDQoNCmBgYHtyfQ0KY29wMiA8LSBub3JtYWxDb3B1bGEocGFyYW09YygwLjcpLCBkaW09MiwgZGlzcHN0cj0idW4iKSAjIDJEIGNvcHVsYSB0byBwbG90DQpwYXIobWZyb3c9YygxLDIpKQ0KcGVyc3AoY29wMiwgZENvcHVsYSwgbWFpbj0iRGVuc2l0eSIsIHhsYWI9InUxIiwgeWxhYj0idTIiLCB0aGV0YT0zNSkNCnBlcnNwKGNvcDIsIHBDb3B1bGEsIG1haW49IkNERiIsICAgICB4bGFiPSJ1MSIsIHlsYWI9InUyIiwgdGhldGE9MzUpDQpwYXIobWZyb3c9YygxLDEpKQ0KYGBgDQoNCmBgYHtyfQ0KaW5kLmNvcCA8LSBpbmRlcENvcHVsYShkaW0gPSAyKQ0KcGFyKG1mcm93PWMoMSwyKSkNCnBlcnNwKGluZC5jb3AsIGRDb3B1bGEsIG1haW49IkRlbnNpdHkiLCB4bGFiPSJ1MSIsIHlsYWI9InUyIiwgdGhldGE9MzUpDQpwZXJzcChpbmQuY29wLCBwQ29wdWxhLCBtYWluPSJDREYiLCAgICAgeGxhYj0idTEiLCB5bGFiPSJ1MiIsIHRoZXRhPTM1KQ0KYGBgDQoNCg0KIyMgSW50cm9kdWN0aW9uDQoNCldlIGdlbmVyYXRlIG4gc2FtcGxlcyBmcm9tIGEgbXVsdGl2YXJpYXRlIG5vcm1hbCBkaXN0cmlidXRpb24gb2YgMyByYW5kb20gdmFyaWFibGVzIGdpdmVuIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBzaWdtYSB1c2luZyB0aGUgTUFTUyBwYWNrYWdlLg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikNCmxpYnJhcnkoTUFTUykgICMgdXNlOiBtdnJub3JtDQpzZXQuc2VlZCgxMDApDQoNCm0gPC0gMw0KbiA8LSAyMDAwDQpzaWdtYSA8LSBtYXRyaXgoYygxLCAwLjUsIDAuMiwNCiAgICAgICAgICAgICAgICAgIDAuNSwgMSwgLTAuNywNCiAgICAgICAgICAgICAgICAgIDAuMiwgLTAuNywgMSksIA0KICAgICAgICAgICAgICAgIG5yb3c9MykNCnggPC0gbXZybm9ybShuLG11PXJlcCgwLCBtKSxTaWdtYT1zaWdtYSxlbXBpcmljYWw9VCkNCmNvbG5hbWVzKHgpIDwtIHBhc3RlMCgieCIsIDE6bSkNCmNvcih4LG1ldGhvZD0nc3BlYXJtYW4nKSAjIGNoZWNrIHNhbXBsZSBjb3JyZWxhdGlvbg0KYGBgDQpGdW5jdGlvbiBtdnJub3JtIGlzIG5pY2UgdG8gZ2VuZXJhdGUgY29ycmVsYXRlZCByYW5kb20gdmFyaWFibGVzIGJ1dCB0aGVyZSBpcyBhIHJlc3RyaWN0aW9uLCB0aGUgbWFyZ2luYWwgZnVuY3Rpb25zIGFyZSBub3JtYWwgZGlzdHJpYnV0aW9ucyBhcyB3ZSBjYW4gc2VlIGJlbG93DQoNCk5vdyB3ZSBjaGVjayB0aGUgc2FtcGxlcyBjb3JyZWFsdGlvbiB1c2luZyBjb3IoKSBhbmQgcGFpcnBsb3QNCg0KV2UgYXJlIGdvaW5nIHRvIHNldCBtZXRob2Q9J3NwZWFybWFuJyBpbiBvcmRlciB0byB1c2UgU3BlYXJtYW6hr3MgUmhvIGluc3RlYWQgb2YgdGhlICdwZWFyc29uJyBtZXRob2QgaW4gdGhlIGNvcigpIGZ1bmN0aW9uICh0aGlzIGlzIG5vdCBzdHJpY3RseSBuZWNlc3NhcnkgaW4gb3VyIGV4YW1wbGUgc2luY2Ugd2UgYXJlIGdvaW5nIHRvIHVzZSBhbiBlbGxpcHRpY2FsIGNvcHVsYSwgaWYgeW91IHVzZSBub24tZWxsaXB0aWNhbCBjb3B1bGFzIHlvdSBtYXkgd2FudCB0byB1c2UgZWl0aGVyIFNwZWFybWFuoa9zIFJobyBvciBLZW5kYWxsoa9zIFRhdSkuDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInBzeWNoIikNCmxpYnJhcnkocHN5Y2gpICMgdXNlOiBwYWlycy5wYW5lbHMNCnBhaXJzLnBhbmVscyh4KQ0KYGBgDQoNCjxjZW50ZXI+IDxoMj4gV2hhdCBpZiB3ZSBoYXZlIG5vbi1ub3JtYWwgbWFyZ2luYWxzPyA8L2gyPiA8L2NlbnRlcj4gIA0KDQpNYW55IGVtcGlyaWNhbCBldmlkZW5jZSBoYXZlIHNrZXdlZCBvciBoZWF2eSB0YWlsZWQgbWFyZ2luYWxzLiBXaGF0IGlmIHdlIHdvdWxkIGxpa2UgdG8gY3JlYXRlIGEgc2ltaWxhciBjb3JyZWxhdGVkIHJhbmRvbSB2YXJpYWJsZXMgYnV0IHdpdGggYXJiaXRyYXJ5IG1hcmdpbmFscywgc2F5LCBhIEdhbW1hKDIsMSksIGEgQmV0YSgyLDIpLCBhbmQgYSB0KKXtpe09NSkgZGlzdHJpYnV0aW9uPw0KDQojIyMjSGVyZaGvcyBhIHBvc3NpYmxlIGFsZ29yaXRobSB0byBkbyB0aGF0Og0KDQojIyMjMS4gR2VuZXJhdGUgdGhlIHZhcmlhYmxlcyB4fml+IGZyb20gYSBHYXVzc2lhbiBtdWx0aXZhcmlhdGUgKGFzIHdlIGRpZCkNCg0KIyMjIzIuIFJlbWVtYmVyaW5nIHRoZSBwcm9iYWJpbGl0eSBpbnRlZ3JhbCB0cmFuc2Zvcm1hdGlvbjogDQpUcmFuc2Zvcm1zIGEgc2V0IG9mIGRlcGVuZGVudCB2YXJpYWJsZXMgaW50byBhIG5ldyBzZXQgb2YNCmluZGVwZW5kZW50IFUoMCwgMSkgdmFyaWFibGVzLCBnaXZlbiB0aGUgbXVsdGl2YXJpYXRlIGRpc3RyaWJ1dGlvbi4NCg0KPGNlbnRlcj4gPGgzPg0KWKGtRn5YfqKhVT1Gflh+KFgpoa1VKDAsMSkNCjwvaDM+IDwvY2VudGVyPiAgDQoNCnRyYW5zZm9ybSB4fml+IHVzaW5nIHRoZSBHYXVzc2lhbiBjZGYsIKXVIChpbiBSIGlzIGNhbGxlZCBwbm9ybSksIHVpPaXVKHhpKSwgd2hlcmUgdX5pfiBoYXZlIG1hcmdpbmFsIHVuaWZvcm0gZGlzdHJpYnV0aW9ucyBidXQgYXJlIHN0aWxsIGNvcnJlbGF0ZWQgYXMgdmFyaWFibGVzIHh+aX4uDQoNCiMjIyMzLiBBcHBseSwgZm9yIGVhY2ggdmFyaWFibGUgdWl1aSwgdGhlIGludmVyc2UgY2RmIG9mIHRoZSByZXF1aXJlZCBkaXN0cmlidXRpb24gdGhhdCB3ZSB3aXNoIGFzIHRoZSBtYXJnaW5hbCAoZWcsIGluIFIsIHRoZSBpbnZlcnNlIG9mICBwbm9ybSBpcyBxbm9ybSksIHRoYXQgaXMgen5pfiA9IEZeLTFeKHV+aX4pDQoNCm5vdyBjb21lcyB0aGUgbWFnaWMgdHJpY2s6IHJlY2FsbCB0aGF0IGlmIFggaXMgYSByYW5kb20gdmFyaWFibGUgd2l0aCBkaXN0cmlidXRpb24gRiB0aGVuIEYoWCkgaXMgdW5pZm9ybWx5IGRpc3RyaWJ1dGVkIGluIHRoZSBpbnRlcnZhbCBbMCwgMV0uIEluIG91ciB0b3kgZXhhbXBsZSB3ZSBhbHJlYWR5IGtub3cgdGhlIGRpc3RyaWJ1dGlvbiBGRiBmb3IgZWFjaCBvZiB0aGUgdGhyZWUgcmFuZG9tIHZhcmlhYmxlcyBzbyB0aGlzIHBhcnQgaXMgcXVpY2sgYW5kIHN0cmFpZ2h0Zm9yd2FyZC4NCg0KYGBge3J9DQp1IDwtIHBub3JtKHgpDQpwYWlycy5wYW5lbHModSkNCmBgYA0KDQojIyMgSW1wb3J0YW50IEZhY3QNCg0KIkNERrfOIHRyYW5zZm9ybWF0aW9uIChQSVQpwLsgx8+46SCx4sG4IG1hcmdpbmFswMcgwaS6uLTCILvntvPB9rDtIGRlcGVuZGVuY2W/oSC068fRIMGkuri4uCCzsrDUtci02S4iDQoNCk5vdGUgdGhhdCBlYWNoIGRpc3RyaWJ1dGlvbiBpcyB1bmlmb3JtIGluIHRoZSBbMCwxXSBpbnRlcnZhbC4gTm90ZSBhbHNvIHRoYXQgdGhlIGNvcnJlbGF0aW9uIHRoZSBzYW1lLCBpbiBmYWN0LCB0aGUgdHJhbnNmb3JtYXRpb24gd2UgYXBwbGllZCBkaWQgbm90IGNoYW5nZSB0aGUgY29ycmVsYXRpb24gc3RydWN0dXJlIGJldHdlZW4gdGhlIHJhbmRvbSB2YXJpYWJsZXMuIEJhc2ljYWxseSB3ZSBhcmUgbGVmdCBvbmx5IHdpdGggd2hhdCBpcyBvZnRlbiByZWZlcnJlZCBhcyB0aGUgZGVwZW5kZW5jZSBzdHJ1Y3R1cmUuDQoNCg0KDQojIyMgM0Qgb2YgdmVjdG9yIHUNCg0KQnkgdXNpbmcgdGhlIHJnbCBsaWJyYXJ5IHdlIGNhbiBwbG90IGEgbmljZSAzRCByZXByZXNlbnRhdGlvbiBvZiB0aGUgdmVjdG9yIHUuIFRoaXMgaXMgdmVyeSBuaWNlIGZvciBwcmVzZW50YXRpb24gcHVycG9zZXMsIHlvdSBjYW4gZWFzaWx5IHJvdGF0ZSBhcm91bmQgdGhlIGdyYXBoIGluIGNhc2UgeW91IHdvdWxkIGxpa2UgdG8gY2hlY2sgYSBkaWZmZXJlbnQgcGVyc3BlY3RpdmU6IGdvIG51dHMhIEp1c3QgY2xpY2sgb24gdGhlIHBpY3R1cmUgYW5kIGRyYWcgdGhlIG1vdXNlIGFyb3VuZC4NCg0KYGBge3J9DQpsaWJyYXJ5KHJnbCkNCnBsb3QzZCh1WywxXSx1WywyXSx1WywzXSxwY2g9MjAsY29sPSduYXZ5Ymx1ZScpDQpgYGANCg0KTGV0J3MgZG8gdGhlIDNyZCBzdGVwLCB3aGljaCBpcyB0byBhcHBseSB0aGUgaW52ZXJzZSBjZGYgb2YgcmVxdWlyZWQgZGlzdHJpYnV0aW9uIHRoYXQgd2Ugd2lzaCBhcyB0aGUgbWFyZ2luYWwuDQoNCmBgYHtyfQ0KejEgPC0gcWdhbW1hKHVbLDFdLCBzaGFwZT0yLCAgc2NhbGU9MSkNCnoyIDwtICBxYmV0YSh1WywyXSwgc2hhcGUxPTIsIHNoYXBlMj0yKQ0KejMgPC0gICAgIHF0KHVbLDNdLCBkZj01KQ0KeiAgPC0gY2JpbmQoejEsejIsejMpDQpjb3IoeiwgbWV0aD0nc3BlYXJtYW4nKQ0KYGBgDQpgYGB7cn0NCnBhaXJzLnBhbmVscyh6KQ0KYGBgDQoNCkFuZCBub3cgd2UgZ290IHRoZSB0YXJnZXRlZCBtYXJnaW5zIHdpdGggZGlzdHJpYnV0aW9uIHRoYXQgd2Ugd2FudGVkLiBXaGF0IGlzIHdvcnRoIG5vdGljaW5nIGlzIHRoYXQgYnkgc3RhcnRpbmcgZnJvbSBhIG11bHRpdmFyaWF0ZSBub3JtYWwgc2FtcGxlIHdlIGhhdmUgYnVpbGQgYSBzYW1wbGUgd2l0aCB0aGUgZGVzaXJlZCBhbmQgZml4ZWQgZGVwZW5kZW5jZSBzdHJ1Y3R1cmUgYW5kLCBiYXNpY2FsbHksIGFyYml0cmFyeSBtYXJnaW5hbHMuICBJIHdvdWxkIGxpa2UgdG8gZW1waGFzaXplIHRoYXQgSSBjaG9zZSB0b3RhbGx5IGFyYml0cmFyeSBtYXJnaW5hbHMuIEkgY291bGQgaGF2ZSBjaG9zZW4gYW55IG1hcmdpbmFsIGFzIGxvbmcgYXMgaXQgd2FzIGEgY29udGludW91cyBmdW5jdGlvbi4NCg0KIyMgVXNpbmcgY29wdWxhIHBhY2thZ2UNCkxldCdzIHJlcGxpY2F0ZSB0aGUgcHJvY2VzcyBhYm92ZSB1c2luZyBjb3B1bGFzDQpgYGB7cn0NCmxpYnJhcnkoY29wdWxhKQ0Kc2V0LnNlZWQoMTAwKQ0KIyBjb25zdHJ1Y3RzIGFuIGVsbGlwdGljYWwgY29wdWxhDQpteUNvcCA8LSBub3JtYWxDb3B1bGEocGFyYW09YygwLjUsMC4yLC0wLjcpLCBkaW09MywgZGlzcHN0cj0idW4iKQ0KIyBjcmVhdGVzIGEgbXVsdGl2YXJpYXRlIGRpc3RyaWJ1dGlvbiB2aWEgY29wdWxhDQpteU12ZCA8LSBtdmRjKGNvcHVsYT1teUNvcCwgbWFyZ2lucz1jKCJnYW1tYSIsICJiZXRhIiwgInQiKSwNCiAgICAgICAgICAgICAgcGFyYW1NYXJnaW5zPWxpc3QobGlzdChzaGFwZT0yLCAgc2NhbGU9MSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3Qoc2hhcGUxPTIsIHNoYXBlMj0yKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QoZGY9NSkpICkNCmBgYA0KDQpOb3cgdGhhdCB3ZSBoYXZlIHNwZWNpZmllZCB0aGUgZGVwZW5kZW5jZSBzdHJ1Y3R1cmUgdGhyb3VnaCB0aGUgY29wdWxhIChhIG5vcm1hbCBjb3B1bGEpIGFuZCBzZXQgdGhlIG1hcmdpbmFscywgdGhlIG12ZGMoKSBmdW5jdGlvbiBnZW5lcmF0ZXMgdGhlIGRlc2lyZWQgZGlzdHJpYnV0aW9uLiAgDQpUaGVuIHdlIGNhbiBnZW5lcmF0ZSByYW5kb20gc2FtcGxlcyB1c2luZyB0aGUgcm12ZGMoKSBmdW5jdGlvbi4NCg0KDQpgYGB7cn0NCnoyIDwtIHJNdmRjKG4sIG15TXZkKQ0KY29sbmFtZXMoejIpIDwtIHBhc3RlMCgieiIsMTptKQ0KcGFpcnMucGFuZWxzKHoyKQ0KYGBgDQoNCg0KDQojIyMjIENvbnN0cnVjdGlvbiBvZiBDb3B1bGEgTW9kZWwNCg0KMSkgRGV0ZXJtaW5lIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbg0KMikgRGVmaW5lIGFuIGFwcHJvcHJpYXRlIENvcHVsYSBmdW5jdGlvbiBzbyBhcyB0byB3ZWxsIGRlc2NyaWJlIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbidzIGRlcGVuZGVudCBzdHJ1Y3R1cmUuICANCg0KVGhlcmUgYXJlIHR3byBrZXkgcXVldGlvbnMgd2hlbiBidWlsZGluZyBtdWx0aXZhcmlhdGUgZmluYW5jaWFsIHRpbWUgc2VyaWVzIENvcHVsYSBtb2RlbCwgd2hpY2ggYXJlIHVuaXZhcmlhdGUgZmluYW5jaWFsIHRpbWUgc2VyaWVzIGRpc3RyaWJ1dGlvbiBtb2RlbCdzIGVzdGFibGlzaG1lbnQgYW5kIENvcHVsYSBmdW5jdGlvbidzIGRldGVybWluYXRpb24uDQoNCiMjIyNNYXJnaW5hbCBkaXN0cmlidXRpb24gb2YgRmluYW5jaWFsIFRpbWUgc2VyaWVzDQoNCjEpIEFSQ0gvR0FSQ0goQXV0b3JlZ3Jlc3NpdmUgY29uZGl0aW9uYWwgaGV0ZXJvc2NlZGFzdGljaXR5KQ0KMikgU1Yoc3RvY2hhc3RpYyB2b2xhdGlsaXR5KQ0KQXMgaXMga25vd24gdG8gYWxsLGZpbmFuY2lhbCB0aW1lIHNlcmllc6GvIGNvbmRpdGlvbmFsIGRpc3RyaWJ1dGlvbiBwcmVzZW50cyBsb3RzIG9mIGZlYXR1cmVzIHN1Y2ggYXMgdGltZSB2YXJ5aW5nIHZvbGF0aWxpdHksIHZvbGF0aWxpdHkgY2x1c3RlcmluZywgc2tld25lc3MsIGt1cnRvc2lzLCBmYXQgdGFpbCwgZXRjLiBBUkNIIGFuZCBTViBib3RoIGNhbiBzdWl0YWJseSBwb3J0cmF5IGZpbmFuY2lhbCB0aW1lIHNlcmllc6GvIGNoYXJhY3RlcnMgYW5kIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbi4NCg0KIyMjIyBDb21tb24gQmluYXJ5IENvcHVsYSBGdW5jdGlvbg0KDQpDb3B1bGEgaXMgYWNjdW11bGF0ZWQgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uIG1ha2luZyByYW5kb20NCnZhcmlhYmxlc6GvIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbnMgY29ubmVjdGluZyBlYWNoIG90aGVyLiBDb21wYXJpbmcgd2l0aCBsaW5lYXJseSBkZXBlbmRlbnQgY29lZmZpY2llbnQsIHRoZSBjb25zaXN0ZW5jeSBhbmQgcmVsYXRpb25hbCBtZWFzdXJlbWVudCBkZXJpdmVkIGZyb20gQ29wdWxhIGZ1bmN0aW9uIGNhbiBjYXRjaCB2YXJpYWJsZXOhryBub25saW5lYXIgY29ycmVsYXRpb24uIEFuZCBkaWZmZXJlbnQgZnJvbSBvdGhlciBqb2ludCBkaXN0cmlidXRpb24gbW9kZWxpbmcgbWV0aG9kcywgQ29wdWxhIG1vZGVsIGlzIG11Y2ggbW9yZSBmbGV4aWJsZSBhbmQgZG9lcyBub3QgbGltaXQgdGhlIGNob2ljZSBvZiBtYXJnaW5hbCBkaXN0cmlidXRpb24uIEJlY2F1c2UsIHdoZW4gdGhlIGFzc3VtcHRpb25zIG9mIGJpbmFyeSBvciBtdWx0aXZhcmlhdGUgbm9ybWFsIGRpc3RyaWJ1dGlvbg0KYXJlIHJlamVjdGVkLCBDb3B1bGEgcHJvdmlkZXMgZGlmZmVyZW50IGtpbmRzIG9mIG1hcmdpbmFsIGRpc3RyaWJ1dGlvbnMgYW5kIGZ1bmN0aW9ucyB0byByZXBsYWNlIHRoZSBmb3JtZXIgb25lIHNvIGFzIHRvIGFjaGlldmUgYmVzdCBmaXR0aW5nIHJlc3VsdHMuIE1vcmVvdmVyLCBDb3B1bGEgbW9kZWwgY2FuIGdldCB0aGUgcGFyYW1ldGVycyBvZiBjb3JyZWxhdGlvbiBtZWFzdXJlbWVudHMgYW5kIHNwZWNpZmljIGZ1bmN0aW9ucyBieSBvcmdhbmljYWxseSBjb21iaW5pbmcgcmFuZG9tIHZhcmlhYmxlc6GvIGRlcGVuZGVudCBkZWdyZWVzIGFuZCBwYXR0ZXJucy4NCg0KMSkgTm9ybWFsIENvcHVsYQ0KMikgR3VtYmVsIENvcHVsYQ0KMykgQ2xheXRvbiBDb3B1bGENCjQpIEZyYW5rIENvcHVsYSAgDQouLi4uLi4NCg0KIyMgQSBzaW1wbGUgZXhhbXBsZSBvZiBhcHBsaWNhdGlvbiAgDQpOb3cgZm9yIHRoZSByZWFsIHdvcmxkIGV4YW1wbGUsICAgDQoNCldlIGFyZSBnb2luZyB0byBmaXQgYSBjb3B1bGEgdG8gdGhlIHJldHVybnMgb2YgdHdvIHN0b2NrcyBhbmQgdHJ5IHRvIHNpbXVsYXRlIHRoZSByZXR1cm5zIHVzaW5nIHRoZSBjb3B1bGEuDQpGcm9tIHRoZSByZWZlcmVuY2Ugd2Vic2l0ZSwgdGhleSBwcm92aWRlIGNsZWFuZWQgcmV0dXJuIGRhdGEgZnJvbSB0d28gc3RvY2tzLiBZb3UgY2FuIGRvd25sb2FkIGl0IGZyb20gYmVsb3cgd2Vic2l0ZS4NCmh0dHA6Ly9kYXRhc2NpZW5jZXBsdXMuY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzEwL3lhaG9vX3IuY3N2DQpodHRwOi8vZGF0YXNjaWVuY2VwbHVzLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8xMC9jcmVlX3IuY3N2DQoNCiMjIExldCdzIGxvYWQgdGhlIHJldHVybnMgaW4gUg0KDQpgYGB7cn0NCmNyZWUgPC0gcmVhZC5jc3YoJ2NyZWVfci5jc3YnLGhlYWRlcj1GKSRWMg0KeWFob28gPC0gcmVhZC5jc3YoJ3lhaG9vX3IuY3N2JyxoZWFkZXI9RikkVjINCmBgYA0KDQpCZWZvcmUgZ29pbmcgc3RyYWlnaHQgdG8gdGhlIGNvcHVsYSBmaXR0aW5nIHByb2Nlc3MsIGxldKGvcyBjaGVjayB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIHN0b2NrIHJldHVybnMgYW5kIHBsb3QgdGhlIHJlZ3Jlc3Npb24gbGluZToNCmBgYHtyfQ0KcGxvdChjcmVlLHlhaG9vLHBjaD0nLicpDQphYmxpbmUobG0oeWFob29+Y3JlZSksY29sPSdyZWQnLGx3ZD0xKQ0KY29yKGNyZWUseWFob28sbWV0aG9kPSdzcGVhcm1hbicpDQpgYGANCg0KSW4gdGhlIGZpcnN0IGV4YW1wbGUgYWJvdmUgSSBjaG9zZSBhIG5vcm1hbCBjb3B1bGEgbW9kZWwgd2l0aG91dCBtdWNoIHRoaW5raW5nLCBob3dldmVyLCB3aGVuIGFwcGx5aW5nIHRoZXNlIG1vZGVscyB0byByZWFsIGRhdGEgb25lIHNob3VsZCBjYXJlZnVsbHkgdGhpbmsgYXQgd2hhdCBjb3VsZCBzdWl0IHRoZSBkYXRhIGJldHRlci4gRm9yIGluc3RhbmNlLCBtYW55IGNvcHVsYXMgYXJlIGJldHRlciBzdWl0ZWQgZm9yIG1vZGVsbGluZyBhc3ltZXRyaWNhbCBjb3JyZWxhdGlvbiBvdGhlciBlbXBoYXNpemUgdGFpbCBjb3JyZWxhdGlvbiBhIGxvdCwgYW5kIHNvIG9uLiBNeSBndWVzcyBmb3Igc3RvY2sgcmV0dXJucyBpcyB0aGF0IGEgdC1jb3B1bGEgc2hvdWxkIGJlIGZpbmUsIGhvd2V2ZXIgYSBndWVzcyBpcyBjZXJ0YWlubHkgbm90IGVub3VnaC4gRm9ydHVuYXRlbHksIHRoZSBWaW5lQ29wdWxhIHBhY2thZ2Ugb2ZmZXJzIGEgZ3JlYXQgZnVuY3Rpb24gd2hpY2ggdGVsbHMgdXMgd2hhdCBjb3B1bGEgd2Ugc2hvdWxkIHVzZS4gRXNzZW50aWFsbHkgdGhlIFZpbmVDb3B1bGEgbGlicmFyeSBhbGxvd3MgdXMgdG8gcGVyZm9ybSBjb3B1bGEgc2VsZWN0aW9uIHVzaW5nIEJJQyBhbmQgQUlDIHRocm91Z2ggdGhlIGZ1bmN0aW9uIEJpQ29wU2VsZWN0KCkNCg0KYGBge3J9DQpsaWJyYXJ5KFZpbmVDb3B1bGEpDQp1IDwtIHBvYnMoYXMubWF0cml4KGNiaW5kKGNyZWUseWFob28pKSlbLDFdDQp2IDwtIHBvYnMoYXMubWF0cml4KGNiaW5kKGNyZWUseWFob28pKSlbLDJdDQpzZWxlY3RlZENvcHVsYSA8LSBCaUNvcFNlbGVjdCh1LHYsZmFtaWx5c2V0PU5BKQ0Kc2VsZWN0ZWRDb3B1bGENCmBgYA0KDQpOb3RlIHRoYXQgSSBmZWQgaW50byB0aGUgc2VsZWN0aW9uIGFsZ29yaXRobSB0aGUgcHNldWRvIG9ic2VydmF0aW9ucyB1c2luZyB0aGUgcG9icygpIGZ1bmN0aW9uLiBQc2V1ZG8gb2JzZXJ2YXRpb25zIGFyZSB0aGUgb2JzZXJ2YXRpb25zIGluIHRoZSBbMCwxXSBpbnRlcnZhbC4NClRoZSBmaXR0aW5nIGFsZ29yaXRobSBpbmRlZWQgc2VsZWN0ZWQgYSB0LWNvcHVsYSAoZW5jb2RlZCBhcyAyIGluIHRoZSAkZmFtaWx5IHJlZmVyZW5jZSkgYW5kIGVzdGltYXRlZCB0aGUgcGFyYW1ldGVycyBmb3IgdXMuIEJ5IHR5cGluZyA/QmlDb3BTZWxlY3QoKSB5b3UgY2FuIGFjdHVhbGx5IHNlZSB0aGUgZW5jb2RpbmcgZm9yIGVhY2ggY29wdWxhLiAgDQoNCkxldKGvcyB0cnkgdG8gZml0IHRoZSBzdWdnZXN0ZWQgbW9kZWwgdXNpbmcgdGhlIGNvcHVsYSBwYWNrYWdlIGFuZCBkb3VibGUgY2hlY2sgdGhlIHBhcmFtZXRlcnMgZml0dGluZy4NCg0KYGBge3J9DQp0LmNvcCA8LSB0Q29wdWxhKGRpbT0yKQ0Kc2V0LnNlZWQoNTAwKQ0KbSA8LSBwb2JzKGFzLm1hdHJpeChjYmluZChjcmVlLHlhaG9vKSkpDQpmaXQgPC0gZml0Q29wdWxhKHQuY29wLG0sbWV0aG9kPSdtbCcpDQpjb2VmKGZpdCkNCmBgYA0KDQpJdCBpcyBuaWNlIHRvIHNlZSB0aGF0IHRoZSBwYXJhbWV0ZXJzIG9mIHRoZSBmaXR0ZWQgY29wdWxhIGFyZSB0aGUgc2FtZSBhcyB0aG9zZSBzdWdnZXN0ZWQgYnkgdGhlIEJpQ29wU2VsZWN0KCkgZnVuY3Rpb24uIExldKGvcyB0YWtlIGEgbG9vayBhdCB0aGUgZGVuc2l0eSBvZiB0aGUgY29wdWxhIHdlIGhhdmUganVzdCBlc3RpbWF0ZWQNCg0KYGBge3J9DQpyaG8gPC0gY29lZihmaXQpWzFdDQpkZiA8LSBjb2VmKGZpdClbMl0NCnBlcnNwKHRDb3B1bGEoZGltPTIscmhvLGRmPWRmKSxkQ29wdWxhKQ0KYGBgDQpOb3cgd2Ugb25seSBuZWVkIHRvIGJ1aWxkIHRoZSBjb3B1bGEgYW5kIHNhbXBsZSBmcm9tIGl0IDM5NjUgcmFuZG9tIHNhbXBsZXMuDQpUaGlzIGlzIHRoZSBwbG90IG9mIHRoZSBzYW1wbGVzIGNvbnRhaW5lZCBpbiB0aGUgdmVjdG9yIHUNCmBgYHtyfQ0KdSA8LSByQ29wdWxhKDM5NjUsdENvcHVsYShkaW09MixyaG8sZGY9ZGYpKQ0KcGxvdCh1WywxXSx1WywyXSxwY2g9Jy4nLGNvbD0nYmx1ZScpDQpjb3IodSxtZXRob2Q9J3NwZWFybWFuJykNCmBgYA0KDQpUaGUgcmFuZG9tIHNhbXBsZXMgZnJvbSB0aGUgY29wdWxhIGxvb2sgYSBsaXR0bGUgYml0IGNsb3NlIHRvIHRoZSBpbmRlcGVuZGVuY2UgY2FzZSwgYnV0IHRoYXQgaXMgZmluZSBzaW5jZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgcmV0dXJucyBpcyBsb3cuIE5vdGUgdGhhdCB0aGUgZ2VuZXJhdGVkIHNhbXBsZXMgaGF2ZSB0aGUgc2FtZSBjb3JyZWxhdGlvbiBhcyB0aGUgZGF0YS4NClRoZSB0LWNvcHVsYSBlbXBoYXNpemVzIGV4dHJlbWUgcmVzdWx0czogaXQgaXMgdXN1YWxseSBnb29kIGZvciBtb2RlbGxpbmcgcGhlbm9tZW5hIHdoZXJlIHRoZXJlIGlzIGhpZ2ggY29ycmVsYXRpb24gaW4gdGhlIGV4dHJlbWUgdmFsdWVzICh0aGUgdGFpbHMgb2YgdGhlIGRpc3RyaWJ1dGlvbikuDQpOb3RlIGFsc28gdGhhdCBpdCBpcyBzeW1tZXRyaWNhbCwgdGhpcyBtaWdodCBiZSBhbiBpc3N1ZSBmb3Igb3VyIGFwcGxpY2F0aW9uLCBob3dldmVyIHdlIGFyZSBnb2luZyB0byBuZWdsZWN0IHRoaXMuDQpOb3cgd2UgYXJlIGZhY2luZyB0aGUgaGFyZCBiaXQ6IG1vZGVsbGluZyB0aGUgbWFyZ2luYWxzLiBXZSBhcmUgZ29pbmcgdG8gYXNzdW1lIG5vcm1hbGx5IGRpc3RyaWJ1dGVkIHJldHVybnMgZm9yIHNpbXBsaWNpdHkgZXZlbiB0aG91Z2ggaXQgaXMgd2VsbCBrbm93biB0byBiZSBhIGZhciBmcm9tIHNvdW5kIGFzc3VtcHRpb24uIFdlIHRoZXJlZm9yZSBlc3RpbWF0ZSB0aGUgcGFyYW1ldGVycyBvZiB0aGUgbWFyZ2luYWxzDQoNCmBgYHtyfQ0KY3JlZV9tdSA8LSBtZWFuKGNyZWUpDQpjcmVlX3NkIDwtIHNkKGNyZWUpDQp5YWhvb19tdSA8LSBtZWFuKHlhaG9vKQ0KeWFob29fc2QgPC0gc2QoeWFob28pDQpgYGANCg0KTGV0oa9zIHBsb3QgdGhlIGZpdHRpbmdzIGFnYWluc3QgdGhlIGhpc3RvZ3JhbSBpbiBvcmRlciB0byBnYXRoZXIgYSB2aXN1YWwgdGFrZSBvbiB3aGF0IHdlIGFyZSBkb2luZzoNCg0KYGBge3J9DQpoaXN0KGNyZWUsYnJlYWtzPTgwLG1haW49J0NyZWUgcmV0dXJucycsZnJlcT1GLGRlbnNpdHk9MzAsY29sPSdjeWFuJyx5bGltPWMoMCwyMCkseGxpbT1jKC0wLjIsMC4zKSkNCmxpbmVzKHNlcSgtMC41LDAuNSwwLjAxKSxkbm9ybShzZXEoLTAuNSwwLjUsMC4wMSksY3JlZV9tdSxjcmVlX3NkKSxjb2w9J3JlZCcsbHdkPTIpDQpsZWdlbmQoJ3RvcHJpZ2h0JyxjKCdGaXR0ZWQgbm9ybWFsJyksY29sPWMoJ3JlZCcpLGx3ZD0yKQ0KaGlzdCh5YWhvbyxicmVha3M9ODAsbWFpbj0nWWFob28gcmV0dXJucycsZGVuc2l0eT0zMCxjb2w9J2N5YW4nLGZyZXE9Rix5bGltPWMoMCwyMCkseGxpbT1jKC0wLjIsMC4yKSkNCmxpbmVzKHNlcSgtMC41LDAuNSwwLjAxKSxkbm9ybShzZXEoLTAuNSwwLjUsMC4wMSkseWFob29fbXUseWFob29fc2QpLGNvbD0ncmVkJyxsd2Q9MikNCmxlZ2VuZCgndG9wcmlnaHQnLGMoJ0ZpdHRlZCBub3JtYWwnKSxjb2w9YygncmVkJyksbHdkPTIpDQpgYGANCg0KT3B0aW9uYWxseSBhdCB0aGUgYmVnaW5uaW5nIHdlIGNvdWxkIGhhdmUgcGxvdCB0aGUgZGF0YSB3aXRoIHRoZSBkaXN0cmlidXRpb24gZm9yIGVhY2ggcmFuZG9tIHZhcmlhYmxlIGFzIGJlbG93DQoNCmBgYHtyfQ0KIyBQbG90IGRhdGEgd2l0aCBoaXN0b2dyYW1zDQp4aGlzdCA8LSBoaXN0KGNyZWUsIGJyZWFrcz04MCwgcGxvdD1GQUxTRSkNCnloaXN0IDwtIGhpc3QoeWFob28sIGJyZWFrcz04MCwgcGxvdD1GQUxTRSkgDQp0b3AgPC0gbWF4KGMoeGhpc3QkY291bnRzLCB5aGlzdCRjb3VudHMpKSANCnhyYW5nZSA8LSBjKC0wLjE1LDAuMTUpDQp5cmFuZ2UgPC0gYygtMC4xNSwwLjE1KQ0KbmYgPC0gbGF5b3V0KG1hdHJpeChjKDIsMCwxLDMpLDIsMixieXJvdz1UUlVFKSwgYygzLDEpLCBjKDEsMyksIFRSVUUpIA0KcGFyKG1hcj1jKDMsMywxLDEpKSANCnBsb3QoY3JlZSx5YWhvbywgeGxpbT14cmFuZ2UsIHlsaW09eXJhbmdlLCB4bGFiPSIiLCB5bGFiPSIiKSANCnBhcihtYXI9YygwLDMsMSwxKSkgDQpiYXJwbG90KHhoaXN0JGNvdW50cywgYXhlcz1GQUxTRSwgeWxpbT1jKDAsIHRvcCksIHNwYWNlPTApIA0KcGFyKG1hcj1jKDMsMCwxLDEpKSANCmJhcnBsb3QoeWhpc3QkY291bnRzLCBheGVzPUZBTFNFLCB4bGltPWMoMCwgdG9wKSwgc3BhY2U9MCwgaG9yaXo9VFJVRSkNCmBgYA0KDQpEbyBub3QgbGV0IHRoZSBuaWNlIGdyYXBocyBmb29sIHlvdSwgdW5kZXIgdGhlIGFzc3VtcHRpb25zIGZvciB0aGUgZGlzdHJpYnV0aW9ucyBvZiB0aGUgcmV0dXJucyB3ZSBhcmUgbmVnbGVjdGluZyBzb21lIGV4dHJlbWUgcmV0dXJucyBsb2NhdGVkIGluIHRoZSB0YWlscyBvZiB0aGUgZGlzdHJpYnV0aW9uLCBidXQgaW4gdGhpcyBleGFtcGxlIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBqb2ludCBiZWhhdmlvdXIsIHNvIHRoYXQgaXMgd2hhdCB3ZSBhcmUgZ29pbmcgdG8gYW5hbHlzZSBzb29uLg0KDQpOb3cgd2UgYXBwbHkgdGhlIGNvcHVsYSBpbiB0aGUgbXZkYygpIGZ1bmN0aW9uIGFuZCB0aGVuIHVzZSBybXZkYygpIHRvIGdldCBvdXIgc2ltdWxhdGVkIG9ic2VydmF0aW9ucyBmcm9tIHRoZSBnZW5lcmF0ZWQgbXVsdGl2YXJpYXRlIGRpc3RyaWJ1dGlvbi4gRmluYWxseSwgd2UgY29tcGFyZSB0aGUgc2ltdWxhdGVkIHJlc3VsdHMgd2l0aCB0aGUgb3JpZ2luYWwgZGF0YS4NCg0KYGBge3J9DQpjb3B1bGFfZGlzdCA8LSBtdmRjKGNvcHVsYT10Q29wdWxhKHJobyxkaW09MixkZj1kZiksIG1hcmdpbnM9Yygibm9ybSIsIm5vcm0iKSwNCiAgICAgICAgICAgICAgICAgICAgcGFyYW1NYXJnaW5zPWxpc3QobGlzdChtZWFuPWNyZWVfbXUsIHNkPWNyZWVfc2QpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KG1lYW49eWFob29fbXUsIHNkPXlhaG9vX3NkKSkpDQpzaW0gPC0gck12ZGMoMzk2NSwgY29wdWxhX2Rpc3QpDQpgYGANCg0KR29vZCwgbm93IHdlIGhhdmUgc2ltdWxhdGVkIGRhdGEsIGxldKGvcyBtYWtlIGEgdmlzdWFsIGNvbXBhcmlzb24NCg0KYGBge3J9DQpwbG90KGNyZWUseWFob28sbWFpbj0nUmV0dXJucycpDQpwb2ludHMoc2ltWywxXSxzaW1bLDJdLGNvbD0ncmVkJykNCmxlZ2VuZCgnYm90dG9tcmlnaHQnLGMoJ09ic2VydmVkJywnU2ltdWxhdGVkJyksY29sPWMoJ2JsYWNrJywncmVkJykscGNoPTIxKQ0KYGBgDQpUaGlzIGlzIHRoZSBmaW5hbCBzY2F0dGVycGxvdCBvZiB0aGUgZGF0YSB1bmRlciB0aGUgYXNzdW1wdGlvbiBvZiBub3JtYWwgbWFyZ2luYWxzDQphbmQgdC1jb3B1bGEgZm9yIHRoZSBkZXBlbmRlbmNlIHN0cnVjdHVyZToNCg0KQXMgeW91IGNhbiBzZWUsIHRoZSB0LWNvcHVsYSBsZWFkcyB0byByZXN1bHRzIGNsb3NlIHRvIHRoZSByZWFsIG9ic2VydmF0aW9ucywgYWx0aG91Z2ggdGhlcmUgYXJlIGZld2VyIGV4dHJlbWUgcmV0dXJucyB0aGFuIGluIHRoZSByZWFsIGRhdGEgYW5kIHdlIG1pc3NlZCBzb21lIG9mIHRoZSBtb3N0IGV4dHJlbWUgcmVzdWx0LiBJZiB3ZSB3ZXJlIGludGVyZXN0ZWQgaW4gbW9kZWxsaW5nIHRoZSByaXNrIGFzc29jaWF0ZWQgd2l0aCB0aGVzZSBzdG9ja3MgdGhlbiB0aGlzIHdvdWxkIGJlIGEgbWFqb3IgcmVkIGZsYWcgdG8gYmUgYWRkcmVzc2VkIHdpdGggZnVydGhlciBtb2RlbCBjYWxpYnJhdGlvbi4NCg0KU2luY2UgdGhlIHB1cnBvc2Ugb2YgdGhpcyBleGFtcGxlIGlzIHNpbXBseSB0byBzaG93IGhvdyB0byBmaXQgYSBjb3B1bGEgYW5kIGdlbmVyYXRlIHJhbmRvbSBvYnNlcnZhdGlvbnMgdXNpbmcgcmVhbCBkYXRhLCB3ZWFyZSBnb2luZyB0byBzdG9wIGhlcmUuDQoNCiMjIFNpbXVsYXRpbmcgd2l0aCBkaWZmZXJlbnQgcGFyYW1ldGVycw0KDQpKdXN0IGFzIGEgY3VyaW9zaXR5IGxldCBtZSBzaG93IHlvdSBob3cgdGhlIHNpbXVsYXRlZCB2YWx1ZXMgZnJvbSB0aGUgY29wdWxhIHdvdWxkIGhhdmUgY2hhbmdlZCBieSB0d2Vha2luZyB0aGUgZGYgcGFyYW1ldGVyIGluIHRoZSB0LWNvcHVsYS4NCkxldKGvcyB0cnkgZGY9MSBhbmQgZGY9OA0KDQpgYGB7cn0NCnNldC5zZWVkKDQyNTgpDQpjb3B1bGFfZGlzdCA8LSBtdmRjKGNvcHVsYT10Q29wdWxhKHJobyxkaW09MixkZj0xKSwgbWFyZ2lucz1jKCJub3JtIiwibm9ybSIpLA0KICAgICAgICAgICAgICAgICAgICBwYXJhbU1hcmdpbnM9bGlzdChsaXN0KG1lYW49Y3JlZV9tdSwgc2Q9Y3JlZV9zZCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpc3QobWVhbj15YWhvb19tdSwgc2Q9eWFob29fc2QpKSkNCnNpbSA8LSByTXZkYygzOTY1LGNvcHVsYV9kaXN0KQ0KcGxvdChjcmVlLHlhaG9vLG1haW49J1JldHVybnMnKQ0KcG9pbnRzKHNpbVssMV0sc2ltWywyXSxjb2w9J3JlZCcpDQpsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxjKCdPYnNlcnZlZCcsJ1NpbXVsYXRlZCBkZj0xJyksY29sPWMoJ2JsYWNrJywncmVkJykscGNoPTIxKQ0KY29yKHNpbVssMV0sc2ltWywyXSxtZXRob2Q9J3NwZWFybWFuJykNCg0KY29wdWxhX2Rpc3QgPC0gbXZkYyhjb3B1bGE9dENvcHVsYShyaG8sZGltPTIsZGY9OCksIG1hcmdpbnM9Yygibm9ybSIsIm5vcm0iKSwNCiAgICAgICAgICAgICAgICAgICAgcGFyYW1NYXJnaW5zPWxpc3QobGlzdChtZWFuPWNyZWVfbXUsIHNkPWNyZWVfc2QpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KG1lYW49eWFob29fbXUsIHNkPXlhaG9vX3NkKSkpDQpzaW0gPC0gck12ZGMoMzk2NSxjb3B1bGFfZGlzdCkNCnBsb3QoY3JlZSx5YWhvbyxtYWluPSdSZXR1cm5zJykNCnBvaW50cyhzaW1bLDFdLHNpbVssMl0sY29sPSdyZWQnKQ0KbGVnZW5kKCdib3R0b21yaWdodCcsYygnT2JzZXJ2ZWQnLCdTaW11bGF0ZWQgZGY9OCcpLGNvbD1jKCdibGFjaycsJ3JlZCcpLHBjaD0yMSkNCmNvcihzaW1bLDFdLHNpbVssMl0sbWV0aG9kPSdzcGVhcm1hbicpDQpgYGANCg0KTm90ZSBob3cgZGlmZmVyZW50IGFyZSB0aGUgZGVwZW5kZW5jZSBzdHJ1Y3R1cmVzIHRoYXQgd2UgaGF2ZSBvYnRhaW5lZCBhbHRob3VnaCBTcGVhcm1hbqGvcyBSaG8gaXMgc2ltaWxhci4NCkNsZWFybHkgdGhlIHBhcmFtZXRlciBkZiBpcyB2ZXJ5IGltcG9ydGFudCBpbiBkZXRlcm1pbmluZyB0aGUgc2hhcGUgb2YgdGhlIGRpc3RyaWJ1dGlvbi4gQXMgZGYgaW5jcmVhc2VzLCB0aGUgdC1jb3B1bGEgdGVuZHMgdG8gYSBnYXVzc2lhbiBjb3B1bGEuDQoNCiMjIEZldyBtb3JlIGV4YW1wbGVzDQoNClRoZSBkYXRhc2V0DQpGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBleGFtcGxlIEkgdXNlZCBhIHNpbXBsZSBkYXRhc2V0IG9mIHJldHVybnMgZm9yIHN0b2NrIHggYW5kIHkgKHgudHh0IGFuZCB5LnR4dCkuIFlvdSBjYW4gZG93bmxvYWQgdGhlIGRhdGFzZXQgYnkgY2xpY2tpbmcgaGVyZS4NCmh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3MvdW54ajB3Z2Q3dDJiMGNjL2RhdGFzZXQuemlwP2RsPTANCg0KRmlyc3Qgb2YgYWxsIHdlIG5lZWQgdG8gbG9hZCB0aGUgZGF0YSBhbmQgY29udmVydCBpdCBpbnRvIGEgbWF0cml4IGZvcm1hdC4gT3B0aW9uYWxseSBvbmUgY2FuIHBsb3QgdGhlIGRhdGEuIFJlbWVtYmVyIHRvIGxvYWQgdGhlIGNvcHVsYSBwYWNrYWdlIHdpdGggDQoNCmBgYHtyfQ0KeCA8LSByZWFkLnRhYmxlKCJDOlxceC50eHQiKQ0KeSA8LSByZWFkLnRhYmxlKCJDOlxceS50eHQiKQ0KDQptYXQgPC0gbWF0cml4KG5yb3c9MTAwLG5jb2w9MikNCg0KZm9yKGkgaW4gMToxMDApDQp7DQogICAgbWF0W2ksMV0gPC0geFssMV1baV0NCiAgICBtYXRbaSwyXSA8LSB5WywxXVtpXQ0KfQ0KDQojIEFjdHVhbCBvYnNlcnZhdGlvbnMNCnBsb3QobWF0WywxXSxtYXRbLDJdLG1haW49IlJldHVybnMiLHhsYWI9IngiLHlsYWI9InkiLGNvbD0iYmx1ZSIpDQpgYGANCg0KTm93IHdlIGhhdmUgb3VyIGRhdGEgbG9hZGVkLCB3ZSBjYW4gY2xlYXJseSBzZWUgdGhhdCB0aGVyZSBpcyBzb21lIGtpbmQgb2YgcG9zaXRpdmUgY29ycmVsYXRpb24uDQoNClRoZSBuZXh0IHN0ZXAgaXMgdGhlIGZpdHRpbmcuIEluIG9yZGVyIHRvIGZpdCB0aGUgZGF0YSB3ZSBuZWVkIHRvIGNob29zZSBhIGNvcHVsYSBtb2RlbC4gVGhlIG1vZGVsIHNob3VsZCBiZSBjaG9zZSBiYXNlZCBvbiB0aGUgc3RydWN0dXJlIG9mIGRhdGEgYW5kIG90aGVyIGZhY3RvcnMuIEFzIGEgZmlyc3QgYXBwcm94aW1hdGlvbiwgd2UgbWF5IHNheSB0aGF0IG91ciBkYXRhIHNob3dzIGEgbWlsZCBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB0aGVyZWZvcmUgYSBjb3B1bGEgd2hpY2ggY2FuIHJlcGxpY2F0ZSBzdWNoIG1pbGQgY29ycmVsYXRpb24gc2hvdWxkIGJlIGZpbmUuIEJlIGF3YXJlIHRoYXQgeW91IGNhbiBlYXNpbHkgbWVzcyB1cCB3aXRoIGNvcHVsYSBtb2RlbHMgYW5kIHRoaXMgdmlzdWFsIGFwcHJvYWNoIGlzIG5vdCBhbHdheXMgdGhlIGJlc3Qgb3B0aW9uLiBBbnl3YXkgSSBjaG9vc2UgdG8gdXNlIGEgbm9ybWFsIGNvcHVsYSBmcm9tIHRoZSBwYWNrYWdlLiBUaGUgZml0dGluZyBwcm9jZXNzIGFueXdheSBpcyBpZGVudGljYWwgZm9yIHRoZSBvdGhlciB0eXBlcyBvZiBjb3B1bGEuDQoNCkxldKGvcyBmaXQgdGhlIGRhdGENCg0KYGBge3J9DQojIE5vcm1hbCBjb3B1bGENCm5vcm1hbC5jb3AgPC0gbm9ybWFsQ29wdWxhKGRpbT0yKQ0KZml0LmNvcDwtIGZpdENvcHVsYShub3JtYWwuY29wLHBvYnMobWF0KSxtZXRob2Q9Im1sIikNCg0KIyBDb2VmZmljaWVudHMNCnJobyA8LSBjb2VmKGZpdC5jb3ApDQpwcmludChyaG8pDQpgYGANCk5vdGUgdGhhdCB0aGUgZGF0YSBtdXN0IGJlIGZlZCB0aHJvdWdoIHRoZSBmdW5jdGlvbiBwb2JzKCkgd2hpY2ggY29udmVydHMgdGhlIHJlYWwgb2JzZXJ2YXRpb25zIGludG8gcHNldWRvIG9ic2VydmF0aW9ucyBpbnRvIHRoZSB1bml0IHNxdWFyZSBbMCwxXS4NCk5vdGUgYWxzbyB0aGF0IHdlIGFyZSB1c2luZyB0aGUgobBtbKGxIG1ldGhvZCAobWF4aW11bSBsaWtlbGlob29kIG1ldGhvZCkgaG93ZXZlciBvdGhlciBtZXRob2RzIGFyZSBhdmFpbGFibGUgc3VjaCBhcyChsGl0YXWhsS4NCg0KVGhlIHBhcmFtZXRlciBvZiB0aGUgZml0dGVkIGNvcHVsYSwgcmhvLCBpbiBvdXIgY2FzZSBpcyBlcXVhbCB0byAwLjczODc0MDkuIExldKGvcw0Kc2ltdWxhdGUgc29tZSBwc2V1ZG8gb2JzZXJ2YXRpb25zDQoNCkJ5IHBsb3R0aW5nIHRoZSBwc2V1ZG8gYW5kIHNpbXVsYXRlZCBvYnNlcnZhdGlvbnMgd2UgY2FuIHNlZSBob3cgdGhlIHNpbXVsYXRpb24gd2l0aA0KdGhlIGNvcHVsYSBtYXRjaGVzIHRoZSBwc2V1ZG8gb2JzZXJ2YXRpb25zDQoNCmBgYHtyfQ0KIyBQc2V1ZG8gb2JzZXJ2YXRpb25zDQpwX29icyA8LSBwb2JzKG1hdCkNCnBsb3QocF9vYnMsIGNvbD0iYmx1ZSIpDQpwbG90KHBfb2JzWywxXSxwX29ic1ssMl0sbWFpbj0iUHNldWRvL3NpbXVsYXRlZCBvYnNlcnZhdGlvbnM6IEJMVUUvUkVEIix4bGFiPSJ1Iix5bGFiPSJ2Iixjb2w9ImJsdWUiKQ0KDQojIFNpbXVsYXRlIGRhdGENCnNldC5zZWVkKDEwMCkNCnUxID0gckNvcHVsYSg1MDAsbm9ybWFsQ29wdWxhKGNvZWYoZml0LmNvcCksZGltPTIpKQ0KcG9pbnRzKHUxWywxXSx1MVssMl0sY29sPSJyZWQiKQ0KYGBgDQoNCg0K