A Two Agent Model of Forward Electricity Prices in Brazil with Generalized Extended CVaR Preferences
Authors: Felipe Van de Sande Araujo, Cristina Pimenta de Mello Spineti Luz, Leonardo Lima Gomes, Luiz Eduardo Teixeira Brandão
Abstract: Despite its continental size and integrated electrical system, Brazil does not have an exchange for trading forward and futures contracts for electricity. Thus, price information for long-term contracts is often obtained through market research and expert opinions. This article proposes a simple yet efficient approach to estimate the forward price of electricity in the Brazilian energy market. The model is based on the equilibrium between two representative agents negotiating bilateral contracts where the agents’ risk aversion is derived from the utility functions related to the Generalized Extended Conditional Value-at-Risk Preference. This model is comprehensive and can be applied to all agents participating in the electricity futures transaction independent of whether they are directly involved in the production chain or simply carry speculative positions. Our results indicate that the model’s forecasted prices, which are based on the participants’ expected behavior, can be used as an indicator for the forward price of electricity, providing more transparency and security for the participants in this market.
This work presents the calculations done for the article referenced above. The following calculations show the sensitivity analysis for the optimized parameters which were obtained in the optimization step (link) and the risk-free return rate used in both training and validation step (link). All the code was run in RStudio using the version of the software below.
Software version
R.version
_
platform x86_64-w64-mingw32
arch x86_64
os mingw32
system x86_64, mingw32
status
major 4
minor 0.3
year 2020
month 10
day 10
svn rev 79318
language R
version.string R version 4.0.3 (2020-10-10)
nickname Bunny-Wunnies Freak Out
Setting of the environment
# Set plot font
windowsFonts(A = windowsFont("Times New Roman"))
Local functions definition
The first function is the determination of the ECPG phi which is the certainty equivalent of the agent when it is directly exposed to the spot prices.
ECPG.phi <- function(parameters, dataset, auxList, buyer=TRUE) {
if (any(parameters < 0) || any(parameters > 1)) return(NA)
sumLambda = parameters['Lambda1'] + parameters['Lambda2']
if (sumLambda > 1) return(NA)
Lambda0 = 1 - sumLambda
if (Lambda0 < 0) return(NA)
if (buyer) {
mean.pld = auxList$meanPLD
VaR0 = auxList$VaR0
VaR1 = auxList$VaR1
} else {
dataset = -dataset
mean.pld = -auxList$meanPLD
VaR0 = auxList$VaR1
VaR1 = auxList$VaR0
}
VaRAlpha1 = apply(dataset, 1, quantile, (1-parameters['Alpha1']))
VaRAlpha2 = apply(dataset, 1, quantile, (1-parameters['Alpha2']))
CVaR1 = CVaR2 = NA
CVaR1.data = dataset
CVaR1.data[dataset > VaRAlpha1] <- NA
CVaR2.data = dataset
CVaR2.data[dataset > VaRAlpha2] <- NA
CVaR1 = apply(CVaR1.data, 1, mean, na.rm=TRUE)
CVaR2 = apply(CVaR2.data, 1, mean, na.rm=TRUE)
Sum1 = parameters['Lambda1']*VaRAlpha1 + parameters['Lambda2']*VaRAlpha2
Sum2 = parameters['Lambda1']*(CVaR1 - VaRAlpha1) + parameters['Lambda2']*(CVaR2 - VaRAlpha2)
Limit1 = Lambda0*VaR0 + Sum1
Limit2 = Lambda0*VaRAlpha1 + Sum1
Limit3 = Lambda0*VaRAlpha2 + Sum1
Limit4 = Lambda0*VaR1 + Sum1
ECPG = Lambda0*mean.pld + parameters['Lambda1']*CVaR1 + parameters['Lambda2']*CVaR2
nIter = nrow(dataset)
Lambda1 = rep(parameters['Lambda1'], nIter)
Lambda2 = rep(parameters['Lambda2'], nIter)
if (any(ECPG>=Limit2)) Lambda1[ECPG>=Limit2] = 0
if (any(ECPG>=Limit3)) Lambda2[ECPG>=Limit3] = 0
Q = Lambda0 + Lambda1/(1-parameters['Alpha1']) + Lambda2/(1-parameters['Alpha2'])
A = Sum2 + Lambda0*mean.pld +
Lambda1*VaRAlpha1/(1-parameters['Alpha1']) +
Lambda2*VaRAlpha2/(1-parameters['Alpha2'])
Eq = A/Q
if (any(is.na(Eq))) return(NA)
return(Eq)
}
We will use a list with all the parameters and data which will be called ECPG_Object, in order to facilitate the next function calls. The next function will create the list.
createECPGObject <- function(dataset, discountVector, forwardPrice, buyerParam, sellerParam) {
VaR0 = apply(dataset, 1, quantile, 1)
VaR1 = apply(dataset, 1, quantile, 0)
meanPLD = apply(dataset, 1, mean)
ECPGObject = list(
dataset=dataset,
discountVector=discountVector,
forwardPrice=forwardPrice,
buyerParam=buyerParam,
sellerParam=sellerParam,
VaR0=VaR0,
VaR1=VaR1,
meanPLD=meanPLD
)
return(ECPGObject)
}
Then we will define a function to obtain the equilibrium price using the calculated phi for each agent. The distinction between the agents must be made because the signal of the price data changes between them, because of the Contract for Differences revenue equations.
ECPG.equilibrium <- function (ECPGobject) {
auxList = list(
VaR0 = ECPGobject$VaR0,
VaR1 = ECPGobject$VaR1,
meanPLD = ECPGobject$meanPLD
)
buyer.phi = ECPG.phi(ECPGobject$buyerParam, ECPGobject$dataset, auxList)
seller.phi = -ECPG.phi(ECPGobject$sellerParam, ECPGobject$dataset, auxList, buyer=FALSE)
equilibrium.prices = (buyer.phi + seller.phi)/2
adjusted.prices = equilibrium.prices/ECPGobject$discount
averagePrice = mean(adjusted.prices)
ECPGobject$buyerPhi = buyer.phi
ECPGobject$sellerPhi = seller.phi
ECPGobject$equilibriumPrices = equilibrium.prices
ECPGobject$adjustedPrices=adjusted.prices
ECPGobject$averagePrice=averagePrice
return(ECPGobject)
}
Next function is used in a recursive call to calculate sensitivity.
sensitivity.foo <- function(i, arg, output, combination.matrix, ECPGobject) {
ECPGobject[[arg]][TRUE] = combination.matrix[i, ]
return(ECPG.equilibrium(ECPGobject)[[output]])
}
And finally a plotting function for the ECPG_Object.
sensitivity.prettyPlot <- function(plot.matrix, xlabel="", ylim=c(0,250), lwd = 2, legendTitle, legendPos="bottomright", inset=0.05, ncol=1, cex=1, legCex=0.9) {
Nseries = ncol(plot.matrix)
viridis.pallette = viridis(Nseries)
line.types = rep(1, Nseries)
Nrows = 1:nrow(plot.matrix)
labels=row.names(plot.matrix)
par(family="A", cex=cex)
matplot(Nrows, plot.matrix, type='l', xlab=xlabel, ylab='Electricity forward price (R$/MWh)', ylim=ylim, lty=line.types, col=viridis.pallette, lwd=lwd, axes=F)
axis(2)
axis(side=1,at=Nrows,labels=labels)
grid (NULL,NULL, lty = 6, col = "grey")
legend(x=legendPos, inset=inset, legend=colnames(plot.matrix), horiz=FALSE, lty=line.types, col=viridis.pallette, lwd=lwd, cex=legCex, ncol=ncol, title=legendTitle)
}
Local variables definition
These variables will be used for all sensitivity tests. The optimization parameters are obtained from the training step.
buyer.param = c(Lambda1 = 0.25159927, Lambda2 = 0.09390924, Alpha1 = 0.32334330, Alpha2 = 0.69293115)
seller.param = c(Lambda1 = 0.1712721, Lambda2 = 0.7466865, Alpha1 = 0.7641934, Alpha2 = 0.9912755)
Variables for July A+1 analysis
Create a vector with discount rates to adjust the equilibrium prices with the risk-free return rate. Risk-free return rate is obtained as an approximation of the SELIC national rate.
# Set risk free rates
anualRate = 0.05
monthlyRate = (1+anualRate)^(1/12)-1
temp.discount.vector <- rep(NA, 23)
for (i in 1:23){
temp.discount.vector[i] <- (1+monthlyRate)^(i-1)
}
julyA1.discountVector = temp.discount.vector[5:16]
Input the forward price for electricity obtained from DCide Energia.
julyA1.forwardPrice = 205.43
Loading the July A+1 data
Loading future spot price (PLD) data.
julyA1.original.data = read.csv(file='./Data/Data_Jul_2019_A1.csv', col.names=paste(month.abb, 2020, sep="-"))
julyA1.data = t(julyA1.original.data)
Create a list with all variables and data to pass on to functions.
julyA1.ECPG_object = createECPGObject(dataset = julyA1.data, buyerParam = buyer.param, sellerParam = seller.param, discountVector = julyA1.discountVector, forwardPrice = julyA1.forwardPrice)
Get the original results for comparison.
julyA1.Results = ECPG.equilibrium(julyA1.ECPG_object)
sensitivity for Lambda 1 and Lambda 2
The first analysis is regarding \(\lambda_1\) and \(\lambda_2\) parameters for each agent. Those are the auxiliary variables set for this task.
# Set sensitivity ranges
lambdas.vec = seq(0, 0.45, by=0.05)
lambdas.matrix = as.matrix(expand.grid(lambdas.vec, lambdas.vec))
colnames(lambdas.matrix) = c('Lambda1', 'Lambda2')
nLambdas = nrow(lambdas.matrix)
From the range set above a combination matrix is created with the buyer parameters.
buyer.lambdas.matrix = cbind(lambdas.matrix, Alpha1=rep(buyer.param['Alpha1'], nLambdas), Alpha2=rep(buyer.param['Alpha2'], nLambdas))
Next, call the sensitivity function and create a matrix of the results for the buyer.
lambdaBuyer.sensitivity = sapply(1:nLambdas,
sensitivity.foo,
arg = "buyerParam",
output = "averagePrice",
combination.matrix = buyer.lambdas.matrix,
julyA1.ECPG_object)
lambdaBuyer.sensiMatrix = matrix(lambdaBuyer.sensitivity,
ncol=length(lambdas.vec),
byrow = TRUE,
dimnames = list(lambdas.vec, lambdas.vec))
Do the same for the seller.
seller.lambdas.matrix = cbind(lambdas.matrix, Alpha1=rep(seller.param['Alpha1'], nLambdas), Alpha2=rep(seller.param['Alpha2'], nLambdas))
lambdaSeller.sensitivity = sapply(1:nLambdas,
sensitivity.foo,
arg = "sellerParam",
output = "averagePrice",
combination.matrix = seller.lambdas.matrix,
julyA1.ECPG_object)
lambdaSeller.sensiMatrix = matrix(lambdaSeller.sensitivity,
ncol=length(lambdas.vec),
byrow = TRUE,
dimnames = list(lambdas.vec, lambdas.vec))
The result are plotted in an interactive widget using plotly.
require(plotly)
Loading required package: plotly
Loading required package: ggplot2
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: 㤼㸱plotly㤼㸲
The following object is masked from 㤼㸱package:ggplot2㤼㸲:
last_plot
The following object is masked from 㤼㸱package:stats㤼㸲:
filter
The following object is masked from 㤼㸱package:graphics㤼㸲:
layout
ax.xy <- list(ticketmode = 'array', ticktext = as.character(lambdas.vec), tickvals=0:9)
axz <- list(title = "Electricity forward price (R$/MWh)")
Analysis of \(\lambda_1\) and \(\lambda_2\) for the buyer. The chart below is interactive.
fig.Buyer <- plot_ly(width=1050, height=750) %>%
add_surface(z = lambdaBuyer.sensiMatrix) %>%
layout(scene = list(xaxis=c(ax.xy, title = "Buyer Lambda1"),yaxis=c(ax.xy, title = "Buyer Lambda2"),zaxis=axz))
fig.Buyer
Create 2D visualization for the above data with Plotly contour plot
fig.BuyerLambdaFlat <- plot_ly(
x = lambdas.vec,
y = lambdas.vec,
z = lambdaBuyer.sensiMatrix,
type = "contour"
)
fig.BuyerLambdaFlat <- fig.BuyerLambdaFlat %>% colorbar(
len=0.85,
title = "Electricity\nForward\nPrice\n(R$/MWh)")
fig.BuyerLambdaFlat <- fig.BuyerLambdaFlat %>% layout(
xaxis = list(title = "Buyer's Lambda 1"),
yaxis = list(title = "Buyer's Lambda 2"),
font=list(size=24)
)
fig.BuyerLambdaFlat
Analysis of \(\lambda_1\) and \(\lambda_2\) for the seller. The chart below is interactive.
fig.Seller <- plot_ly(width=1050, height=750) %>%
add_surface(z = lambdaSeller.sensiMatrix) %>%
layout(scene = list(xaxis=c(ax.xy, title = "Seller Lambda1"),yaxis=c(ax.xy, title = "Seller Lambda2"),zaxis=axz))
fig.Seller
Create 2D visualization for the above data with Plotly contour plot
fig.SellerLambdaFlat <- plot_ly(
x = lambdas.vec,
y = lambdas.vec,
z = lambdaSeller.sensiMatrix,
type = "contour"
)
fig.SellerLambdaFlat <- fig.SellerLambdaFlat %>% colorbar(
len=0.85,
title = "Electricity\nForward\nPrice\n(R$/MWh)")
fig.SellerLambdaFlat <- fig.SellerLambdaFlat %>% layout(
xaxis = list(title = "Seller's Lambda 1"),
yaxis = list(title = "Seller's Lambda 2"),
font=list(size=24)
)
fig.SellerLambdaFlat
sensitivity for Lambda 1 and Alpha 1 in ECP
The following test is regarding both \(\alpha\) and \(\lambda\) parameters for each agent using ECP measure instead of ECP_G. Those are the auxiliary variables set for this task.
lambdaAlpha.vec = seq(0, 0.9, by=0.1)
lambdaAlpha.matrix = as.matrix(expand.grid(lambdaAlpha.vec, lambdaAlpha.vec))
colnames(lambdaAlpha.matrix) = c('Lambda1', 'Alpha1')
nLambdaAlpha = nrow(lambdaAlpha.matrix)
From the range set above a combination matrix is created with the buyer parameters.
buyer.lambdaAlpha.matrix = cbind(lambdaAlpha.matrix, Lambda2=rep(0, nLambdaAlpha), Alpha2=rep(buyer.param['Alpha2'], nLambdaAlpha))
buyer.lambdaAlpha.matrix = buyer.lambdaAlpha.matrix[, c('Lambda1', 'Lambda2', 'Alpha1', 'Alpha2')]
Next, call the sensitivity function and create a matrix of the results for the buyer.
lambdaAlphaBuyer.sensitivity = sapply(1:nLambdaAlpha,
sensitivity.foo,
arg="buyerParam",
output = "averagePrice",
combination.matrix = buyer.lambdaAlpha.matrix,
within(julyA1.ECPG_object, buyerParam[TRUE] <- rep(0,4)))
lambdaAlphaBuyer.sensiMatrix = matrix(lambdaAlphaBuyer.sensitivity,
ncol=length(lambdaAlpha.vec),
byrow = TRUE,
dimnames = list(lambdaAlpha.vec, lambdaAlpha.vec))
And the same is done for the seller.
seller.lambdaAlpha.matrix = cbind(lambdaAlpha.matrix, Lambda2=rep(0, nLambdaAlpha), Alpha2=rep(seller.param['Alpha2'], nLambdaAlpha))
seller.lambdaAlpha.matrix = seller.lambdaAlpha.matrix[, c('Lambda1', 'Lambda2', 'Alpha1', 'Alpha2')]
lambdaAlphaSeller.sensitivity = sapply(1:nLambdaAlpha,
sensitivity.foo,arg="sellerParam",
output = "averagePrice",
combination.matrix = seller.lambdaAlpha.matrix,
within(julyA1.ECPG_object, sellerParam[TRUE] <- rep(0,4)))
lambdaAlphaSeller.sensiMatrix = matrix(lambdaAlphaSeller.sensitivity,
ncol=length(lambdaAlpha.vec),
byrow = TRUE,
dimnames = list(lambdaAlpha.vec, lambdaAlpha.vec))
The results are visualized with our own plot function. Library viridis is called to provide color pallette for colorblind visualization.
require(viridis)
Loading required package: viridis
Loading required package: viridisLite
Alpha-Lambda sensitivity for the buyer.
sensitivity.prettyPlot(lambdaAlphaBuyer.sensiMatrix, xlabel=expression(paste("Buyer ", alpha, 1)), ylim=c(170, 250), legendPos = "bottom", ncol=5, cex=1.4, lwd=3, legendTitle=expression(paste("Buyer ", lambda, 1)))
Alpha-Lambda sensitivity for the seller.
sensitivity.prettyPlot(lambdaAlphaSeller.sensiMatrix, xlabel=expression(paste("Seller ", alpha, 1)), ylim=c(0, 180), legendTitle=expression(paste("Seller ", lambda, 1)), legendPos = "bottom", ncol=5, inset=0.02, cex=1.4, lwd=3)
Prepare variables for the risk-free rate sensitivity analysis
Set auxiliary variables.
annualRates.vec = seq(0, 0.09, by=0.01)
monthlyRates.vec = (1+annualRates.vec)^(1/12)-1
nRates = length(monthlyRates.vec)
Create a discount matrix to be passed to the sensitivity function.
discount.matrix <- matrix(NA, ncol=35, nrow=nRates)
for (ii in 1:nRates) {
for (i in 1:35){
discount.matrix[ii, i] <- (1+monthlyRates.vec[ii])^(i-1)
}
}
discount.matrix = discount.matrix[, 24:35]
rownames(discount.matrix) = annualRates.vec
Apply the sensitivity function to obtain the average price for each risk-free rate.
rates.sensitivity = sapply(1:nRates,
sensitivity.foo,
arg="discountVector",
output = "averagePrice",
combination.matrix = discount.matrix,
julyA1.ECPG_object)
names(rates.sensitivity) = annualRates.vec
Check if results are internally consistent.
julyA1.Results = ECPG.equilibrium(within(julyA1.ECPG_object, discountVector <- discount.matrix["0.05", ]))
julyA1.Results$averagePrice == rates.sensitivity["0.05"]
0.05
TRUE
Plot the results
plot(annualRates.vec, rates.sensitivity, ylim=c(0,250), ylab="Electricity forward price (R$/MWh)", xlab="Annual Risk-Free Rate", col="steelblue")
See also:
Model Training (link)
Model Validation (link)
LS0tDQp0aXRsZTogIlNlbnNpdGl2aXR5IFRlc3RpbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyAgQSBUd28gQWdlbnQgTW9kZWwgb2YgRm9yd2FyZCBFbGVjdHJpY2l0eSBQcmljZXMgaW4gQnJhemlsIHdpdGggR2VuZXJhbGl6ZWQgRXh0ZW5kZWQgQ1ZhUiBQcmVmZXJlbmNlcw0KDQpBdXRob3JzOiBGZWxpcGUgVmFuIGRlIFNhbmRlIEFyYXVqbywgQ3Jpc3RpbmEgUGltZW50YSBkZSBNZWxsbyBTcGluZXRpIEx1eiwgTGVvbmFyZG8gTGltYSBHb21lcywgTHVpeiBFZHVhcmRvIFRlaXhlaXJhIEJyYW5kw6NvDQoNCkFic3RyYWN0OiBEZXNwaXRlIGl0cyBjb250aW5lbnRhbCBzaXplIGFuZCBpbnRlZ3JhdGVkIGVsZWN0cmljYWwgc3lzdGVtLCBCcmF6aWwgZG9lcyBub3QgaGF2ZSBhbiBleGNoYW5nZSBmb3IgdHJhZGluZyBmb3J3YXJkIGFuZCBmdXR1cmVzIGNvbnRyYWN0cyBmb3IgZWxlY3RyaWNpdHkuIFRodXMsIHByaWNlIGluZm9ybWF0aW9uIGZvciBsb25nLXRlcm0gY29udHJhY3RzIGlzIG9mdGVuIG9idGFpbmVkIHRocm91Z2ggbWFya2V0IHJlc2VhcmNoIGFuZCBleHBlcnQgb3BpbmlvbnMuIFRoaXMgYXJ0aWNsZSBwcm9wb3NlcyBhIHNpbXBsZSB5ZXQgZWZmaWNpZW50IGFwcHJvYWNoIHRvIGVzdGltYXRlIHRoZSBmb3J3YXJkIHByaWNlIG9mIGVsZWN0cmljaXR5IGluIHRoZSBCcmF6aWxpYW4gZW5lcmd5IG1hcmtldC4gVGhlIG1vZGVsIGlzIGJhc2VkIG9uIHRoZSBlcXVpbGlicml1bSBiZXR3ZWVuIHR3byByZXByZXNlbnRhdGl2ZSBhZ2VudHMgbmVnb3RpYXRpbmcgYmlsYXRlcmFsIGNvbnRyYWN0cyB3aGVyZSB0aGUgYWdlbnRz4oCZIHJpc2sgYXZlcnNpb24gaXMgZGVyaXZlZCBmcm9tIHRoZSB1dGlsaXR5IGZ1bmN0aW9ucyByZWxhdGVkIHRvIHRoZSBHZW5lcmFsaXplZCBFeHRlbmRlZCBDb25kaXRpb25hbCBWYWx1ZS1hdC1SaXNrIFByZWZlcmVuY2UuIFRoaXMgbW9kZWwgaXMgY29tcHJlaGVuc2l2ZSBhbmQgY2FuIGJlIGFwcGxpZWQgdG8gYWxsIGFnZW50cyBwYXJ0aWNpcGF0aW5nIGluIHRoZSBlbGVjdHJpY2l0eSBmdXR1cmVzIHRyYW5zYWN0aW9uIGluZGVwZW5kZW50IG9mIHdoZXRoZXIgdGhleSBhcmUgZGlyZWN0bHkgaW52b2x2ZWQgaW4gdGhlIHByb2R1Y3Rpb24gY2hhaW4gb3Igc2ltcGx5IGNhcnJ5IHNwZWN1bGF0aXZlIHBvc2l0aW9ucy4gT3VyIHJlc3VsdHMgaW5kaWNhdGUgdGhhdCB0aGUgbW9kZWzigJlzIGZvcmVjYXN0ZWQgcHJpY2VzLCB3aGljaCBhcmUgYmFzZWQgb24gdGhlIHBhcnRpY2lwYW50cycgZXhwZWN0ZWQgYmVoYXZpb3IsIGNhbiBiZSB1c2VkIGFzIGFuIGluZGljYXRvciBmb3IgdGhlIGZvcndhcmQgcHJpY2Ugb2YgZWxlY3RyaWNpdHksIHByb3ZpZGluZyBtb3JlIHRyYW5zcGFyZW5jeSBhbmQgc2VjdXJpdHkgZm9yIHRoZSBwYXJ0aWNpcGFudHMgaW4gdGhpcyBtYXJrZXQuDQoNClRoaXMgd29yayBwcmVzZW50cyB0aGUgY2FsY3VsYXRpb25zIGRvbmUgZm9yIHRoZSBhcnRpY2xlIHJlZmVyZW5jZWQgYWJvdmUuIFRoZSBmb2xsb3dpbmcgY2FsY3VsYXRpb25zIHNob3cgdGhlIHNlbnNpdGl2aXR5IGFuYWx5c2lzIGZvciB0aGUgb3B0aW1pemVkIHBhcmFtZXRlcnMgd2hpY2ggd2VyZSBvYnRhaW5lZCBpbiB0aGUgb3B0aW1pemF0aW9uIHN0ZXAgKFtsaW5rXSguL01vZGVsVHJhaW5pbmdOb3RlYm9vay5uYi5odG1sKSkgYW5kIHRoZSByaXNrLWZyZWUgcmV0dXJuIHJhdGUgdXNlZCBpbiBib3RoIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIHN0ZXAgKFtsaW5rXSguL01vZGVsVmFsaWRhdGlvbk5vdGVib29rLm5iLmh0bWwpKS4gQWxsIHRoZSBjb2RlIHdhcyBydW4gaW4gUlN0dWRpbyB1c2luZyB0aGUgdmVyc2lvbiBvZiB0aGUgc29mdHdhcmUgYmVsb3cuDQoNCiMjIyBTb2Z0d2FyZSB2ZXJzaW9uDQoNCmBgYHtyfQ0KUi52ZXJzaW9uDQpgYGANCg0KIyMjIFNldHRpbmcgb2YgdGhlIGVudmlyb25tZW50DQoNCmBgYHtyfQ0KIyBTZXQgcGxvdCBmb250DQp3aW5kb3dzRm9udHMoQSA9IHdpbmRvd3NGb250KCJUaW1lcyBOZXcgUm9tYW4iKSkgDQpgYGANCg0KIyMjIExvY2FsIGZ1bmN0aW9ucyBkZWZpbml0aW9uDQoNClRoZSBmaXJzdCBmdW5jdGlvbiBpcyB0aGUgZGV0ZXJtaW5hdGlvbiBvZiB0aGUgRUNQRyBwaGkgd2hpY2ggaXMgdGhlIGNlcnRhaW50eSBlcXVpdmFsZW50IG9mIHRoZSBhZ2VudCB3aGVuIGl0IGlzIGRpcmVjdGx5IGV4cG9zZWQgdG8gdGhlIHNwb3QgcHJpY2VzLg0KDQpgYGB7cn0NCkVDUEcucGhpIDwtIGZ1bmN0aW9uKHBhcmFtZXRlcnMsIGRhdGFzZXQsIGF1eExpc3QsIGJ1eWVyPVRSVUUpIHsNCiAgaWYgKGFueShwYXJhbWV0ZXJzIDwgMCkgfHwgYW55KHBhcmFtZXRlcnMgPiAxKSkgcmV0dXJuKE5BKQ0KICBzdW1MYW1iZGEgPSBwYXJhbWV0ZXJzWydMYW1iZGExJ10gKyBwYXJhbWV0ZXJzWydMYW1iZGEyJ10NCiAgaWYgKHN1bUxhbWJkYSA+IDEpIHJldHVybihOQSkNCiAgTGFtYmRhMCA9IDEgLSBzdW1MYW1iZGENCiAgaWYgKExhbWJkYTAgPCAwKSByZXR1cm4oTkEpDQogIA0KICBpZiAoYnV5ZXIpIHsNCiAgICBtZWFuLnBsZCA9IGF1eExpc3QkbWVhblBMRA0KICAgIFZhUjAgPSBhdXhMaXN0JFZhUjANCiAgICBWYVIxID0gYXV4TGlzdCRWYVIxDQogIH0gZWxzZSB7DQogICAgZGF0YXNldCA9IC1kYXRhc2V0DQogICAgbWVhbi5wbGQgPSAtYXV4TGlzdCRtZWFuUExEDQogICAgVmFSMCA9IGF1eExpc3QkVmFSMQ0KICAgIFZhUjEgPSBhdXhMaXN0JFZhUjANCiAgfQ0KICANCiAgVmFSQWxwaGExID0gYXBwbHkoZGF0YXNldCwgMSwgcXVhbnRpbGUsICgxLXBhcmFtZXRlcnNbJ0FscGhhMSddKSkNCiAgVmFSQWxwaGEyID0gYXBwbHkoZGF0YXNldCwgMSwgcXVhbnRpbGUsICgxLXBhcmFtZXRlcnNbJ0FscGhhMiddKSkNCiAgDQogIENWYVIxID0gQ1ZhUjIgPSBOQQ0KICANCiAgQ1ZhUjEuZGF0YSA9IGRhdGFzZXQNCiAgQ1ZhUjEuZGF0YVtkYXRhc2V0ID4gVmFSQWxwaGExXSA8LSBOQQ0KICANCiAgQ1ZhUjIuZGF0YSA9IGRhdGFzZXQNCiAgQ1ZhUjIuZGF0YVtkYXRhc2V0ID4gVmFSQWxwaGEyXSA8LSBOQQ0KICANCiAgQ1ZhUjEgPSBhcHBseShDVmFSMS5kYXRhLCAxLCBtZWFuLCBuYS5ybT1UUlVFKQ0KICBDVmFSMiA9IGFwcGx5KENWYVIyLmRhdGEsIDEsIG1lYW4sIG5hLnJtPVRSVUUpDQogIA0KICBTdW0xID0gcGFyYW1ldGVyc1snTGFtYmRhMSddKlZhUkFscGhhMSArIHBhcmFtZXRlcnNbJ0xhbWJkYTInXSpWYVJBbHBoYTINCiAgU3VtMiA9IHBhcmFtZXRlcnNbJ0xhbWJkYTEnXSooQ1ZhUjEgLSBWYVJBbHBoYTEpICsgcGFyYW1ldGVyc1snTGFtYmRhMiddKihDVmFSMiAtIFZhUkFscGhhMikNCiAgDQogIExpbWl0MSA9IExhbWJkYTAqVmFSMCArIFN1bTENCiAgTGltaXQyID0gTGFtYmRhMCpWYVJBbHBoYTEgKyBTdW0xDQogIExpbWl0MyA9IExhbWJkYTAqVmFSQWxwaGEyICsgU3VtMQ0KICBMaW1pdDQgPSBMYW1iZGEwKlZhUjEgKyBTdW0xDQogIA0KICBFQ1BHID0gTGFtYmRhMCptZWFuLnBsZCArIHBhcmFtZXRlcnNbJ0xhbWJkYTEnXSpDVmFSMSArIHBhcmFtZXRlcnNbJ0xhbWJkYTInXSpDVmFSMg0KICANCiAgbkl0ZXIgPSBucm93KGRhdGFzZXQpDQogIExhbWJkYTEgPSByZXAocGFyYW1ldGVyc1snTGFtYmRhMSddLCBuSXRlcikNCiAgTGFtYmRhMiA9IHJlcChwYXJhbWV0ZXJzWydMYW1iZGEyJ10sIG5JdGVyKQ0KICANCiAgaWYgKGFueShFQ1BHPj1MaW1pdDIpKSBMYW1iZGExW0VDUEc+PUxpbWl0Ml0gPSAwDQogIGlmIChhbnkoRUNQRz49TGltaXQzKSkgTGFtYmRhMltFQ1BHPj1MaW1pdDNdID0gMA0KICANCiAgUSA9IExhbWJkYTAgKyBMYW1iZGExLygxLXBhcmFtZXRlcnNbJ0FscGhhMSddKSArIExhbWJkYTIvKDEtcGFyYW1ldGVyc1snQWxwaGEyJ10pDQogIA0KICBBID0gU3VtMiArIExhbWJkYTAqbWVhbi5wbGQgKyANCiAgICBMYW1iZGExKlZhUkFscGhhMS8oMS1wYXJhbWV0ZXJzWydBbHBoYTEnXSkgKw0KICAgIExhbWJkYTIqVmFSQWxwaGEyLygxLXBhcmFtZXRlcnNbJ0FscGhhMiddKQ0KICANCiAgRXEgPSBBL1ENCiAgDQogIGlmIChhbnkoaXMubmEoRXEpKSkgcmV0dXJuKE5BKQ0KICByZXR1cm4oRXEpDQp9DQpgYGANCg0KV2Ugd2lsbCB1c2UgYSBsaXN0IHdpdGggYWxsIHRoZSBwYXJhbWV0ZXJzIGFuZCBkYXRhIHdoaWNoIHdpbGwgYmUgY2FsbGVkIEVDUEdfT2JqZWN0LCBpbiBvcmRlciB0byBmYWNpbGl0YXRlIHRoZSBuZXh0IGZ1bmN0aW9uIGNhbGxzLiBUaGUgbmV4dCBmdW5jdGlvbiB3aWxsIGNyZWF0ZSB0aGUgbGlzdC4NCg0KYGBge3J9DQpjcmVhdGVFQ1BHT2JqZWN0IDwtIGZ1bmN0aW9uKGRhdGFzZXQsIGRpc2NvdW50VmVjdG9yLCBmb3J3YXJkUHJpY2UsIGJ1eWVyUGFyYW0sIHNlbGxlclBhcmFtKSB7DQogIFZhUjAgPSBhcHBseShkYXRhc2V0LCAxLCBxdWFudGlsZSwgMSkNCiAgVmFSMSA9IGFwcGx5KGRhdGFzZXQsIDEsIHF1YW50aWxlLCAwKQ0KICBtZWFuUExEID0gYXBwbHkoZGF0YXNldCwgMSwgbWVhbikNCiAgDQogIEVDUEdPYmplY3QgPSBsaXN0KA0KICAgIGRhdGFzZXQ9ZGF0YXNldCwNCiAgICBkaXNjb3VudFZlY3Rvcj1kaXNjb3VudFZlY3RvciwNCiAgICBmb3J3YXJkUHJpY2U9Zm9yd2FyZFByaWNlLA0KICAgIGJ1eWVyUGFyYW09YnV5ZXJQYXJhbSwNCiAgICBzZWxsZXJQYXJhbT1zZWxsZXJQYXJhbSwNCiAgICBWYVIwPVZhUjAsDQogICAgVmFSMT1WYVIxLA0KICAgIG1lYW5QTEQ9bWVhblBMRA0KICApDQogIHJldHVybihFQ1BHT2JqZWN0KQ0KfQ0KYGBgDQoNClRoZW4gd2Ugd2lsbCBkZWZpbmUgYSBmdW5jdGlvbiB0byBvYnRhaW4gdGhlIGVxdWlsaWJyaXVtIHByaWNlIHVzaW5nIHRoZSBjYWxjdWxhdGVkIHBoaSBmb3IgZWFjaCBhZ2VudC4gVGhlIGRpc3RpbmN0aW9uIGJldHdlZW4gdGhlIGFnZW50cyBtdXN0IGJlIG1hZGUgYmVjYXVzZSB0aGUgc2lnbmFsIG9mIHRoZSBwcmljZSBkYXRhIGNoYW5nZXMgYmV0d2VlbiB0aGVtLCBiZWNhdXNlIG9mIHRoZSBDb250cmFjdCBmb3IgRGlmZmVyZW5jZXMgcmV2ZW51ZSBlcXVhdGlvbnMuIA0KDQpgYGB7cn0NCkVDUEcuZXF1aWxpYnJpdW0gPC0gZnVuY3Rpb24gKEVDUEdvYmplY3QpIHsNCiAgYXV4TGlzdCA9IGxpc3QoDQogICAgVmFSMCA9IEVDUEdvYmplY3QkVmFSMCwNCiAgICBWYVIxID0gRUNQR29iamVjdCRWYVIxLA0KICAgIG1lYW5QTEQgPSBFQ1BHb2JqZWN0JG1lYW5QTEQNCiAgKQ0KICANCiAgYnV5ZXIucGhpID0gRUNQRy5waGkoRUNQR29iamVjdCRidXllclBhcmFtLCBFQ1BHb2JqZWN0JGRhdGFzZXQsIGF1eExpc3QpDQogIHNlbGxlci5waGkgPSAtRUNQRy5waGkoRUNQR29iamVjdCRzZWxsZXJQYXJhbSwgRUNQR29iamVjdCRkYXRhc2V0LCBhdXhMaXN0LCBidXllcj1GQUxTRSkNCiAgDQogIGVxdWlsaWJyaXVtLnByaWNlcyA9IChidXllci5waGkgKyBzZWxsZXIucGhpKS8yDQogIA0KICBhZGp1c3RlZC5wcmljZXMgPSBlcXVpbGlicml1bS5wcmljZXMvRUNQR29iamVjdCRkaXNjb3VudA0KICANCiAgYXZlcmFnZVByaWNlID0gbWVhbihhZGp1c3RlZC5wcmljZXMpDQogIA0KICBFQ1BHb2JqZWN0JGJ1eWVyUGhpID0gYnV5ZXIucGhpDQogIEVDUEdvYmplY3Qkc2VsbGVyUGhpID0gc2VsbGVyLnBoaQ0KICBFQ1BHb2JqZWN0JGVxdWlsaWJyaXVtUHJpY2VzID0gZXF1aWxpYnJpdW0ucHJpY2VzDQogIEVDUEdvYmplY3QkYWRqdXN0ZWRQcmljZXM9YWRqdXN0ZWQucHJpY2VzDQogIEVDUEdvYmplY3QkYXZlcmFnZVByaWNlPWF2ZXJhZ2VQcmljZQ0KICANCiAgcmV0dXJuKEVDUEdvYmplY3QpDQp9DQpgYGANCg0KTmV4dCBmdW5jdGlvbiBpcyB1c2VkIGluIGEgcmVjdXJzaXZlIGNhbGwgdG8gY2FsY3VsYXRlIHNlbnNpdGl2aXR5Lg0KDQpgYGB7cn0NCnNlbnNpdGl2aXR5LmZvbyA8LSBmdW5jdGlvbihpLCBhcmcsIG91dHB1dCwgY29tYmluYXRpb24ubWF0cml4LCBFQ1BHb2JqZWN0KSB7DQogIA0KICBFQ1BHb2JqZWN0W1thcmddXVtUUlVFXSA9IGNvbWJpbmF0aW9uLm1hdHJpeFtpLCBdDQogIA0KICByZXR1cm4oRUNQRy5lcXVpbGlicml1bShFQ1BHb2JqZWN0KVtbb3V0cHV0XV0pDQp9DQpgYGANCg0KQW5kIGZpbmFsbHkgYSBwbG90dGluZyBmdW5jdGlvbiBmb3IgdGhlIEVDUEdfT2JqZWN0Lg0KDQpgYGB7cn0NCnNlbnNpdGl2aXR5LnByZXR0eVBsb3QgPC0gZnVuY3Rpb24ocGxvdC5tYXRyaXgsIHhsYWJlbD0iIiwgeWxpbT1jKDAsMjUwKSwgbHdkID0gMiwgbGVnZW5kVGl0bGUsIGxlZ2VuZFBvcz0iYm90dG9tcmlnaHQiLCBpbnNldD0wLjA1LCBuY29sPTEsIGNleD0xLCBsZWdDZXg9MC45KSB7DQogIE5zZXJpZXMgPSBuY29sKHBsb3QubWF0cml4KQ0KICB2aXJpZGlzLnBhbGxldHRlID0gdmlyaWRpcyhOc2VyaWVzKQ0KICBsaW5lLnR5cGVzID0gcmVwKDEsIE5zZXJpZXMpDQogIA0KICBOcm93cyA9IDE6bnJvdyhwbG90Lm1hdHJpeCkNCiAgbGFiZWxzPXJvdy5uYW1lcyhwbG90Lm1hdHJpeCkNCiAgDQogIHBhcihmYW1pbHk9IkEiLCBjZXg9Y2V4KQ0KICBtYXRwbG90KE5yb3dzLCBwbG90Lm1hdHJpeCwgdHlwZT0nbCcsIHhsYWI9eGxhYmVsLCB5bGFiPSdFbGVjdHJpY2l0eSBmb3J3YXJkIHByaWNlIChSJC9NV2gpJywgeWxpbT15bGltLCBsdHk9bGluZS50eXBlcywgY29sPXZpcmlkaXMucGFsbGV0dGUsIGx3ZD1sd2QsIGF4ZXM9RikNCiAgYXhpcygyKQ0KICBheGlzKHNpZGU9MSxhdD1Ocm93cyxsYWJlbHM9bGFiZWxzKQ0KICBncmlkIChOVUxMLE5VTEwsIGx0eSA9IDYsIGNvbCA9ICJncmV5IikNCiAgbGVnZW5kKHg9bGVnZW5kUG9zLCBpbnNldD1pbnNldCwgbGVnZW5kPWNvbG5hbWVzKHBsb3QubWF0cml4KSwgaG9yaXo9RkFMU0UsIGx0eT1saW5lLnR5cGVzLCBjb2w9dmlyaWRpcy5wYWxsZXR0ZSwgbHdkPWx3ZCwgY2V4PWxlZ0NleCwgbmNvbD1uY29sLCB0aXRsZT1sZWdlbmRUaXRsZSkNCn0NCmBgYA0KDQojIyMgTG9jYWwgdmFyaWFibGVzIGRlZmluaXRpb24NCg0KVGhlc2UgdmFyaWFibGVzIHdpbGwgYmUgdXNlZCBmb3IgYWxsIHNlbnNpdGl2aXR5IHRlc3RzLiBUaGUgb3B0aW1pemF0aW9uIHBhcmFtZXRlcnMgYXJlIG9idGFpbmVkIGZyb20gdGhlIHRyYWluaW5nIHN0ZXAuDQoNCmBgYHtyfQ0KYnV5ZXIucGFyYW0gPSBjKExhbWJkYTEgPSAwLjI1MTU5OTI3LCBMYW1iZGEyID0gMC4wOTM5MDkyNCwgQWxwaGExID0gMC4zMjMzNDMzMCwgQWxwaGEyID0gMC42OTI5MzExNSkNCnNlbGxlci5wYXJhbSA9IGMoTGFtYmRhMSA9IDAuMTcxMjcyMSwgTGFtYmRhMiA9IDAuNzQ2Njg2NSwgQWxwaGExID0gMC43NjQxOTM0LCBBbHBoYTIgPSAwLjk5MTI3NTUpDQpgYGANCg0KIyMjIFZhcmlhYmxlcyBmb3IgSnVseSBBKzEgYW5hbHlzaXMNCg0KQ3JlYXRlIGEgdmVjdG9yIHdpdGggZGlzY291bnQgcmF0ZXMgdG8gYWRqdXN0IHRoZSBlcXVpbGlicml1bSBwcmljZXMgd2l0aCB0aGUgcmlzay1mcmVlIHJldHVybiByYXRlLiBSaXNrLWZyZWUgcmV0dXJuIHJhdGUgaXMgb2J0YWluZWQgYXMgYW4gYXBwcm94aW1hdGlvbiBvZiB0aGUgU0VMSUMgbmF0aW9uYWwgcmF0ZS4NCg0KYGBge3J9DQojIFNldCByaXNrIGZyZWUgcmF0ZXMNCmFudWFsUmF0ZSA9IDAuMDUNCm1vbnRobHlSYXRlID0gKDErYW51YWxSYXRlKV4oMS8xMiktMQ0KDQp0ZW1wLmRpc2NvdW50LnZlY3RvciA8LSByZXAoTkEsIDIzKQ0KZm9yIChpIGluIDE6MjMpew0KICB0ZW1wLmRpc2NvdW50LnZlY3RvcltpXSA8LSAoMSttb250aGx5UmF0ZSleKGktMSkNCn0NCmp1bHlBMS5kaXNjb3VudFZlY3RvciA9IHRlbXAuZGlzY291bnQudmVjdG9yWzU6MTZdDQpgYGANCg0KSW5wdXQgdGhlIGZvcndhcmQgcHJpY2UgZm9yIGVsZWN0cmljaXR5IG9idGFpbmVkIGZyb20gRENpZGUgRW5lcmdpYS4NCg0KYGBge3J9DQpqdWx5QTEuZm9yd2FyZFByaWNlID0gMjA1LjQzDQpgYGANCg0KIyMjIExvYWRpbmcgdGhlIEp1bHkgQSsxIGRhdGENCg0KTG9hZGluZyBmdXR1cmUgc3BvdCBwcmljZSAoUExEKSBkYXRhLg0KDQpgYGB7cn0NCmp1bHlBMS5vcmlnaW5hbC5kYXRhID0gcmVhZC5jc3YoZmlsZT0nLi9EYXRhL0RhdGFfSnVsXzIwMTlfQTEuY3N2JywgY29sLm5hbWVzPXBhc3RlKG1vbnRoLmFiYiwgMjAyMCwgc2VwPSItIikpDQpqdWx5QTEuZGF0YSA9IHQoanVseUExLm9yaWdpbmFsLmRhdGEpDQpgYGANCg0KQ3JlYXRlIGEgbGlzdCB3aXRoIGFsbCB2YXJpYWJsZXMgYW5kIGRhdGEgdG8gcGFzcyBvbiB0byBmdW5jdGlvbnMuDQoNCmBgYHtyfQ0KanVseUExLkVDUEdfb2JqZWN0ID0gY3JlYXRlRUNQR09iamVjdChkYXRhc2V0ID0ganVseUExLmRhdGEsIGJ1eWVyUGFyYW0gPSBidXllci5wYXJhbSwgc2VsbGVyUGFyYW0gPSBzZWxsZXIucGFyYW0sIGRpc2NvdW50VmVjdG9yID0ganVseUExLmRpc2NvdW50VmVjdG9yLCBmb3J3YXJkUHJpY2UgPSBqdWx5QTEuZm9yd2FyZFByaWNlKQ0KYGBgDQoNCkdldCB0aGUgb3JpZ2luYWwgcmVzdWx0cyBmb3IgY29tcGFyaXNvbi4NCg0KYGBge3J9DQpqdWx5QTEuUmVzdWx0cyA9IEVDUEcuZXF1aWxpYnJpdW0oanVseUExLkVDUEdfb2JqZWN0KQ0KYGBgDQoNCg0KIyMjIHNlbnNpdGl2aXR5IGZvciBMYW1iZGEgMSBhbmQgTGFtYmRhIDINCg0KVGhlIGZpcnN0IGFuYWx5c2lzIGlzIHJlZ2FyZGluZyAkXGxhbWJkYV8xJCBhbmQgJFxsYW1iZGFfMiQgcGFyYW1ldGVycyBmb3IgZWFjaCBhZ2VudC4NClRob3NlIGFyZSB0aGUgYXV4aWxpYXJ5IHZhcmlhYmxlcyBzZXQgZm9yIHRoaXMgdGFzay4NCg0KYGBge3J9DQojIFNldCBzZW5zaXRpdml0eSByYW5nZXMNCg0KbGFtYmRhcy52ZWMgPSBzZXEoMCwgMC40NSwgYnk9MC4wNSkNCmxhbWJkYXMubWF0cml4ID0gYXMubWF0cml4KGV4cGFuZC5ncmlkKGxhbWJkYXMudmVjLCBsYW1iZGFzLnZlYykpDQpjb2xuYW1lcyhsYW1iZGFzLm1hdHJpeCkgPSBjKCdMYW1iZGExJywgJ0xhbWJkYTInKQ0KbkxhbWJkYXMgPSBucm93KGxhbWJkYXMubWF0cml4KQ0KYGBgDQoNCkZyb20gdGhlIHJhbmdlIHNldCBhYm92ZSBhIGNvbWJpbmF0aW9uIG1hdHJpeCBpcyBjcmVhdGVkIHdpdGggdGhlICoqYnV5ZXIqKiBwYXJhbWV0ZXJzLg0KDQpgYGB7cn0NCmJ1eWVyLmxhbWJkYXMubWF0cml4ID0gY2JpbmQobGFtYmRhcy5tYXRyaXgsIEFscGhhMT1yZXAoYnV5ZXIucGFyYW1bJ0FscGhhMSddLCBuTGFtYmRhcyksIEFscGhhMj1yZXAoYnV5ZXIucGFyYW1bJ0FscGhhMiddLCBuTGFtYmRhcykpDQpgYGANCg0KTmV4dCwgY2FsbCB0aGUgc2Vuc2l0aXZpdHkgZnVuY3Rpb24gYW5kIGNyZWF0ZSBhIG1hdHJpeCBvZiB0aGUgcmVzdWx0cyBmb3IgdGhlICoqYnV5ZXIqKi4NCg0KYGBge3J9DQpsYW1iZGFCdXllci5zZW5zaXRpdml0eSA9IHNhcHBseSgxOm5MYW1iZGFzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vuc2l0aXZpdHkuZm9vLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJnID0gImJ1eWVyUGFyYW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gImF2ZXJhZ2VQcmljZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21iaW5hdGlvbi5tYXRyaXggPSBidXllci5sYW1iZGFzLm1hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp1bHlBMS5FQ1BHX29iamVjdCkNCg0KbGFtYmRhQnV5ZXIuc2Vuc2lNYXRyaXggPSBtYXRyaXgobGFtYmRhQnV5ZXIuc2Vuc2l0aXZpdHksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sPWxlbmd0aChsYW1iZGFzLnZlYyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieXJvdyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGFtYmRhcy52ZWMsIGxhbWJkYXMudmVjKSkNCmBgYA0KDQpEbyB0aGUgc2FtZSBmb3IgdGhlICoqc2VsbGVyKiouDQoNCmBgYHtyfQ0Kc2VsbGVyLmxhbWJkYXMubWF0cml4ID0gY2JpbmQobGFtYmRhcy5tYXRyaXgsIEFscGhhMT1yZXAoc2VsbGVyLnBhcmFtWydBbHBoYTEnXSwgbkxhbWJkYXMpLCBBbHBoYTI9cmVwKHNlbGxlci5wYXJhbVsnQWxwaGEyJ10sIG5MYW1iZGFzKSkNCmBgYA0KDQpgYGB7cn0NCmxhbWJkYVNlbGxlci5zZW5zaXRpdml0eSA9IHNhcHBseSgxOm5MYW1iZGFzLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnNpdGl2aXR5LmZvbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmcgPSAic2VsbGVyUGFyYW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9ICJhdmVyYWdlUHJpY2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbWJpbmF0aW9uLm1hdHJpeCA9IHNlbGxlci5sYW1iZGFzLm1hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBqdWx5QTEuRUNQR19vYmplY3QpDQoNCmxhbWJkYVNlbGxlci5zZW5zaU1hdHJpeCA9IG1hdHJpeChsYW1iZGFTZWxsZXIuc2Vuc2l0aXZpdHksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbD1sZW5ndGgobGFtYmRhcy52ZWMpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5cm93ID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGFtYmRhcy52ZWMsIGxhbWJkYXMudmVjKSkNCmBgYA0KDQpUaGUgcmVzdWx0IGFyZSBwbG90dGVkIGluIGFuIGludGVyYWN0aXZlIHdpZGdldCB1c2luZyAqcGxvdGx5Ki4NCg0KYGBge3J9DQpyZXF1aXJlKHBsb3RseSkNCg0KYXgueHkgPC0gbGlzdCh0aWNrZXRtb2RlID0gJ2FycmF5JywgdGlja3RleHQgPSBhcy5jaGFyYWN0ZXIobGFtYmRhcy52ZWMpLCB0aWNrdmFscz0wOjkpDQpheHogPC0gbGlzdCh0aXRsZSA9ICJFbGVjdHJpY2l0eSBmb3J3YXJkIHByaWNlIChSJC9NV2gpIikNCmBgYA0KDQpBbmFseXNpcyBvZiAkXGxhbWJkYV8xJCBhbmQgJFxsYW1iZGFfMiQgZm9yIHRoZSAqKmJ1eWVyKiouIFRoZSBjaGFydCBiZWxvdyBpcyBpbnRlcmFjdGl2ZS4NCg0KYGBge3J9DQpmaWcuQnV5ZXIgPC0gcGxvdF9seSh3aWR0aD0xMDUwLCBoZWlnaHQ9NzUwKSAlPiUNCiAgYWRkX3N1cmZhY2UoeiA9IGxhbWJkYUJ1eWVyLnNlbnNpTWF0cml4KSAlPiUNCiAgbGF5b3V0KHNjZW5lID0gbGlzdCh4YXhpcz1jKGF4Lnh5LCB0aXRsZSA9ICJCdXllciBMYW1iZGExIikseWF4aXM9YyhheC54eSwgdGl0bGUgPSAiQnV5ZXIgTGFtYmRhMiIpLHpheGlzPWF4eikpDQoNCmZpZy5CdXllcg0KYGBgDQoNCkNyZWF0ZSAyRCB2aXN1YWxpemF0aW9uIGZvciB0aGUgYWJvdmUgZGF0YSB3aXRoIFBsb3RseSBjb250b3VyIHBsb3QNCg0KYGBge3J9DQpmaWcuQnV5ZXJMYW1iZGFGbGF0IDwtIHBsb3RfbHkoDQogIHggPSBsYW1iZGFzLnZlYywgDQogIHkgPSBsYW1iZGFzLnZlYywgDQogIHogPSBsYW1iZGFCdXllci5zZW5zaU1hdHJpeCwgDQogIHR5cGUgPSAiY29udG91ciIgDQopDQoNCmZpZy5CdXllckxhbWJkYUZsYXQgPC0gZmlnLkJ1eWVyTGFtYmRhRmxhdCAlPiUgY29sb3JiYXIoDQogIGxlbj0wLjg1LA0KICB0aXRsZSA9ICJFbGVjdHJpY2l0eVxuRm9yd2FyZFxuUHJpY2VcbihSJC9NV2gpIikNCg0KZmlnLkJ1eWVyTGFtYmRhRmxhdCA8LSBmaWcuQnV5ZXJMYW1iZGFGbGF0ICU+JSBsYXlvdXQoDQogICAgICAgICAgICAgICAgICAgICAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIkJ1eWVyJ3MgTGFtYmRhIDEiKSwNCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiQnV5ZXIncyBMYW1iZGEgMiIpLA0KICAgICAgICAgICAgICAgICAgICAgIGZvbnQ9bGlzdChzaXplPTI0KQ0KICAgICAgICAgICAgICAgICAgICAgICkNCg0KZmlnLkJ1eWVyTGFtYmRhRmxhdA0KYGBgDQoNCkFuYWx5c2lzIG9mICRcbGFtYmRhXzEkIGFuZCAkXGxhbWJkYV8yJCBmb3IgdGhlICoqc2VsbGVyKiouIFRoZSBjaGFydCBiZWxvdyBpcyBpbnRlcmFjdGl2ZS4NCg0KYGBge3J9DQpmaWcuU2VsbGVyIDwtIHBsb3RfbHkod2lkdGg9MTA1MCwgaGVpZ2h0PTc1MCkgJT4lDQogIGFkZF9zdXJmYWNlKHogPSBsYW1iZGFTZWxsZXIuc2Vuc2lNYXRyaXgpICU+JQ0KICBsYXlvdXQoc2NlbmUgPSBsaXN0KHhheGlzPWMoYXgueHksIHRpdGxlID0gIlNlbGxlciBMYW1iZGExIikseWF4aXM9YyhheC54eSwgdGl0bGUgPSAiU2VsbGVyIExhbWJkYTIiKSx6YXhpcz1heHopKQ0KDQpmaWcuU2VsbGVyDQpgYGANCg0KQ3JlYXRlIDJEIHZpc3VhbGl6YXRpb24gZm9yIHRoZSBhYm92ZSBkYXRhIHdpdGggUGxvdGx5IGNvbnRvdXIgcGxvdA0KDQpgYGB7cn0NCmZpZy5TZWxsZXJMYW1iZGFGbGF0IDwtIHBsb3RfbHkoDQogIHggPSBsYW1iZGFzLnZlYywgDQogIHkgPSBsYW1iZGFzLnZlYywgDQogIHogPSBsYW1iZGFTZWxsZXIuc2Vuc2lNYXRyaXgsIA0KICB0eXBlID0gImNvbnRvdXIiIA0KKQ0KDQpmaWcuU2VsbGVyTGFtYmRhRmxhdCA8LSBmaWcuU2VsbGVyTGFtYmRhRmxhdCAlPiUgY29sb3JiYXIoDQogIGxlbj0wLjg1LA0KICB0aXRsZSA9ICJFbGVjdHJpY2l0eVxuRm9yd2FyZFxuUHJpY2VcbihSJC9NV2gpIikNCg0KZmlnLlNlbGxlckxhbWJkYUZsYXQgPC0gZmlnLlNlbGxlckxhbWJkYUZsYXQgJT4lIGxheW91dCgNCiAgeGF4aXMgPSBsaXN0KHRpdGxlID0gIlNlbGxlcidzIExhbWJkYSAxIiksDQogIHlheGlzID0gbGlzdCh0aXRsZSA9ICJTZWxsZXIncyBMYW1iZGEgMiIpLA0KICBmb250PWxpc3Qoc2l6ZT0yNCkNCikNCg0KZmlnLlNlbGxlckxhbWJkYUZsYXQNCmBgYA0KDQojIyMgc2Vuc2l0aXZpdHkgZm9yIExhbWJkYSAxIGFuZCBBbHBoYSAxIGluIEVDUA0KDQpUaGUgZm9sbG93aW5nIHRlc3QgaXMgcmVnYXJkaW5nIGJvdGggJFxhbHBoYSQgYW5kICRcbGFtYmRhJCBwYXJhbWV0ZXJzIGZvciBlYWNoIGFnZW50IHVzaW5nIEVDUCBtZWFzdXJlIGluc3RlYWQgb2YgRUNQX0cuDQpUaG9zZSBhcmUgdGhlIGF1eGlsaWFyeSB2YXJpYWJsZXMgc2V0IGZvciB0aGlzIHRhc2suDQoNCmBgYHtyfQ0KbGFtYmRhQWxwaGEudmVjID0gc2VxKDAsIDAuOSwgYnk9MC4xKQ0KbGFtYmRhQWxwaGEubWF0cml4ID0gYXMubWF0cml4KGV4cGFuZC5ncmlkKGxhbWJkYUFscGhhLnZlYywgbGFtYmRhQWxwaGEudmVjKSkNCmNvbG5hbWVzKGxhbWJkYUFscGhhLm1hdHJpeCkgPSBjKCdMYW1iZGExJywgJ0FscGhhMScpDQpuTGFtYmRhQWxwaGEgPSBucm93KGxhbWJkYUFscGhhLm1hdHJpeCkNCmBgYA0KDQpGcm9tIHRoZSByYW5nZSBzZXQgYWJvdmUgYSBjb21iaW5hdGlvbiBtYXRyaXggaXMgY3JlYXRlZCB3aXRoIHRoZSAqKmJ1eWVyKiogcGFyYW1ldGVycy4NCg0KYGBge3J9DQpidXllci5sYW1iZGFBbHBoYS5tYXRyaXggPSBjYmluZChsYW1iZGFBbHBoYS5tYXRyaXgsIExhbWJkYTI9cmVwKDAsIG5MYW1iZGFBbHBoYSksIEFscGhhMj1yZXAoYnV5ZXIucGFyYW1bJ0FscGhhMiddLCBuTGFtYmRhQWxwaGEpKQ0KYnV5ZXIubGFtYmRhQWxwaGEubWF0cml4ID0gYnV5ZXIubGFtYmRhQWxwaGEubWF0cml4WywgYygnTGFtYmRhMScsICdMYW1iZGEyJywgJ0FscGhhMScsICdBbHBoYTInKV0NCmBgYA0KDQpOZXh0LCBjYWxsIHRoZSBzZW5zaXRpdml0eSBmdW5jdGlvbiBhbmQgY3JlYXRlIGEgbWF0cml4IG9mIHRoZSByZXN1bHRzIGZvciB0aGUgKipidXllcioqLg0KDQpgYGB7cn0NCmxhbWJkYUFscGhhQnV5ZXIuc2Vuc2l0aXZpdHkgPSBzYXBwbHkoMTpuTGFtYmRhQWxwaGEsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbnNpdGl2aXR5LmZvbywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJnPSJidXllclBhcmFtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gImF2ZXJhZ2VQcmljZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbWJpbmF0aW9uLm1hdHJpeCA9IGJ1eWVyLmxhbWJkYUFscGhhLm1hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2l0aGluKGp1bHlBMS5FQ1BHX29iamVjdCwgYnV5ZXJQYXJhbVtUUlVFXSA8LSByZXAoMCw0KSkpDQoNCmxhbWJkYUFscGhhQnV5ZXIuc2Vuc2lNYXRyaXggPSBtYXRyaXgobGFtYmRhQWxwaGFCdXllci5zZW5zaXRpdml0eSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbD1sZW5ndGgobGFtYmRhQWxwaGEudmVjKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnlyb3cgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGFtYmRhQWxwaGEudmVjLCBsYW1iZGFBbHBoYS52ZWMpKQ0KDQpgYGANCg0KQW5kIHRoZSBzYW1lIGlzIGRvbmUgZm9yIHRoZSAqKnNlbGxlcioqLg0KDQpgYGB7cn0NCnNlbGxlci5sYW1iZGFBbHBoYS5tYXRyaXggPSBjYmluZChsYW1iZGFBbHBoYS5tYXRyaXgsIExhbWJkYTI9cmVwKDAsIG5MYW1iZGFBbHBoYSksIEFscGhhMj1yZXAoc2VsbGVyLnBhcmFtWydBbHBoYTInXSwgbkxhbWJkYUFscGhhKSkNCnNlbGxlci5sYW1iZGFBbHBoYS5tYXRyaXggPSBzZWxsZXIubGFtYmRhQWxwaGEubWF0cml4WywgYygnTGFtYmRhMScsICdMYW1iZGEyJywgJ0FscGhhMScsICdBbHBoYTInKV0NCmBgYA0KDQpgYGB7cn0NCmxhbWJkYUFscGhhU2VsbGVyLnNlbnNpdGl2aXR5ID0gc2FwcGx5KDE6bkxhbWJkYUFscGhhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vuc2l0aXZpdHkuZm9vLGFyZz0ic2VsbGVyUGFyYW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3V0cHV0ID0gImF2ZXJhZ2VQcmljZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21iaW5hdGlvbi5tYXRyaXggPSBzZWxsZXIubGFtYmRhQWxwaGEubWF0cml4LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2l0aGluKGp1bHlBMS5FQ1BHX29iamVjdCwgc2VsbGVyUGFyYW1bVFJVRV0gPC0gcmVwKDAsNCkpKQ0KDQpsYW1iZGFBbHBoYVNlbGxlci5zZW5zaU1hdHJpeCA9IG1hdHJpeChsYW1iZGFBbHBoYVNlbGxlci5zZW5zaXRpdml0eSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2w9bGVuZ3RoKGxhbWJkYUFscGhhLnZlYyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieXJvdyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QobGFtYmRhQWxwaGEudmVjLCBsYW1iZGFBbHBoYS52ZWMpKQ0KYGBgDQoNClRoZSByZXN1bHRzIGFyZSB2aXN1YWxpemVkIHdpdGggb3VyIG93biBwbG90IGZ1bmN0aW9uLiBMaWJyYXJ5ICp2aXJpZGlzKiBpcyBjYWxsZWQgdG8gcHJvdmlkZSBjb2xvciBwYWxsZXR0ZSBmb3IgY29sb3JibGluZCB2aXN1YWxpemF0aW9uLg0KDQpgYGB7cn0NCnJlcXVpcmUodmlyaWRpcykNCmBgYA0KDQpBbHBoYS1MYW1iZGEgc2Vuc2l0aXZpdHkgZm9yIHRoZSAqKmJ1eWVyKiouDQoNCmBgYHtyfQ0Kc2Vuc2l0aXZpdHkucHJldHR5UGxvdChsYW1iZGFBbHBoYUJ1eWVyLnNlbnNpTWF0cml4LCB4bGFiZWw9ZXhwcmVzc2lvbihwYXN0ZSgiQnV5ZXIgIiwgYWxwaGEsIDEpKSwgeWxpbT1jKDE3MCwgMjUwKSwgbGVnZW5kUG9zID0gImJvdHRvbSIsIG5jb2w9NSwgY2V4PTEuNCwgbHdkPTMsIGxlZ2VuZFRpdGxlPWV4cHJlc3Npb24ocGFzdGUoIkJ1eWVyICIsIGxhbWJkYSwgMSkpKQ0KYGBgDQoNCkFscGhhLUxhbWJkYSBzZW5zaXRpdml0eSBmb3IgdGhlICoqc2VsbGVyKiouDQoNCmBgYHtyfQ0Kc2Vuc2l0aXZpdHkucHJldHR5UGxvdChsYW1iZGFBbHBoYVNlbGxlci5zZW5zaU1hdHJpeCwgeGxhYmVsPWV4cHJlc3Npb24ocGFzdGUoIlNlbGxlciAiLCBhbHBoYSwgMSkpLCB5bGltPWMoMCwgMTgwKSwgbGVnZW5kVGl0bGU9ZXhwcmVzc2lvbihwYXN0ZSgiU2VsbGVyICIsIGxhbWJkYSwgMSkpLCBsZWdlbmRQb3MgPSAiYm90dG9tIiwgbmNvbD01LCBpbnNldD0wLjAyLCBjZXg9MS40LCBsd2Q9MykNCmBgYA0KDQojIyMgUHJlcGFyZSB2YXJpYWJsZXMgZm9yIHRoZSByaXNrLWZyZWUgcmF0ZSBzZW5zaXRpdml0eSBhbmFseXNpcw0KDQpTZXQgYXV4aWxpYXJ5IHZhcmlhYmxlcy4NCg0KYGBge3J9DQphbm51YWxSYXRlcy52ZWMgPSBzZXEoMCwgMC4wOSwgYnk9MC4wMSkNCm1vbnRobHlSYXRlcy52ZWMgPSAoMSthbm51YWxSYXRlcy52ZWMpXigxLzEyKS0xDQpuUmF0ZXMgPSBsZW5ndGgobW9udGhseVJhdGVzLnZlYykNCmBgYA0KDQpDcmVhdGUgYSBkaXNjb3VudCBtYXRyaXggdG8gYmUgcGFzc2VkIHRvIHRoZSBzZW5zaXRpdml0eSBmdW5jdGlvbi4NCg0KYGBge3J9DQpkaXNjb3VudC5tYXRyaXggPC0gbWF0cml4KE5BLCBuY29sPTM1LCBucm93PW5SYXRlcykNCmZvciAoaWkgaW4gMTpuUmF0ZXMpIHsNCiAgZm9yIChpIGluIDE6MzUpew0KICAgIGRpc2NvdW50Lm1hdHJpeFtpaSwgaV0gPC0gKDErbW9udGhseVJhdGVzLnZlY1tpaV0pXihpLTEpDQogIH0NCn0NCmRpc2NvdW50Lm1hdHJpeCA9IGRpc2NvdW50Lm1hdHJpeFssIDI0OjM1XQ0Kcm93bmFtZXMoZGlzY291bnQubWF0cml4KSA9IGFubnVhbFJhdGVzLnZlYw0KYGBgDQoNCkFwcGx5IHRoZSBzZW5zaXRpdml0eSBmdW5jdGlvbiB0byBvYnRhaW4gdGhlIGF2ZXJhZ2UgcHJpY2UgZm9yIGVhY2ggcmlzay1mcmVlIHJhdGUuDQoNCmBgYHtyfQ0KcmF0ZXMuc2Vuc2l0aXZpdHkgPSBzYXBwbHkoMTpuUmF0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzZW5zaXRpdml0eS5mb28sDQogICAgICAgICAgICAgICAgICAgICAgICAgICBhcmc9ImRpc2NvdW50VmVjdG9yIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG91dHB1dCA9ICJhdmVyYWdlUHJpY2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbWJpbmF0aW9uLm1hdHJpeCA9IGRpc2NvdW50Lm1hdHJpeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGp1bHlBMS5FQ1BHX29iamVjdCkNCm5hbWVzKHJhdGVzLnNlbnNpdGl2aXR5KSA9IGFubnVhbFJhdGVzLnZlYw0KYGBgDQoNCkNoZWNrIGlmIHJlc3VsdHMgYXJlIGludGVybmFsbHkgY29uc2lzdGVudC4NCg0KYGBge3J9DQpqdWx5QTEuUmVzdWx0cyA9IEVDUEcuZXF1aWxpYnJpdW0od2l0aGluKGp1bHlBMS5FQ1BHX29iamVjdCwgZGlzY291bnRWZWN0b3IgPC0gZGlzY291bnQubWF0cml4WyIwLjA1IiwgXSkpDQpqdWx5QTEuUmVzdWx0cyRhdmVyYWdlUHJpY2UgPT0gcmF0ZXMuc2Vuc2l0aXZpdHlbIjAuMDUiXQ0KYGBgDQoNClBsb3QgdGhlIHJlc3VsdHMNCg0KYGBge3J9DQpwbG90KGFubnVhbFJhdGVzLnZlYywgcmF0ZXMuc2Vuc2l0aXZpdHksIHlsaW09YygwLDI1MCksIHlsYWI9IkVsZWN0cmljaXR5IGZvcndhcmQgcHJpY2UgKFIkL01XaCkiLCB4bGFiPSJBbm51YWwgUmlzay1GcmVlIFJhdGUiLCBjb2w9InN0ZWVsYmx1ZSIpDQpgYGANCg0KDQoNCg0KIyMjIFNlZSBhbHNvOg0KDQpNb2RlbCBUcmFpbmluZyAoW2xpbmtdKC4vTW9kZWxUcmFpbmluZ05vdGVib29rLm5iLmh0bWwpKQ0KDQpNb2RlbCBWYWxpZGF0aW9uIChbbGlua10oLi9Nb2RlbFZhbGlkYXRpb25Ob3RlYm9vay5uYi5odG1sKSkNCg==