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 validation of the optimized parameters which were obtained in the optimization 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 define another function that explicitly calculates the risk premium for both agents. This function is redundant with the above one and the whole code could be simplified, but its objective is to provide a double verification step. The risk premium can also be calculated by using the simpler formulas provided in the article.

ECPG.premium <- 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 + Lambda1*(mean.pld - VaRAlpha1)/(1-parameters['Alpha1']) +
    Lambda2*(mean.pld - 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)
  
  buyer.premium = ECPG.premium(ECPGobject$buyerParam, ECPGobject$dataset, auxList)
  seller.premium = -ECPG.premium(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$buyerPremium = buyer.premium
  ECPGobject$sellerPremium = seller.premium
  ECPGobject$equilibriumPrices = equilibrium.prices
  ECPGobject$adjustedPrices=adjusted.prices
  ECPGobject$averagePrice=averagePrice
  
  return(ECPGobject)
}

Next we define two functions for defining the discount vector, based on the annual risk-free rate.

getCompleteDiscountVector <- function(index, modifier, anualRate) {
  yearAdj = (11+modifier*12)
  monthlyRate = (1+anualRate)^(1/12)-1
  completeDiscountVector <- rep(NA, yearAdj)
  for (i in 1:yearAdj){
    completeDiscountVector[i] <- (1+monthlyRate)^(i-1)
  }
  
  return(completeDiscountVector)
}

getDiscountBounds <- function(index, modifier, completeDiscountVector) {
  lower.limit = 1+12*modifier-index
  upper.limit = 12+12*modifier-index
  
  return(completeDiscountVector[lower.limit:upper.limit])
}

And finally a plotting function for the ECPG_Object.

data.prettyPlot <- function(DataObject, ylim=c(0,350), lwd = 2, legendPos="bottomright", inset=0.05, cex=1, legCex=0.7, horiz=FALSE) {
  plot.matrix = cbind(DataObject$meanPLD, DataObject$forwardPrice, DataObject$averagePrice)
  colnames(plot.matrix) = c("NEWAVE Spot", "DCide Forward", "Model Forward")
  rownames(plot.matrix) = DataObject$labels
  
  Nseries = ncol(plot.matrix)
  color.pallette = c("#56B4E9", "#F0E442", "#E69F00") # Selected for easy visualization for colorblind
  line.types = c(2, 1, 1)
  
  Nrows = 1:nrow(plot.matrix)
  labels=row.names(plot.matrix)
  
  LastInSample = which(data.index[,3]=="OUTSAMPLE")[1] - 1
  
  par(family="serif", cex=cex)
  matplot(Nrows, plot.matrix, type='l', xlab="", ylab='Electricity forward price (R$/MWh)', ylim=ylim, lty=line.types, lwd=lwd, col=color.pallette, axes=F)
  axis(2)
  axis(side=1,at=Nrows,labels=labels)
  grid (NULL,NULL, lty = 6, col = "grey", lwd = 1)
  abline(v=LastInSample + 0.5, lty = 2)
  text(x=LastInSample/2 + 0.5, y=25, "In sample")
  text(x=(nrow(plot.matrix) - LastInSample)/2 + LastInSample + 0.5, y=25, "Out-of-sample")
  legend(x=legendPos, inset=inset, legend=colnames(plot.matrix),col=color.pallette, lty=line.types, cex=legCex, horiz=horiz, lwd=lwd)
}

Local variables definition

Set the calibrated parameters, obtained from the training stage and the annual risk-free rate. A sensitivity analysis for this rate is provided in the sensitivity analysis sector (link).

## Obtain optimized parameters from training 
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)

anualRate = 0.05

Loading Data

Define the data points to be used for the model validation.

data.index = rbind(
  c(7, 2019, "insample"),
  c(8, 2019, "insample"),
  c(9, 2019, "insample"),
  c(10, 2019, "insample"),
  c(11, 2019, "insample"),
  c(12, 2019, "insample"),
  c(1, 2020, "insample"),
  c(2, 2020, "insample"),
  c(3, 2020, "OUTSAMPLE"),
  c(4, 2020, "OUTSAMPLE"),
  c(5, 2020, "OUTSAMPLE"),
  c(6, 2020, "OUTSAMPLE"),
  c(7, 2020, "OUTSAMPLE"),
  c(8, 2020, "OUTSAMPLE"),
  c(9, 2020, "OUTSAMPLE"),
  c(10, 2020, "OUTSAMPLE")
)

# Define the number of years ahead that the forward price is applied to.
seriesA = 1

Load and select the forward price for electricity obtained from DCide Energia.

forwardPrice.data = read.csv(file='./Data/ForwardPrices.csv')
colnames(forwardPrice.data) = c("Period", paste(c(rep(2019,6), rep(2020, 10)), c(7:12,1:10), sep="_"))

selectedData = paste(data.index[,2], data.index[,1], sep="_")
forwardPrices = as.numeric(forwardPrice.data[seriesA,selectedData])

Calculated the relevant discount vector for the whole period.

completeDiscountVector = getCompleteDiscountVector(as.numeric(data.index[,1]), seriesA, anualRate)

Start of Validation

Create accessory variables

data.abbrev = paste(month.abb[as.numeric(data.index[,1])], data.index[,2], sep="_")

all.data = list()
averagePrices = c()
spotPrices = c()
buyer.phi = c()
seller.phi = c()
buyer.premium = c()
seller.premium = c()

Run the validation for each data point in a loop.

for (n in 1:nrow(data.index)) {
  
  ## Set Variables
  discountVector = getDiscountBounds(as.numeric(data.index[n,1]), seriesA, completeDiscountVector)
  
  forwardPrice = forwardPrices[n]
  
  ## Load Data
  original.data = read.csv(file=paste('./Data/Data', data.abbrev[n], paste0('A', seriesA, '.csv'), sep="_"), col.names=paste(month.abb, as.numeric(data.index[n,2])+seriesA, sep="-"))
  
  spotPrices[n] = mean(colMeans(original.data))
  
  ECPG_object = createECPGObject(dataset = t(original.data), buyerParam = buyer.param, sellerParam = seller.param, discountVector = discountVector, forwardPrice = forwardPrice)
  
  ## Calculate results from optimized parameters
  ECPG_object = ECPG.equilibrium(ECPG_object)
  
  all.data[[data.abbrev[n]]] <- ECPG_object
  averagePrices[n] = ECPG_object$averagePrice
  buyer.phi[n] = mean(ECPG_object$buyerPhi)
  seller.phi[n] = mean(ECPG_object$sellerPhi)
  buyer.premium[n] = mean(ECPG_object$buyerPremium)
  seller.premium[n] = mean(ECPG_object$sellerPremium)
}

Plot the validation results

Create a list to used with the plotting function

consolidated.data <- list()
consolidated.data$meanPLD = spotPrices
consolidated.data$forwardPrice = forwardPrices
consolidated.data$averagePrice = averagePrices
consolidated.data$labels = data.abbrev

Finally the results can be plotted.

data.prettyPlot(consolidated.data, lwd=3, cex=1.1, horiz=TRUE, legCex=1, legendPos="top")

Plot Certainty Equivalent and risk premia for the market participants

Create a matrix with the data and plot a sketch of it. Another prettier plot will be made below with plotly.

ecqMat = rbind(buyer.phi, seller.phi, averagePrices, spotPrices)
matplot(t(ecqMat), type='l')

Load plotly library

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

Create the plotly chart.

data <- data.frame(month=data.abbrev, seller=seller.phi, buyer=buyer.phi, spot=spotPrices, average = averagePrices)

#The default order will be alphabetized unless specified as below:
data$month <- factor(data$month, levels = data[["month"]])

fig <- plot_ly(data, x = ~month, y = ~seller, type = 'scatter', mode = 'lines',
               line = list(color = 'rgba(0,100,80,1)'),
               showlegend = TRUE, name = 'Seller C.E.')

fig <- fig %>% add_trace(y = ~average, type = 'scatter', mode = 'lines',
                         fill = 'tonexty', fillcolor='rgba(0,100,80,0.2)', line = list(color = 'rgba(0,0,0,1)', dash='dash'),
                         showlegend = TRUE, name = 'Equilibrium Price')

fig <- fig %>% add_trace(y = ~spot, type = 'scatter', mode = 'lines',
                         fill = 'tonexty', fillcolor='rgba(0,100,80,0.2)', line = list(color = 'rgba(0,0,100,1)', dash='dot'),
                         showlegend = TRUE, name = 'Forecasted Spot Price')

fig <- fig %>% add_trace(y = ~buyer, type = 'scatter', mode = 'lines',
                         fill = 'tonexty', fillcolor='rgba(100,0,80,0.2)', line = list(color = 'rgba(100,0,80,1)'),
                         showlegend = TRUE, name = 'Buyer C.E.')

fig <- fig %>% layout(paper_bgcolor='rgb(255,255,255)', plot_bgcolor='rgb(229,229,229)',
                      xaxis = list(title = "Months",
                                   gridcolor = 'rgb(255,255,255)',
                                   showgrid = TRUE,
                                   showline = FALSE,
                                   showticklabels = TRUE,
                                   tickcolor = 'rgb(127,127,127)',
                                   ticks = 'outside',
                                   zeroline = FALSE),
                      yaxis = list(title = "Electricity forward price (R$/MWh)",
                                   gridcolor = 'rgb(255,255,255)',
                                   showgrid = TRUE,
                                   showline = FALSE,
                                   showticklabels = TRUE,
                                   tickcolor = 'rgb(127,127,127)',
                                   ticks = 'outside',
                                   zeroline = FALSE),
                      font=list(size=14),
                      legend=list(
                        x=0.7,
                        y=0.9,
                        traceorder='normal'
                        ))

View the plotly figure

fig

See also:

Model Training (link)

Model Sensitivity (link)

LS0tDQp0aXRsZTogIk1vZGVsIFZhbGlkYXRpb24iDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyAgQSBUd28gQWdlbnQgTW9kZWwgb2YgRm9yd2FyZCBFbGVjdHJpY2l0eSBQcmljZXMgaW4gQnJhemlsIHdpdGggR2VuZXJhbGl6ZWQgRXh0ZW5kZWQgQ1ZhUiBQcmVmZXJlbmNlcw0KDQpBdXRob3JzOiBGZWxpcGUgVmFuIGRlIFNhbmRlIEFyYXVqbywgQ3Jpc3RpbmEgUGltZW50YSBkZSBNZWxsbyBTcGluZXRpIEx1eiwgTGVvbmFyZG8gTGltYSBHb21lcywgTHVpeiBFZHVhcmRvIFRlaXhlaXJhIEJyYW5kw6NvDQoNCkFic3RyYWN0OiBEZXNwaXRlIGl0cyBjb250aW5lbnRhbCBzaXplIGFuZCBpbnRlZ3JhdGVkIGVsZWN0cmljYWwgc3lzdGVtLCBCcmF6aWwgZG9lcyBub3QgaGF2ZSBhbiBleGNoYW5nZSBmb3IgdHJhZGluZyBmb3J3YXJkIGFuZCBmdXR1cmVzIGNvbnRyYWN0cyBmb3IgZWxlY3RyaWNpdHkuIFRodXMsIHByaWNlIGluZm9ybWF0aW9uIGZvciBsb25nLXRlcm0gY29udHJhY3RzIGlzIG9mdGVuIG9idGFpbmVkIHRocm91Z2ggbWFya2V0IHJlc2VhcmNoIGFuZCBleHBlcnQgb3BpbmlvbnMuIFRoaXMgYXJ0aWNsZSBwcm9wb3NlcyBhIHNpbXBsZSB5ZXQgZWZmaWNpZW50IGFwcHJvYWNoIHRvIGVzdGltYXRlIHRoZSBmb3J3YXJkIHByaWNlIG9mIGVsZWN0cmljaXR5IGluIHRoZSBCcmF6aWxpYW4gZW5lcmd5IG1hcmtldC4gVGhlIG1vZGVsIGlzIGJhc2VkIG9uIHRoZSBlcXVpbGlicml1bSBiZXR3ZWVuIHR3byByZXByZXNlbnRhdGl2ZSBhZ2VudHMgbmVnb3RpYXRpbmcgYmlsYXRlcmFsIGNvbnRyYWN0cyB3aGVyZSB0aGUgYWdlbnRz4oCZIHJpc2sgYXZlcnNpb24gaXMgZGVyaXZlZCBmcm9tIHRoZSB1dGlsaXR5IGZ1bmN0aW9ucyByZWxhdGVkIHRvIHRoZSBHZW5lcmFsaXplZCBFeHRlbmRlZCBDb25kaXRpb25hbCBWYWx1ZS1hdC1SaXNrIFByZWZlcmVuY2UuIFRoaXMgbW9kZWwgaXMgY29tcHJlaGVuc2l2ZSBhbmQgY2FuIGJlIGFwcGxpZWQgdG8gYWxsIGFnZW50cyBwYXJ0aWNpcGF0aW5nIGluIHRoZSBlbGVjdHJpY2l0eSBmdXR1cmVzIHRyYW5zYWN0aW9uIGluZGVwZW5kZW50IG9mIHdoZXRoZXIgdGhleSBhcmUgZGlyZWN0bHkgaW52b2x2ZWQgaW4gdGhlIHByb2R1Y3Rpb24gY2hhaW4gb3Igc2ltcGx5IGNhcnJ5IHNwZWN1bGF0aXZlIHBvc2l0aW9ucy4gT3VyIHJlc3VsdHMgaW5kaWNhdGUgdGhhdCB0aGUgbW9kZWzigJlzIGZvcmVjYXN0ZWQgcHJpY2VzLCB3aGljaCBhcmUgYmFzZWQgb24gdGhlIHBhcnRpY2lwYW50cycgZXhwZWN0ZWQgYmVoYXZpb3IsIGNhbiBiZSB1c2VkIGFzIGFuIGluZGljYXRvciBmb3IgdGhlIGZvcndhcmQgcHJpY2Ugb2YgZWxlY3RyaWNpdHksIHByb3ZpZGluZyBtb3JlIHRyYW5zcGFyZW5jeSBhbmQgc2VjdXJpdHkgZm9yIHRoZSBwYXJ0aWNpcGFudHMgaW4gdGhpcyBtYXJrZXQuDQoNClRoaXMgd29yayBwcmVzZW50cyB0aGUgY2FsY3VsYXRpb25zIGRvbmUgZm9yIHRoZSBhcnRpY2xlIHJlZmVyZW5jZWQgYWJvdmUuIFRoZSBmb2xsb3dpbmcgY2FsY3VsYXRpb25zIHNob3cgdGhlIHZhbGlkYXRpb24gb2YgdGhlIG9wdGltaXplZCBwYXJhbWV0ZXJzIHdoaWNoIHdlcmUgb2J0YWluZWQgaW4gdGhlIG9wdGltaXphdGlvbiBzdGVwIChbbGlua10oLi9Nb2RlbFRyYWluaW5nTm90ZWJvb2submIuaHRtbCkpLiBBbGwgdGhlIGNvZGUgd2FzIHJ1biBpbiBSU3R1ZGlvIHVzaW5nIHRoZSB2ZXJzaW9uIG9mIHRoZSBzb2Z0d2FyZSBiZWxvdy4NCg0KIyMjIFNvZnR3YXJlIHZlcnNpb24NCg0KYGBge3J9DQpSLnZlcnNpb24NCmBgYA0KDQojIyMgU2V0dGluZyBvZiB0aGUgZW52aXJvbm1lbnQNCg0KYGBge3J9DQojIFNldCBwbG90IGZvbnQNCndpbmRvd3NGb250cyhBID0gd2luZG93c0ZvbnQoIlRpbWVzIE5ldyBSb21hbiIpKSANCmBgYA0KDQojIyMgTG9jYWwgZnVuY3Rpb25zIGRlZmluaXRpb24NCg0KVGhlIGZpcnN0IGZ1bmN0aW9uIGlzIHRoZSBkZXRlcm1pbmF0aW9uIG9mIHRoZSBFQ1BHIHBoaSB3aGljaCBpcyB0aGUgY2VydGFpbnR5IGVxdWl2YWxlbnQgb2YgdGhlIGFnZW50IHdoZW4gaXQgaXMgZGlyZWN0bHkgZXhwb3NlZCB0byB0aGUgc3BvdCBwcmljZXMuDQoNCmBgYHtyfQ0KRUNQRy5waGkgPC0gZnVuY3Rpb24ocGFyYW1ldGVycywgZGF0YXNldCwgYXV4TGlzdCwgYnV5ZXI9VFJVRSkgew0KICBpZiAoYW55KHBhcmFtZXRlcnMgPCAwKSB8fCBhbnkocGFyYW1ldGVycyA+IDEpKSByZXR1cm4oTkEpDQogIHN1bUxhbWJkYSA9IHBhcmFtZXRlcnNbJ0xhbWJkYTEnXSArIHBhcmFtZXRlcnNbJ0xhbWJkYTInXQ0KICBpZiAoc3VtTGFtYmRhID4gMSkgcmV0dXJuKE5BKQ0KICBMYW1iZGEwID0gMSAtIHN1bUxhbWJkYQ0KICBpZiAoTGFtYmRhMCA8IDApIHJldHVybihOQSkNCiAgDQogIGlmIChidXllcikgew0KICAgIG1lYW4ucGxkID0gYXV4TGlzdCRtZWFuUExEDQogICAgVmFSMCA9IGF1eExpc3QkVmFSMA0KICAgIFZhUjEgPSBhdXhMaXN0JFZhUjENCiAgfSBlbHNlIHsNCiAgICBkYXRhc2V0ID0gLWRhdGFzZXQNCiAgICBtZWFuLnBsZCA9IC1hdXhMaXN0JG1lYW5QTEQNCiAgICBWYVIwID0gYXV4TGlzdCRWYVIxDQogICAgVmFSMSA9IGF1eExpc3QkVmFSMA0KICB9DQogIA0KICBWYVJBbHBoYTEgPSBhcHBseShkYXRhc2V0LCAxLCBxdWFudGlsZSwgKDEtcGFyYW1ldGVyc1snQWxwaGExJ10pKQ0KICBWYVJBbHBoYTIgPSBhcHBseShkYXRhc2V0LCAxLCBxdWFudGlsZSwgKDEtcGFyYW1ldGVyc1snQWxwaGEyJ10pKQ0KICANCiAgQ1ZhUjEgPSBDVmFSMiA9IE5BDQogIA0KICBDVmFSMS5kYXRhID0gZGF0YXNldA0KICBDVmFSMS5kYXRhW2RhdGFzZXQgPiBWYVJBbHBoYTFdIDwtIE5BDQogIA0KICBDVmFSMi5kYXRhID0gZGF0YXNldA0KICBDVmFSMi5kYXRhW2RhdGFzZXQgPiBWYVJBbHBoYTJdIDwtIE5BDQogIA0KICBDVmFSMSA9IGFwcGx5KENWYVIxLmRhdGEsIDEsIG1lYW4sIG5hLnJtPVRSVUUpDQogIENWYVIyID0gYXBwbHkoQ1ZhUjIuZGF0YSwgMSwgbWVhbiwgbmEucm09VFJVRSkNCiAgDQogIFN1bTEgPSBwYXJhbWV0ZXJzWydMYW1iZGExJ10qVmFSQWxwaGExICsgcGFyYW1ldGVyc1snTGFtYmRhMiddKlZhUkFscGhhMg0KICBTdW0yID0gcGFyYW1ldGVyc1snTGFtYmRhMSddKihDVmFSMSAtIFZhUkFscGhhMSkgKyBwYXJhbWV0ZXJzWydMYW1iZGEyJ10qKENWYVIyIC0gVmFSQWxwaGEyKQ0KICANCiAgTGltaXQxID0gTGFtYmRhMCpWYVIwICsgU3VtMQ0KICBMaW1pdDIgPSBMYW1iZGEwKlZhUkFscGhhMSArIFN1bTENCiAgTGltaXQzID0gTGFtYmRhMCpWYVJBbHBoYTIgKyBTdW0xDQogIExpbWl0NCA9IExhbWJkYTAqVmFSMSArIFN1bTENCiAgDQogIEVDUEcgPSBMYW1iZGEwKm1lYW4ucGxkICsgcGFyYW1ldGVyc1snTGFtYmRhMSddKkNWYVIxICsgcGFyYW1ldGVyc1snTGFtYmRhMiddKkNWYVIyDQogIA0KICBuSXRlciA9IG5yb3coZGF0YXNldCkNCiAgTGFtYmRhMSA9IHJlcChwYXJhbWV0ZXJzWydMYW1iZGExJ10sIG5JdGVyKQ0KICBMYW1iZGEyID0gcmVwKHBhcmFtZXRlcnNbJ0xhbWJkYTInXSwgbkl0ZXIpDQogIA0KICBpZiAoYW55KEVDUEc+PUxpbWl0MikpIExhbWJkYTFbRUNQRz49TGltaXQyXSA9IDANCiAgaWYgKGFueShFQ1BHPj1MaW1pdDMpKSBMYW1iZGEyW0VDUEc+PUxpbWl0M10gPSAwDQogIA0KICBRID0gTGFtYmRhMCArIExhbWJkYTEvKDEtcGFyYW1ldGVyc1snQWxwaGExJ10pICsgTGFtYmRhMi8oMS1wYXJhbWV0ZXJzWydBbHBoYTInXSkNCiAgDQogIEEgPSBTdW0yICsgTGFtYmRhMCptZWFuLnBsZCArIA0KICAgIExhbWJkYTEqVmFSQWxwaGExLygxLXBhcmFtZXRlcnNbJ0FscGhhMSddKSArDQogICAgTGFtYmRhMipWYVJBbHBoYTIvKDEtcGFyYW1ldGVyc1snQWxwaGEyJ10pDQogIA0KICBFcSA9IEEvUQ0KICANCiAgaWYgKGFueShpcy5uYShFcSkpKSByZXR1cm4oTkEpDQogIHJldHVybihFcSkNCn0NCg0KYGBgDQoNCldlIGRlZmluZSBhbm90aGVyIGZ1bmN0aW9uIHRoYXQgZXhwbGljaXRseSBjYWxjdWxhdGVzIHRoZSByaXNrIHByZW1pdW0gZm9yIGJvdGggYWdlbnRzLiBUaGlzIGZ1bmN0aW9uIGlzIHJlZHVuZGFudCB3aXRoIHRoZSBhYm92ZSBvbmUgYW5kIHRoZSB3aG9sZSBjb2RlIGNvdWxkIGJlIHNpbXBsaWZpZWQsIGJ1dCBpdHMgb2JqZWN0aXZlIGlzIHRvIHByb3ZpZGUgYSBkb3VibGUgdmVyaWZpY2F0aW9uIHN0ZXAuIFRoZSByaXNrIHByZW1pdW0gY2FuIGFsc28gYmUgY2FsY3VsYXRlZCBieSB1c2luZyB0aGUgc2ltcGxlciBmb3JtdWxhcyBwcm92aWRlZCBpbiB0aGUgYXJ0aWNsZS4NCg0KYGBge3J9DQpFQ1BHLnByZW1pdW0gPC0gZnVuY3Rpb24ocGFyYW1ldGVycywgZGF0YXNldCwgYXV4TGlzdCwgYnV5ZXI9VFJVRSkgew0KICBpZiAoYW55KHBhcmFtZXRlcnMgPCAwKSB8fCBhbnkocGFyYW1ldGVycyA+IDEpKSByZXR1cm4oTkEpDQogIHN1bUxhbWJkYSA9IHBhcmFtZXRlcnNbJ0xhbWJkYTEnXSArIHBhcmFtZXRlcnNbJ0xhbWJkYTInXQ0KICBpZiAoc3VtTGFtYmRhID4gMSkgcmV0dXJuKE5BKQ0KICBMYW1iZGEwID0gMSAtIHN1bUxhbWJkYQ0KICBpZiAoTGFtYmRhMCA8IDApIHJldHVybihOQSkNCiAgDQogIGlmIChidXllcikgew0KICAgIG1lYW4ucGxkID0gYXV4TGlzdCRtZWFuUExEDQogICAgVmFSMCA9IGF1eExpc3QkVmFSMA0KICAgIFZhUjEgPSBhdXhMaXN0JFZhUjENCiAgfSBlbHNlIHsNCiAgICBkYXRhc2V0ID0gLWRhdGFzZXQNCiAgICBtZWFuLnBsZCA9IC1hdXhMaXN0JG1lYW5QTEQNCiAgICBWYVIwID0gYXV4TGlzdCRWYVIxDQogICAgVmFSMSA9IGF1eExpc3QkVmFSMA0KICB9DQogIA0KICBWYVJBbHBoYTEgPSBhcHBseShkYXRhc2V0LCAxLCBxdWFudGlsZSwgKDEtcGFyYW1ldGVyc1snQWxwaGExJ10pKQ0KICBWYVJBbHBoYTIgPSBhcHBseShkYXRhc2V0LCAxLCBxdWFudGlsZSwgKDEtcGFyYW1ldGVyc1snQWxwaGEyJ10pKQ0KICANCiAgQ1ZhUjEgPSBDVmFSMiA9IE5BDQogIA0KICBDVmFSMS5kYXRhID0gZGF0YXNldA0KICBDVmFSMS5kYXRhW2RhdGFzZXQgPiBWYVJBbHBoYTFdIDwtIE5BDQogIA0KICBDVmFSMi5kYXRhID0gZGF0YXNldA0KICBDVmFSMi5kYXRhW2RhdGFzZXQgPiBWYVJBbHBoYTJdIDwtIE5BDQogIA0KICBDVmFSMSA9IGFwcGx5KENWYVIxLmRhdGEsIDEsIG1lYW4sIG5hLnJtPVRSVUUpDQogIENWYVIyID0gYXBwbHkoQ1ZhUjIuZGF0YSwgMSwgbWVhbiwgbmEucm09VFJVRSkNCiAgDQogIFN1bTEgPSBwYXJhbWV0ZXJzWydMYW1iZGExJ10qVmFSQWxwaGExICsgcGFyYW1ldGVyc1snTGFtYmRhMiddKlZhUkFscGhhMg0KICBTdW0yID0gcGFyYW1ldGVyc1snTGFtYmRhMSddKihDVmFSMSAtIFZhUkFscGhhMSkgKyBwYXJhbWV0ZXJzWydMYW1iZGEyJ10qKENWYVIyIC0gVmFSQWxwaGEyKQ0KICANCiAgTGltaXQxID0gTGFtYmRhMCpWYVIwICsgU3VtMQ0KICBMaW1pdDIgPSBMYW1iZGEwKlZhUkFscGhhMSArIFN1bTENCiAgTGltaXQzID0gTGFtYmRhMCpWYVJBbHBoYTIgKyBTdW0xDQogIExpbWl0NCA9IExhbWJkYTAqVmFSMSArIFN1bTENCiAgDQogIEVDUEcgPSBMYW1iZGEwKm1lYW4ucGxkICsgcGFyYW1ldGVyc1snTGFtYmRhMSddKkNWYVIxICsgcGFyYW1ldGVyc1snTGFtYmRhMiddKkNWYVIyDQogIA0KICBuSXRlciA9IG5yb3coZGF0YXNldCkNCiAgTGFtYmRhMSA9IHJlcChwYXJhbWV0ZXJzWydMYW1iZGExJ10sIG5JdGVyKQ0KICBMYW1iZGEyID0gcmVwKHBhcmFtZXRlcnNbJ0xhbWJkYTInXSwgbkl0ZXIpDQogIA0KICBpZiAoYW55KEVDUEc+PUxpbWl0MikpIExhbWJkYTFbRUNQRz49TGltaXQyXSA9IDANCiAgaWYgKGFueShFQ1BHPj1MaW1pdDMpKSBMYW1iZGEyW0VDUEc+PUxpbWl0M10gPSAwDQogIA0KICBRID0gTGFtYmRhMCArIExhbWJkYTEvKDEtcGFyYW1ldGVyc1snQWxwaGExJ10pICsgTGFtYmRhMi8oMS1wYXJhbWV0ZXJzWydBbHBoYTInXSkNCiAgDQogIEEgPSAgLSBTdW0yICsgTGFtYmRhMSoobWVhbi5wbGQgLSBWYVJBbHBoYTEpLygxLXBhcmFtZXRlcnNbJ0FscGhhMSddKSArDQogICAgTGFtYmRhMioobWVhbi5wbGQgLSBWYVJBbHBoYTIpLygxLXBhcmFtZXRlcnNbJ0FscGhhMiddKQ0KICANCiAgRXEgPSBBL1ENCiAgDQogIGlmIChhbnkoaXMubmEoRXEpKSkgcmV0dXJuKE5BKQ0KICByZXR1cm4oRXEpDQp9DQpgYGANCg0KDQpXZSB3aWxsIHVzZSBhIGxpc3Qgd2l0aCBhbGwgdGhlIHBhcmFtZXRlcnMgYW5kIGRhdGEgd2hpY2ggd2lsbCBiZSBjYWxsZWQgRUNQR19PYmplY3QsIGluIG9yZGVyIHRvIGZhY2lsaXRhdGUgdGhlIG5leHQgZnVuY3Rpb24gY2FsbHMuIFRoZSBuZXh0IGZ1bmN0aW9uIHdpbGwgY3JlYXRlIHRoZSBsaXN0Lg0KDQpgYGB7cn0NCmNyZWF0ZUVDUEdPYmplY3QgPC0gZnVuY3Rpb24oZGF0YXNldCwgZGlzY291bnRWZWN0b3IsIGZvcndhcmRQcmljZSwgYnV5ZXJQYXJhbSwgc2VsbGVyUGFyYW0pIHsNCiAgVmFSMCA9IGFwcGx5KGRhdGFzZXQsIDEsIHF1YW50aWxlLCAxKQ0KICBWYVIxID0gYXBwbHkoZGF0YXNldCwgMSwgcXVhbnRpbGUsIDApDQogIG1lYW5QTEQgPSBhcHBseShkYXRhc2V0LCAxLCBtZWFuKQ0KICANCiAgRUNQR09iamVjdCA9IGxpc3QoDQogICAgZGF0YXNldD1kYXRhc2V0LA0KICAgIGRpc2NvdW50VmVjdG9yPWRpc2NvdW50VmVjdG9yLA0KICAgIGZvcndhcmRQcmljZT1mb3J3YXJkUHJpY2UsDQogICAgYnV5ZXJQYXJhbT1idXllclBhcmFtLA0KICAgIHNlbGxlclBhcmFtPXNlbGxlclBhcmFtLA0KICAgIFZhUjA9VmFSMCwNCiAgICBWYVIxPVZhUjEsDQogICAgbWVhblBMRD1tZWFuUExEDQogICkNCiAgcmV0dXJuKEVDUEdPYmplY3QpDQp9DQpgYGANCg0KVGhlbiB3ZSB3aWxsIGRlZmluZSBhIGZ1bmN0aW9uIHRvIG9idGFpbiB0aGUgZXF1aWxpYnJpdW0gcHJpY2UgdXNpbmcgdGhlIGNhbGN1bGF0ZWQgcGhpIGZvciBlYWNoIGFnZW50LiBUaGUgZGlzdGluY3Rpb24gYmV0d2VlbiB0aGUgYWdlbnRzIG11c3QgYmUgbWFkZSBiZWNhdXNlIHRoZSBzaWduYWwgb2YgdGhlIHByaWNlIGRhdGEgY2hhbmdlcyBiZXR3ZWVuIHRoZW0sIGJlY2F1c2Ugb2YgdGhlIENvbnRyYWN0IGZvciBEaWZmZXJlbmNlcyByZXZlbnVlIGVxdWF0aW9ucy4gDQoNCmBgYHtyfQ0KRUNQRy5lcXVpbGlicml1bSA8LSBmdW5jdGlvbiAoRUNQR29iamVjdCkgew0KICBhdXhMaXN0ID0gbGlzdCgNCiAgICBWYVIwID0gRUNQR29iamVjdCRWYVIwLA0KICAgIFZhUjEgPSBFQ1BHb2JqZWN0JFZhUjEsDQogICAgbWVhblBMRCA9IEVDUEdvYmplY3QkbWVhblBMRA0KICApDQogIA0KICBidXllci5waGkgPSBFQ1BHLnBoaShFQ1BHb2JqZWN0JGJ1eWVyUGFyYW0sIEVDUEdvYmplY3QkZGF0YXNldCwgYXV4TGlzdCkNCiAgc2VsbGVyLnBoaSA9IC1FQ1BHLnBoaShFQ1BHb2JqZWN0JHNlbGxlclBhcmFtLCBFQ1BHb2JqZWN0JGRhdGFzZXQsIGF1eExpc3QsIGJ1eWVyPUZBTFNFKQ0KICANCiAgYnV5ZXIucHJlbWl1bSA9IEVDUEcucHJlbWl1bShFQ1BHb2JqZWN0JGJ1eWVyUGFyYW0sIEVDUEdvYmplY3QkZGF0YXNldCwgYXV4TGlzdCkNCiAgc2VsbGVyLnByZW1pdW0gPSAtRUNQRy5wcmVtaXVtKEVDUEdvYmplY3Qkc2VsbGVyUGFyYW0sIEVDUEdvYmplY3QkZGF0YXNldCwgYXV4TGlzdCwgYnV5ZXI9RkFMU0UpDQogIA0KICBlcXVpbGlicml1bS5wcmljZXMgPSAoYnV5ZXIucGhpICsgc2VsbGVyLnBoaSkvMg0KICANCiAgYWRqdXN0ZWQucHJpY2VzID0gZXF1aWxpYnJpdW0ucHJpY2VzL0VDUEdvYmplY3QkZGlzY291bnQNCiAgDQogIGF2ZXJhZ2VQcmljZSA9IG1lYW4oYWRqdXN0ZWQucHJpY2VzKQ0KICANCiAgRUNQR29iamVjdCRidXllclBoaSA9IGJ1eWVyLnBoaQ0KICBFQ1BHb2JqZWN0JHNlbGxlclBoaSA9IHNlbGxlci5waGkNCiAgRUNQR29iamVjdCRidXllclByZW1pdW0gPSBidXllci5wcmVtaXVtDQogIEVDUEdvYmplY3Qkc2VsbGVyUHJlbWl1bSA9IHNlbGxlci5wcmVtaXVtDQogIEVDUEdvYmplY3QkZXF1aWxpYnJpdW1QcmljZXMgPSBlcXVpbGlicml1bS5wcmljZXMNCiAgRUNQR29iamVjdCRhZGp1c3RlZFByaWNlcz1hZGp1c3RlZC5wcmljZXMNCiAgRUNQR29iamVjdCRhdmVyYWdlUHJpY2U9YXZlcmFnZVByaWNlDQogIA0KICByZXR1cm4oRUNQR29iamVjdCkNCn0NCmBgYA0KDQpOZXh0IHdlIGRlZmluZSB0d28gZnVuY3Rpb25zIGZvciBkZWZpbmluZyB0aGUgZGlzY291bnQgdmVjdG9yLCBiYXNlZCBvbiB0aGUgYW5udWFsIHJpc2stZnJlZSByYXRlLg0KDQpgYGB7cn0NCmdldENvbXBsZXRlRGlzY291bnRWZWN0b3IgPC0gZnVuY3Rpb24oaW5kZXgsIG1vZGlmaWVyLCBhbnVhbFJhdGUpIHsNCiAgeWVhckFkaiA9ICgxMSttb2RpZmllcioxMikNCiAgbW9udGhseVJhdGUgPSAoMSthbnVhbFJhdGUpXigxLzEyKS0xDQogIGNvbXBsZXRlRGlzY291bnRWZWN0b3IgPC0gcmVwKE5BLCB5ZWFyQWRqKQ0KICBmb3IgKGkgaW4gMTp5ZWFyQWRqKXsNCiAgICBjb21wbGV0ZURpc2NvdW50VmVjdG9yW2ldIDwtICgxK21vbnRobHlSYXRlKV4oaS0xKQ0KICB9DQogIA0KICByZXR1cm4oY29tcGxldGVEaXNjb3VudFZlY3RvcikNCn0NCg0KZ2V0RGlzY291bnRCb3VuZHMgPC0gZnVuY3Rpb24oaW5kZXgsIG1vZGlmaWVyLCBjb21wbGV0ZURpc2NvdW50VmVjdG9yKSB7DQogIGxvd2VyLmxpbWl0ID0gMSsxMiptb2RpZmllci1pbmRleA0KICB1cHBlci5saW1pdCA9IDEyKzEyKm1vZGlmaWVyLWluZGV4DQogIA0KICByZXR1cm4oY29tcGxldGVEaXNjb3VudFZlY3Rvcltsb3dlci5saW1pdDp1cHBlci5saW1pdF0pDQp9DQpgYGANCg0KDQpBbmQgZmluYWxseSBhIHBsb3R0aW5nIGZ1bmN0aW9uIGZvciB0aGUgRUNQR19PYmplY3QuDQoNCmBgYHtyfQ0KZGF0YS5wcmV0dHlQbG90IDwtIGZ1bmN0aW9uKERhdGFPYmplY3QsIHlsaW09YygwLDM1MCksIGx3ZCA9IDIsIGxlZ2VuZFBvcz0iYm90dG9tcmlnaHQiLCBpbnNldD0wLjA1LCBjZXg9MSwgbGVnQ2V4PTAuNywgaG9yaXo9RkFMU0UpIHsNCiAgcGxvdC5tYXRyaXggPSBjYmluZChEYXRhT2JqZWN0JG1lYW5QTEQsIERhdGFPYmplY3QkZm9yd2FyZFByaWNlLCBEYXRhT2JqZWN0JGF2ZXJhZ2VQcmljZSkNCiAgY29sbmFtZXMocGxvdC5tYXRyaXgpID0gYygiTkVXQVZFIFNwb3QiLCAiRENpZGUgRm9yd2FyZCIsICJNb2RlbCBGb3J3YXJkIikNCiAgcm93bmFtZXMocGxvdC5tYXRyaXgpID0gRGF0YU9iamVjdCRsYWJlbHMNCiAgDQogIE5zZXJpZXMgPSBuY29sKHBsb3QubWF0cml4KQ0KICBjb2xvci5wYWxsZXR0ZSA9IGMoIiM1NkI0RTkiLCAiI0YwRTQ0MiIsICIjRTY5RjAwIikgIyBTZWxlY3RlZCBmb3IgZWFzeSB2aXN1YWxpemF0aW9uIGZvciBjb2xvcmJsaW5kDQogIGxpbmUudHlwZXMgPSBjKDIsIDEsIDEpDQogIA0KICBOcm93cyA9IDE6bnJvdyhwbG90Lm1hdHJpeCkNCiAgbGFiZWxzPXJvdy5uYW1lcyhwbG90Lm1hdHJpeCkNCiAgDQogIExhc3RJblNhbXBsZSA9IHdoaWNoKGRhdGEuaW5kZXhbLDNdPT0iT1VUU0FNUExFIilbMV0gLSAxDQogIA0KICBwYXIoZmFtaWx5PSJzZXJpZiIsIGNleD1jZXgpDQogIG1hdHBsb3QoTnJvd3MsIHBsb3QubWF0cml4LCB0eXBlPSdsJywgeGxhYj0iIiwgeWxhYj0nRWxlY3RyaWNpdHkgZm9yd2FyZCBwcmljZSAoUiQvTVdoKScsIHlsaW09eWxpbSwgbHR5PWxpbmUudHlwZXMsIGx3ZD1sd2QsIGNvbD1jb2xvci5wYWxsZXR0ZSwgYXhlcz1GKQ0KICBheGlzKDIpDQogIGF4aXMoc2lkZT0xLGF0PU5yb3dzLGxhYmVscz1sYWJlbHMpDQogIGdyaWQgKE5VTEwsTlVMTCwgbHR5ID0gNiwgY29sID0gImdyZXkiLCBsd2QgPSAxKQ0KICBhYmxpbmUodj1MYXN0SW5TYW1wbGUgKyAwLjUsIGx0eSA9IDIpDQogIHRleHQoeD1MYXN0SW5TYW1wbGUvMiArIDAuNSwgeT0yNSwgIkluIHNhbXBsZSIpDQogIHRleHQoeD0obnJvdyhwbG90Lm1hdHJpeCkgLSBMYXN0SW5TYW1wbGUpLzIgKyBMYXN0SW5TYW1wbGUgKyAwLjUsIHk9MjUsICJPdXQtb2Ytc2FtcGxlIikNCiAgbGVnZW5kKHg9bGVnZW5kUG9zLCBpbnNldD1pbnNldCwgbGVnZW5kPWNvbG5hbWVzKHBsb3QubWF0cml4KSxjb2w9Y29sb3IucGFsbGV0dGUsIGx0eT1saW5lLnR5cGVzLCBjZXg9bGVnQ2V4LCBob3Jpej1ob3JpeiwgbHdkPWx3ZCkNCn0NCmBgYA0KDQojIyMgTG9jYWwgdmFyaWFibGVzIGRlZmluaXRpb24NCg0KU2V0IHRoZSBjYWxpYnJhdGVkIHBhcmFtZXRlcnMsIG9idGFpbmVkIGZyb20gdGhlIHRyYWluaW5nIHN0YWdlIGFuZCB0aGUgYW5udWFsIHJpc2stZnJlZSByYXRlLiBBIHNlbnNpdGl2aXR5IGFuYWx5c2lzIGZvciB0aGlzIHJhdGUgaXMgcHJvdmlkZWQgaW4gdGhlIHNlbnNpdGl2aXR5IGFuYWx5c2lzIHNlY3RvciAoW2xpbmtdKC4vU2Vuc2l0aXZpdHlBbmFseXNpc05vdGVib29rLm5iLmh0bWwpKS4NCg0KYGBge3J9DQojIyBPYnRhaW4gb3B0aW1pemVkIHBhcmFtZXRlcnMgZnJvbSB0cmFpbmluZyANCmJ1eWVyLnBhcmFtID0gYyhMYW1iZGExID0gMC4yNTE1OTkyNywgTGFtYmRhMiA9IDAuMDkzOTA5MjQsIEFscGhhMSA9IDAuMzIzMzQzMzAsIEFscGhhMiA9IDAuNjkyOTMxMTUpDQpzZWxsZXIucGFyYW0gPSBjKExhbWJkYTEgPSAwLjE3MTI3MjEsIExhbWJkYTIgPSAwLjc0NjY4NjUsIEFscGhhMSA9IDAuNzY0MTkzNCwgQWxwaGEyID0gMC45OTEyNzU1KQ0KDQphbnVhbFJhdGUgPSAwLjA1DQpgYGANCg0KIyMjIExvYWRpbmcgRGF0YQ0KDQpEZWZpbmUgdGhlIGRhdGEgcG9pbnRzIHRvIGJlIHVzZWQgZm9yIHRoZSBtb2RlbCB2YWxpZGF0aW9uLg0KDQpgYGB7cn0NCmRhdGEuaW5kZXggPSByYmluZCgNCiAgYyg3LCAyMDE5LCAiaW5zYW1wbGUiKSwNCiAgYyg4LCAyMDE5LCAiaW5zYW1wbGUiKSwNCiAgYyg5LCAyMDE5LCAiaW5zYW1wbGUiKSwNCiAgYygxMCwgMjAxOSwgImluc2FtcGxlIiksDQogIGMoMTEsIDIwMTksICJpbnNhbXBsZSIpLA0KICBjKDEyLCAyMDE5LCAiaW5zYW1wbGUiKSwNCiAgYygxLCAyMDIwLCAiaW5zYW1wbGUiKSwNCiAgYygyLCAyMDIwLCAiaW5zYW1wbGUiKSwNCiAgYygzLCAyMDIwLCAiT1VUU0FNUExFIiksDQogIGMoNCwgMjAyMCwgIk9VVFNBTVBMRSIpLA0KICBjKDUsIDIwMjAsICJPVVRTQU1QTEUiKSwNCiAgYyg2LCAyMDIwLCAiT1VUU0FNUExFIiksDQogIGMoNywgMjAyMCwgIk9VVFNBTVBMRSIpLA0KICBjKDgsIDIwMjAsICJPVVRTQU1QTEUiKSwNCiAgYyg5LCAyMDIwLCAiT1VUU0FNUExFIiksDQogIGMoMTAsIDIwMjAsICJPVVRTQU1QTEUiKQ0KKQ0KDQojIERlZmluZSB0aGUgbnVtYmVyIG9mIHllYXJzIGFoZWFkIHRoYXQgdGhlIGZvcndhcmQgcHJpY2UgaXMgYXBwbGllZCB0by4NCnNlcmllc0EgPSAxDQpgYGANCg0KTG9hZCBhbmQgc2VsZWN0IHRoZSBmb3J3YXJkIHByaWNlIGZvciBlbGVjdHJpY2l0eSBvYnRhaW5lZCBmcm9tIERDaWRlIEVuZXJnaWEuDQoNCmBgYHtyfQ0KZm9yd2FyZFByaWNlLmRhdGEgPSByZWFkLmNzdihmaWxlPScuL0RhdGEvRm9yd2FyZFByaWNlcy5jc3YnKQ0KY29sbmFtZXMoZm9yd2FyZFByaWNlLmRhdGEpID0gYygiUGVyaW9kIiwgcGFzdGUoYyhyZXAoMjAxOSw2KSwgcmVwKDIwMjAsIDEwKSksIGMoNzoxMiwxOjEwKSwgc2VwPSJfIikpDQoNCnNlbGVjdGVkRGF0YSA9IHBhc3RlKGRhdGEuaW5kZXhbLDJdLCBkYXRhLmluZGV4WywxXSwgc2VwPSJfIikNCmZvcndhcmRQcmljZXMgPSBhcy5udW1lcmljKGZvcndhcmRQcmljZS5kYXRhW3Nlcmllc0Esc2VsZWN0ZWREYXRhXSkNCmBgYA0KDQpDYWxjdWxhdGVkIHRoZSByZWxldmFudCBkaXNjb3VudCB2ZWN0b3IgZm9yIHRoZSB3aG9sZSBwZXJpb2QuDQoNCmBgYHtyfQ0KY29tcGxldGVEaXNjb3VudFZlY3RvciA9IGdldENvbXBsZXRlRGlzY291bnRWZWN0b3IoYXMubnVtZXJpYyhkYXRhLmluZGV4WywxXSksIHNlcmllc0EsIGFudWFsUmF0ZSkNCmBgYA0KDQojIyMgU3RhcnQgb2YgVmFsaWRhdGlvbg0KDQpDcmVhdGUgYWNjZXNzb3J5IHZhcmlhYmxlcw0KDQpgYGB7cn0NCmRhdGEuYWJicmV2ID0gcGFzdGUobW9udGguYWJiW2FzLm51bWVyaWMoZGF0YS5pbmRleFssMV0pXSwgZGF0YS5pbmRleFssMl0sIHNlcD0iXyIpDQoNCmFsbC5kYXRhID0gbGlzdCgpDQphdmVyYWdlUHJpY2VzID0gYygpDQpzcG90UHJpY2VzID0gYygpDQpidXllci5waGkgPSBjKCkNCnNlbGxlci5waGkgPSBjKCkNCmJ1eWVyLnByZW1pdW0gPSBjKCkNCnNlbGxlci5wcmVtaXVtID0gYygpDQpgYGANCg0KUnVuIHRoZSB2YWxpZGF0aW9uIGZvciBlYWNoIGRhdGEgcG9pbnQgaW4gYSBsb29wLg0KDQpgYGB7cn0NCmZvciAobiBpbiAxOm5yb3coZGF0YS5pbmRleCkpIHsNCiAgDQogICMjIFNldCBWYXJpYWJsZXMNCiAgZGlzY291bnRWZWN0b3IgPSBnZXREaXNjb3VudEJvdW5kcyhhcy5udW1lcmljKGRhdGEuaW5kZXhbbiwxXSksIHNlcmllc0EsIGNvbXBsZXRlRGlzY291bnRWZWN0b3IpDQogIA0KICBmb3J3YXJkUHJpY2UgPSBmb3J3YXJkUHJpY2VzW25dDQogIA0KICAjIyBMb2FkIERhdGENCiAgb3JpZ2luYWwuZGF0YSA9IHJlYWQuY3N2KGZpbGU9cGFzdGUoJy4vRGF0YS9EYXRhJywgZGF0YS5hYmJyZXZbbl0sIHBhc3RlMCgnQScsIHNlcmllc0EsICcuY3N2JyksIHNlcD0iXyIpLCBjb2wubmFtZXM9cGFzdGUobW9udGguYWJiLCBhcy5udW1lcmljKGRhdGEuaW5kZXhbbiwyXSkrc2VyaWVzQSwgc2VwPSItIikpDQogIA0KICBzcG90UHJpY2VzW25dID0gbWVhbihjb2xNZWFucyhvcmlnaW5hbC5kYXRhKSkNCiAgDQogIEVDUEdfb2JqZWN0ID0gY3JlYXRlRUNQR09iamVjdChkYXRhc2V0ID0gdChvcmlnaW5hbC5kYXRhKSwgYnV5ZXJQYXJhbSA9IGJ1eWVyLnBhcmFtLCBzZWxsZXJQYXJhbSA9IHNlbGxlci5wYXJhbSwgZGlzY291bnRWZWN0b3IgPSBkaXNjb3VudFZlY3RvciwgZm9yd2FyZFByaWNlID0gZm9yd2FyZFByaWNlKQ0KICANCiAgIyMgQ2FsY3VsYXRlIHJlc3VsdHMgZnJvbSBvcHRpbWl6ZWQgcGFyYW1ldGVycw0KICBFQ1BHX29iamVjdCA9IEVDUEcuZXF1aWxpYnJpdW0oRUNQR19vYmplY3QpDQogIA0KICBhbGwuZGF0YVtbZGF0YS5hYmJyZXZbbl1dXSA8LSBFQ1BHX29iamVjdA0KICBhdmVyYWdlUHJpY2VzW25dID0gRUNQR19vYmplY3QkYXZlcmFnZVByaWNlDQogIGJ1eWVyLnBoaVtuXSA9IG1lYW4oRUNQR19vYmplY3QkYnV5ZXJQaGkpDQogIHNlbGxlci5waGlbbl0gPSBtZWFuKEVDUEdfb2JqZWN0JHNlbGxlclBoaSkNCiAgYnV5ZXIucHJlbWl1bVtuXSA9IG1lYW4oRUNQR19vYmplY3QkYnV5ZXJQcmVtaXVtKQ0KICBzZWxsZXIucHJlbWl1bVtuXSA9IG1lYW4oRUNQR19vYmplY3Qkc2VsbGVyUHJlbWl1bSkNCn0NCmBgYA0KDQojIyMgUGxvdCB0aGUgdmFsaWRhdGlvbiByZXN1bHRzDQoNCkNyZWF0ZSBhIGxpc3QgdG8gdXNlZCB3aXRoIHRoZSBwbG90dGluZyBmdW5jdGlvbg0KDQpgYGB7cn0NCmNvbnNvbGlkYXRlZC5kYXRhIDwtIGxpc3QoKQ0KY29uc29saWRhdGVkLmRhdGEkbWVhblBMRCA9IHNwb3RQcmljZXMNCmNvbnNvbGlkYXRlZC5kYXRhJGZvcndhcmRQcmljZSA9IGZvcndhcmRQcmljZXMNCmNvbnNvbGlkYXRlZC5kYXRhJGF2ZXJhZ2VQcmljZSA9IGF2ZXJhZ2VQcmljZXMNCmNvbnNvbGlkYXRlZC5kYXRhJGxhYmVscyA9IGRhdGEuYWJicmV2DQpgYGANCg0KRmluYWxseSB0aGUgcmVzdWx0cyBjYW4gYmUgcGxvdHRlZC4NCg0KYGBge3J9DQpkYXRhLnByZXR0eVBsb3QoY29uc29saWRhdGVkLmRhdGEsIGx3ZD0zLCBjZXg9MS4xLCBob3Jpej1UUlVFLCBsZWdDZXg9MSwgbGVnZW5kUG9zPSJ0b3AiKQ0KYGBgDQoNCg0KIyMjIFBsb3QgQ2VydGFpbnR5IEVxdWl2YWxlbnQgYW5kIHJpc2sgcHJlbWlhIGZvciB0aGUgbWFya2V0IHBhcnRpY2lwYW50cw0KDQpDcmVhdGUgYSBtYXRyaXggd2l0aCB0aGUgZGF0YSBhbmQgcGxvdCBhIHNrZXRjaCBvZiBpdC4gQW5vdGhlciBwcmV0dGllciBwbG90IHdpbGwgYmUgbWFkZSBiZWxvdyB3aXRoIHBsb3RseS4NCg0KYGBge3J9DQplY3FNYXQgPSByYmluZChidXllci5waGksIHNlbGxlci5waGksIGF2ZXJhZ2VQcmljZXMsIHNwb3RQcmljZXMpDQptYXRwbG90KHQoZWNxTWF0KSwgdHlwZT0nbCcpDQpgYGANCg0KTG9hZCBwbG90bHkgbGlicmFyeQ0KYGBge3J9DQpyZXF1aXJlKHBsb3RseSkNCmBgYA0KDQpDcmVhdGUgdGhlIHBsb3RseSBjaGFydC4NCmBgYHtyfQ0KZGF0YSA8LSBkYXRhLmZyYW1lKG1vbnRoPWRhdGEuYWJicmV2LCBzZWxsZXI9c2VsbGVyLnBoaSwgYnV5ZXI9YnV5ZXIucGhpLCBzcG90PXNwb3RQcmljZXMsIGF2ZXJhZ2UgPSBhdmVyYWdlUHJpY2VzKQ0KDQojVGhlIGRlZmF1bHQgb3JkZXIgd2lsbCBiZSBhbHBoYWJldGl6ZWQgdW5sZXNzIHNwZWNpZmllZCBhcyBiZWxvdzoNCmRhdGEkbW9udGggPC0gZmFjdG9yKGRhdGEkbW9udGgsIGxldmVscyA9IGRhdGFbWyJtb250aCJdXSkNCg0KZmlnIDwtIHBsb3RfbHkoZGF0YSwgeCA9IH5tb250aCwgeSA9IH5zZWxsZXIsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLA0KICAgICAgICAgICAgICAgbGluZSA9IGxpc3QoY29sb3IgPSAncmdiYSgwLDEwMCw4MCwxKScpLA0KICAgICAgICAgICAgICAgc2hvd2xlZ2VuZCA9IFRSVUUsIG5hbWUgPSAnU2VsbGVyIEMuRS4nKQ0KDQpmaWcgPC0gZmlnICU+JSBhZGRfdHJhY2UoeSA9IH5hdmVyYWdlLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gJ3RvbmV4dHknLCBmaWxsY29sb3I9J3JnYmEoMCwxMDAsODAsMC4yKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYmEoMCwwLDAsMSknLCBkYXNoPSdkYXNoJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgc2hvd2xlZ2VuZCA9IFRSVUUsIG5hbWUgPSAnRXF1aWxpYnJpdW0gUHJpY2UnKQ0KDQpmaWcgPC0gZmlnICU+JSBhZGRfdHJhY2UoeSA9IH5zcG90LCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gJ3RvbmV4dHknLCBmaWxsY29sb3I9J3JnYmEoMCwxMDAsODAsMC4yKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYmEoMCwwLDEwMCwxKScsIGRhc2g9J2RvdCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBUUlVFLCBuYW1lID0gJ0ZvcmVjYXN0ZWQgU3BvdCBQcmljZScpDQoNCmZpZyA8LSBmaWcgJT4lIGFkZF90cmFjZSh5ID0gfmJ1eWVyLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gJ3RvbmV4dHknLCBmaWxsY29sb3I9J3JnYmEoMTAwLDAsODAsMC4yKScsIGxpbmUgPSBsaXN0KGNvbG9yID0gJ3JnYmEoMTAwLDAsODAsMSknKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBzaG93bGVnZW5kID0gVFJVRSwgbmFtZSA9ICdCdXllciBDLkUuJykNCg0KZmlnIDwtIGZpZyAlPiUgbGF5b3V0KHBhcGVyX2JnY29sb3I9J3JnYigyNTUsMjU1LDI1NSknLCBwbG90X2JnY29sb3I9J3JnYigyMjksMjI5LDIyOSknLA0KICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJNb250aHMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmlkY29sb3IgPSAncmdiKDI1NSwyNTUsMjU1KScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dncmlkID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd2xpbmUgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd3RpY2tsYWJlbHMgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aWNrY29sb3IgPSAncmdiKDEyNywxMjcsMTI3KScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpY2tzID0gJ291dHNpZGUnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB6ZXJvbGluZSA9IEZBTFNFKSwNCiAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiRWxlY3RyaWNpdHkgZm9yd2FyZCBwcmljZSAoUiQvTVdoKSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyaWRjb2xvciA9ICdyZ2IoMjU1LDI1NSwyNTUpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd2dyaWQgPSBUUlVFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93bGluZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93dGlja2xhYmVscyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpY2tjb2xvciA9ICdyZ2IoMTI3LDEyNywxMjcpJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGlja3MgPSAnb3V0c2lkZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplcm9saW5lID0gRkFMU0UpLA0KICAgICAgICAgICAgICAgICAgICAgIGZvbnQ9bGlzdChzaXplPTE0KSwNCiAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQ9bGlzdCgNCiAgICAgICAgICAgICAgICAgICAgICAgIHg9MC43LA0KICAgICAgICAgICAgICAgICAgICAgICAgeT0wLjksDQogICAgICAgICAgICAgICAgICAgICAgICB0cmFjZW9yZGVyPSdub3JtYWwnDQogICAgICAgICAgICAgICAgICAgICAgICApKQ0KYGBgDQoNClZpZXcgdGhlIHBsb3RseSBmaWd1cmUNCmBgYHtyfQ0KZmlnDQpgYGANCg0KDQojIyMgU2VlIGFsc286DQoNCk1vZGVsIFRyYWluaW5nIChbbGlua10oLi9Nb2RlbFRyYWluaW5nTm90ZWJvb2submIuaHRtbCkpDQoNCk1vZGVsIFNlbnNpdGl2aXR5IChbbGlua10oLi9TZW5zaXRpdml0eUFuYWx5c2lzTm90ZWJvb2submIuaHRtbCkp