Finazas de la empresa I

Sebastián Alonso Sosa Pérez
Atoche Tavara Angelica
Rodríguez Pulache Evelyn Marily
Tavara Ordinola Ruth
Yarleque Peña Marco

Teoría Moderna de Portafolios

Creado: 9-01-2021

Descripción: En el siguiente entrenamiento se aplicará la teoría de portafolios eficientes para la elaboración de una cartera de inversión con seis valores bursátiles, correspondientes a empresas pertenecientes a diferentes sectores económicos y registradas en el índice Standard and Poor 500 (S&P 500).

Introducción a la teoría de Markowitz

En 1952, un economista llamado Harry Markowitz escribió un artículo titulado “Portfolio Selection”, un documento que contenía teorías que transformaron el panorama de la gestión de cartera, páginas que le otorgarían el Premio Nobel de Economía casi cuatro décadas después.

Antagónica a las estrategias clásicas de inversión, su Teoría de Portafolio Moderna continúa siendo una estrategia de inversión utilizada por traders profesionales. Esta herramienta de gestión de cartera, si se usa correctamente, puede dar como resultado una cartera de inversión diversa y rentable.

La idea central de su teoría plantea que el inversor racional, buscará maximizar sus beneficios (retornos) asumiendo el menor riesgo (volatilidad) posible, esto se puede lograr con la diversificación de la cartera, eligiendo en principio, acciones que tengan una correlación baja o negativa. Por ejemplo, combinando acciones de empresas con distinta actividad económica.

La teoría de Markowitz también es conocida como análisis de varianza media, que se resume en: encontrar el mayor rendimiento en un determinado nivel de riesgo o el menor riesgo en un determinado nivel de rendimiento.

Supuestos de la teoría de Markowitz

  • Los inversores son racionales y se comportan de manera que maximicen su utilidad con un determinado nivel de ingresos o dinero.
  • Los inversores tienen acceso gratuito a información justa y correcta sobre los rendimientos y el riesgo.
  • Los mercados son eficientes y absorben la información de manera rápida y perfecta.
  • Los inversores son reacios al riesgo y tratan de minimizar el riesgo y maximizar el rendimiento.
  • Los inversores basan sus decisiones en los rendimientos esperados y la varianza o desviación estándar de estos rendimientos de la media.
  • Los inversores eligen rendimientos más altos a rendimientos más bajos para un determinado nivel de riesgo.

Supuestos del modelo

  • Los retornos siguen una distribución normal.
  • Una función de utilidad cuadrática representa las decisiones de los inversores.

Diversificación de la teoría de Markowitz

Markowitz postuló que la diversificación no solo debe apuntar a reducir el riesgo de un valor reduciendo su variabilidad o desviación estándar, sino también reduciendo la covarianza o el riesgo interactivo de dos o más valores en una cartera. Como por combinación de diferentes valores, es teóricamente posible tener un rango de riesgo que varía de cero a infinito.

Frontera eficiente

La frontera eficiente es el conjunto de carteras óptimas que ofrecen el rendimiento esperado más alto para un nivel de riesgo definido o el riesgo más bajo para un nivel dado de rendimiento esperado.

Frontera eficiente

Datos a utilizar:

Los activos a usar son los siguientes activos financieros:

Riesgo de cartera y rendimiento esperado

El rendimiento esperado de la cartera se calcula como una suma ponderada de los rendimientos de los activos individuales. Para su cálculo se utiliza la siguiente fórmula: \[ Rep: \sum_{i=1}^{n}(Rep_1W_1)+(Rep_2W_2)+..........+(Rep_nW_n+) \]

Covarianza

La covarianza es una medida de la relación entre dos variables aleatorias. La métrica evalúa cuánto, en qué medida, las variables cambian juntas. En otras palabras, es esencialmente una medida de la varianza entre dos variables. Sin embargo, la métrica no evalúa la dependencia entre variables. Su cálculo se realiza mediante la sumatoria de la diferencia entre las rentabilidades de cada uno de los activos (Ri) y su rentabilidad esperada (Re), multiplicados por la probabilidad de ocurrencia (p).

\[ COV=\sum_{i=1}^n(Ri_x-Re_x)(Ri_y-Re_y)(p) \]

Coeficiente de correlación

La correlación mide la fuerza de la relación entre variables. La correlación es la medida a escala de la covarianza. Es adimensional. En otras palabras, el coeficiente de correlación siempre es un valor puro y no se mide en ninguna unidad.

\[ r=\frac{COV_{xy}}{\sigma_x\sigma_y} \]

Riesgo

La variación de la cartera es un valor estadístico que evalúa el grado de dispersión de los rendimientos de una cartera. Es un concepto importante en la teoría moderna de la inversión. Aunque la medida estadística en sí misma puede no proporcionar información significativa, podemos calcular la desviación estándar de la cartera utilizando la varianza de la cartera.

\[ \sigma^2=\sum_{i=1}^{n}(WxWyCOV_{(x,y)}) \]

Resultados

library(quantmod)
library(PerformanceAnalytics)
library(ggplot2)
library(IntroCompFinR)
library(pacman)
library(tidyverse)
tickers<-c("IBM","BABA","AMZN","FB","MSFT","LNVGY","TSLA")
precios<-NULL
fecha<-"2015-01-01"
for(a in tickers){
  precios <- cbind(precios, getSymbols.yahoo(a, from=fecha , periodicity = "daily" , auto.assign=F)[,6])
}
names(precios)<-tickers
PerformanceAnalytics::chart.TimeSeries(scale(precios),legend.loc = "topleft")

mean<- apply(PerformanceAnalytics::Return.calculate(precios,method = "log")[-1,],
             2,function(x) mean(x))
sd<- apply(PerformanceAnalytics::Return.calculate(precios,method = "log")[-1,],
           2,function(x) sd(x))
m<- as.data.frame(mean)
m<- t(m)
s<- t(as.data.frame(sd))
x<- rbind(m,s)*100
library(kableExtra)
x %>% t() %>% kbl(caption = "Table 1: Media & desviación estandar de los activos", escape = TRUE) %>%
  kable_classic(full_width = FALSE, html_font = "Cambria")
Table 1: Media & desviación estandar de los activos
mean sd
IBM -0.0023392 1.606594
BABA 0.0613897 2.131121
AMZN 0.1555131 1.935110
FB 0.0801107 2.018976
MSFT 0.1149366 1.751622
LNVGY 0.0177169 2.220536
TSLA 0.1934034 3.474488
library(ggplot2)
g1<- ggplot(mapping = aes(sd,mean,
                          label=c("IBM","BABA","AMZN","FB","MSFT","LNVGY","TSLA")))+
  geom_point()

g1<- g1+geom_text(hjust=0,vjust=0)

g1<- g1+theme_bw()+xlab("Riesgo")+
  ylab("Retorno")
g2<- g1
g2<-g2+scale_y_continuous(
  breaks = seq(0,0.003,by=0.001),
  limits = c(0,0.003)
)
g2<- g2+scale_x_continuous(
  breaks = seq(0.015,0.037,by=0.007),
  limits = c(0.015,0.037)
)
g2<- g2+ggtitle("Trade-off Riesgo-Retorno",
                subtitle = "7 Activos Riesgosos")+theme_minimal()
g2

retornos<- PerformanceAnalytics::Return.calculate(precios,method = "log")[-1,]
cov<-cov(retornos)*100
cov %>% round(digits = 4) %>% kbl(caption = "Table 2: Matriz de covarianzas de los activos", escape = TRUE) %>%
  kable_classic(full_width = FALSE, html_font = "Cambria")
Table 2: Matriz de covarianzas de los activos
IBM BABA AMZN FB MSFT LNVGY TSLA
IBM 0.0258 0.0110 0.0110 0.0117 0.0152 0.0088 0.0150
BABA 0.0110 0.0454 0.0198 0.0188 0.0183 0.0153 0.0221
AMZN 0.0110 0.0198 0.0374 0.0236 0.0222 0.0099 0.0244
FB 0.0117 0.0188 0.0236 0.0408 0.0216 0.0098 0.0240
MSFT 0.0152 0.0183 0.0222 0.0216 0.0307 0.0114 0.0244
LNVGY 0.0088 0.0153 0.0099 0.0098 0.0114 0.0493 0.0146
TSLA 0.0150 0.0221 0.0244 0.0240 0.0244 0.0146 0.1207
library(gplots)
generate_heat_map <- function(correlationMatrix, title)
{
  
  heatmap.2(x = correlationMatrix,    
            cellnote = correlationMatrix,   
            main = title,           
            symm = TRUE,            
            dendrogram="none",      
            Rowv = FALSE,           
            trace="none",           
            density.info="none",        
            notecol="black")          
}

corr1 <- round(cor(retornos) * 100, 2)
generate_heat_map(corr1,"Heatmap: Correlaciones")

Portafolio de minima varianza

Primero calculamos la media, la desviasión estandar y la matriz de varianza y covarianzas que son requeridas, esto se puede verificar en el teorema revisado en la sección anterio.

mean<- apply(retornos,2,function(x) mean(x))
sd<- apply(retornos, 2,function(x) sd(x))
cov<- cov(retornos)

Después de haber realizado estos cálculos se procede al realizar el cálculo del portafolio de minima varianza.

library(IntroCompFinR)
library(ggplot2)
# Pesos  ------------------------------------------------------------------

weights<- rep(1,7)/7


# construimos el portafolio -----------------------------------------------

getPortfolio(mean,cov,weights = 
               weights)


# Portafolio de minima varianza -------------------------------------------

globalmin=globalMin.portfolio(mean,cov,
                              shorts = F)


# Grafico -----------------------------------------------------------------

g3<- ggplot()+geom_point(
  mapping = aes(globalmin$sd,
                globalmin$er,
                color="1"),size=4
)
g3<- g3+geom_point(mapping = 
                     aes(sd,mean,color="2"),
                   size=4)

g3<- g3+ scale_color_manual(
  "",values = c("blue","purple"),
  labels=c("Min Var.","Stocks 1")
)
g3<- g3+ xlab("Riesgo")+ylab("Retorno")+
  ggtitle("Trade-off Riesgo-Retorno",
          subtitle = "Siete Activos riesgos & minima varianza")+
  theme_minimal()
g3

library(scales)
data=as.data.frame(round(globalmin$weights*100,2))
for(i in  1:7){
  if(data[i,1]==0){
    data[i,1]=NA
  }
  else{
    data[i,1]=data[i,1]
  }
}
names(data)=c("Pesos")
data=data.frame("Acciones"=tickers,"Pesos"=data)
data=na.omit(data)
bp<- ggplot(data = data,aes(x="",y=Pesos,fill=Acciones))+geom_bar(width = 1,stat = "identity")
pie<- bp + coord_polar("y",start=0)
aa<-pie +scale_fill_brewer(palette = "Blues")+ggtitle("Portafolio de minima varianza")
ata<-aa+geom_text(aes(label=percent(data[,2])),position = position_stack(vjust = 0.4),color="black")+
  theme_minimal()
ata + theme(panel.grid.major = element_line(linetype = "longdash")
)+
  theme(plot.title = element_text(family = "Times", 
                                  hjust = 0.50), legend.position = "bottom", 
        legend.direction = "horizontal") +labs(x = "Acciones")

Portafolio tangente y frontera eficente

library(IntroCompFinR)
library(ggplot2)
# Portafolio de minima varianza sujeto a un retorno objetivo  -------------
port.ibm<- efficient.portfolio(mean,cov,mean[1],shorts = F)
port.baba<- efficient.portfolio(mean,cov,mean[2],shorts = F)
port.amzn<- efficient.portfolio(mean,cov,mean[3],shorts = F)
port.fb<- efficient.portfolio(mean,cov,mean[4],shorts = F)
port.msft<- efficient.portfolio(mean,cov,mean[5],shorts = F)
port.lngvgy<- efficient.portfolio(mean,cov,mean[6],shorts = F)
port.tsla<- efficient.portfolio(mean,cov,mean[7],shorts = F)
mean.2<- c(port.ibm$er,port.baba$er,port.amzn$er,port.fb$er,port.msft$er,
           port.lngvgy$er,port.tsla$er)
sd.2 <- c(port.ibm$sd,port.baba$sd,port.amzn$sd,port.fb$sd,port.msft$sd,
          port.lngvgy$sd,port.tsla$sd)

# Cáluclo Portafolio tangente ---------------------------------------------

risk_free<- 0.0001

por.tang<- tangency.portfolio(mean,cov,
                              risk_free,shorts = F)

sharpe.ratio= (por.tang$er-risk_free)/por.tang$sd   
# Frontera eficiente  -----------------------------------------------------

eff.front.short<- efficient.frontier(mean,cov,
                                     nport = 50,
                                     alpha.min = -2,
                                     alpha.max = 1.5,shorts =F )
# grafica 
g6<- ggplot()+geom_point(mapping = 
                           aes(eff.front.short$sd,
                               eff.front.short$er,color="1"))

g6<- g6+geom_point(mapping = aes(por.tang$sd,
                                 por.tang$er,color="4"),size=4)
g6<- g6 + geom_point(mapping = aes(globalmin$sd
                                   ,globalmin$er,color="5"),size=4)

g6<- g6+geom_abline(intercept = risk_free,
                    slope=sharpe.ratio,color="#58FAF4",lty=2,size=1)

g6<- g6+ggtitle("Frontera Eficiente y Recta tangente")+xlab("Riesgo")+ylab("Retorno")+
  theme_minimal()

g6<- g6+scale_color_manual("",
                           values = c(
                             "#0040FF","#FE2EF7","#BDBDBD"      
                           ),
                           labels=c("Frontera Eficiente","Portafolio Tangente",
                                    "Min.var"))
g6

library(scales)
library(ggplot2)
data=as.data.frame(round(por.tang$weights*100,2))
for(i in  1:7){
  if(data[i,1]==0){
    data[i,1]=NA
  }
  else{
    data[i,1]=data[i,1]
  }
}
names(data)=c("Pesos")
data=data.frame("Acciones"=tickers,"Pesos"=data)
data=na.omit(data)
bp<- ggplot(data = data,aes(x="",y=Pesos,fill=Acciones))+geom_bar(width = 1,stat = "identity")
pie<- bp + coord_polar("y",start=0)
aa<-pie +scale_fill_brewer(palette = "Blues")+ggtitle("Portafolio tangente")
ata<-aa+geom_text(aes(label=percent(data[,2])),position = position_stack(vjust = 0.4),color="black")+
  theme_minimal()

ata + theme(panel.grid.major = element_line(linetype = "longdash")
)+
  theme(plot.title = element_text(family = "Times", 
                                  hjust = 0.50), legend.position = "bottom", 
        legend.direction = "horizontal") +labs(x = "Acciones")

Codigo anexado

#install.packages("IntroCompFinR", repos="http://R-Forge.R-project.org")
library(IntroCompFinR)
library(pacman)
library(PerformanceAnalytics)
library(quantmod)
library(tidyverse)
tickers<-c("IBM","BABA","AMZN","FB","MSFT","LNVGY","TSLA")
precios<-NULL
fecha<-"2015-01-01"
for(a in tickers){
  precios <- cbind(precios, getSymbols.yahoo(a, from=fecha , periodicity = "daily" , auto.assign=F)[,6])
}
names(precios)<-tickers
retornos<- Return.calculate(precios,method = "log")[-1,]
# Promedio

mean<- apply(PerformanceAnalytics::Return.calculate(precios,method = "log")[-1,],
             2,function(x) mean(x))

# Desviación estandar

sd<- apply(PerformanceAnalytics::Return.calculate(precios,method = "log")[-1,],
           2,function(x) sd(x))

# matriz de covarianzas

cov<- cov(retornos)

# Vamos a graficar 

g1<- ggplot(mapping = aes(sd,mean,
                          label=c("IBM","BABA","AMZN","FB","MSFT","LNVGY","TSLA")))+
  geom_point()

g1<- g1+geom_text(hjust=0,vjust=0)

g1<- g1+theme_bw()+xlab("Riesgo")+
  ylab("Retorno")
g2<- g1
g2<-g2+scale_y_continuous(
  breaks = seq(0,0.003,by=0.001),
  limits = c(0,0.003)
)
g2<- g2+scale_x_continuous(
  breaks = seq(0.015,0.037,by=0.007),
  limits = c(0.015,0.037)
)
g2<- g2+ggtitle("Trade-off Riesgo-Retorno",
                subtitle = "7 Activos Riesgosos")+theme_minimal()
g2

# Pesos  ------------------------------------------------------------------

weights<- rep(1,7)/7


# construimos el portafolio -----------------------------------------------

getPortfolio(mean,cov,weights = 
               weights)


# Portafolio de minima varianza -------------------------------------------

globalmin=globalMin.portfolio(mean,cov,
                              shorts = T)


# Grafico -----------------------------------------------------------------

g3<- ggplot()+geom_point(
  mapping = aes(globalmin$sd,
                globalmin$er,
                color="1"),size=4
)
g3<- g3+geom_point(mapping = 
                     aes(sd,mean,color="2"),
                   size=4)

g3<- g3+ scale_color_manual(
  "",values = c("blue","purple"),
  labels=c("Min Var.","Stocks 1")
)
g3<- g3+ xlab("Riesgo")+ylab("Retorno")+
  ggtitle("Trade-off Riesgo-Retorno",
          subtitle = "Tres Activos riesgos & minima varianza")+
  theme_modern_rc()


# Portafolio de minima varianza sujeto a un retorno objetivo  -------------
c("IBM","BABA","AMZN","FB","MSFT","LNVGY","TSLA")
port.ibm<- efficient.portfolio(mean,cov,mean[1],shorts = T)
port.baba<- efficient.portfolio(mean,cov,mean[2],shorts = T)
port.amzn<- efficient.portfolio(mean,cov,mean[3],shorts = T)
port.fb<- efficient.portfolio(mean,cov,mean[4],shorts = T)
port.msft<- efficient.portfolio(mean,cov,mean[5],shorts = T)
port.lngvgy<- efficient.portfolio(mean,cov,mean[6],shorts = T)
port.tsla<- efficient.portfolio(mean,cov,mean[7],shorts = T)
mean.2<- c(port.ibm$er,port.baba$er,port.amzn$er,port.fb$er,port.msft$er,
           port.lngvgy$er,port.tsla$er)
sd.2 <- c(port.ibm$sd,port.baba$sd,port.amzn$sd,port.fb$sd,port.msft$sd,
          port.lngvgy$sd,port.tsla$sd)

# Cáluclo Portafolio tangente ---------------------------------------------

risk_free<- 0.0001

por.tang<- tangency.portfolio(mean,cov,
                              risk_free,shorts = T)

sharpe.ratio= (por.tang$er-risk_free)/por.tang$sd                              

g4<- ggplot()+geom_point(mapping = aes(sd,
                                       mean,color="1"))
g4<-g4 + geom_point(mapping = aes(sd.2,mean.2),color="2")
g4<- g4+geom_point(mapping = aes(por.tang$sd,
                                 por.tang$er,color="3"))
g4<- g4 + geom_point(mapping = aes(globalmin$sd
                                   ,globalmin$er,color="4"))

g4<- g4+geom_abline(intercept = risk_free,
                    slope=sharpe.ratio,color="#58FAF4")
g4<- g4+scale_color_manual("",
                           values = c(
                             "red","pink","blue","green"      
                           ),
                           labels=c("stock1","tang.Port"
                                    "stock2","Min.var"))
g4<- g4+theme_modern_rc()+xlab("Riesgo")+
  ylab("Retorno")+
  ggtitle("Trade-off Riesgo-Retorno",
          subtitle = "Tres activos riesgosos & minima varianza")


# Frontera eficiente  -----------------------------------------------------

eff.front.short<- efficient.frontier(mean,cov,
                                     nport = 50,
                                     alpha.min = -2,
                                     alpha.max = 1.5,shorts = T)
# grafica 
g6<- ggplot()+geom_point(mapping = 
                           aes(eff.front.short$sd,
                               eff.front.short$er,color="1"))

g6<- g6+geom_point(mapping = aes(por.tang$sd,
                                 por.tang$er,color="4"),size=4)
g6<- g6 + geom_point(mapping = aes(globalmin$sd
                                   ,globalmin$er,color="5"),size=4)

g6<- g6+geom_abline(intercept = risk_free,
                    slope=sharpe.ratio,color="#58FAF4",lty=2,size=1)

g6<- g6+ggtitle("Frontera Eficiente y Recta tangente")+xlab("Riesgo")+ylab("Retorno")+
  theme_modern_rc()

g6<- g6+scale_color_manual("",
                           values = c(
                             "#0040FF","#FE2EF7","#BDBDBD"      
                           ),
                           labels=c("Frontera Eficiente","Portafolio Tangente",
                                    "Min.var"))

library(scales)
data=as.data.frame(round(globalmin$weights*100,2))
for(i in  1:7){
  if(data[i,1]==0){
    data[i,1]=NA
  }
  else{
    data[i,1]=data[i,1]
  }
}
names(data)=c("Pesos")
data=data.frame("Acciones"=tickers,"Pesos"=data)
data=na.omit(data)
bp<- ggplot(data = data,aes(x="",y=Pesos,fill=Acciones))+geom_bar(width = 1,stat = "identity")
bp
pie<- bp + coord_polar("y",start=0)
aa<-pie +scale_fill_brewer(palette = "Blues")+ggtitle("Portafolio de minima varianza")
ata<-aa+geom_text(aes(label=percent(data[,2])),position = position_stack(vjust = 0.4),color="black")+
  theme_minimal()

ata + theme(panel.grid.major = element_line(linetype = "longdash")
)+
  theme(plot.title = element_text(family = "Times", 
                                  hjust = 0.50), legend.position = "bottom", 
        legend.direction = "horizontal") +labs(x = "Acciones")




library(scales)
data=as.data.frame(round(por.tang$weights*100,2))
for(i in  1:7){
  if(data[i,1]==0){
    data[i,1]=NA
  }
  else{
    data[i,1]=data[i,1]
  }
}
names(data)=c("Pesos")
data=data.frame("Acciones"=tickers,"Pesos"=data)
data=na.omit(data)
bp<- ggplot(data = data,aes(x="",y=Pesos,fill=Acciones))+geom_bar(width = 1,stat = "identity")
bp
pie<- bp + coord_polar("y",start=0)
aa<-pie +scale_fill_brewer(palette = "Blues")+ggtitle("Portafolio tangente")
ata<-aa+geom_text(aes(label=percent(data[,2])),position = position_stack(vjust = 0.4),color="black")+
  theme_minimal()

ata + theme(panel.grid.major = element_line(linetype = "longdash")
)+
  theme(plot.title = element_text(family = "Times", 
                                  hjust = 0.50), legend.position = "bottom", 
        legend.direction = "horizontal") +labs(x = "Acciones")
LS0tDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgaGlnaGxpZ2h0OiB6ZW5idXJuDQogICAgdGhlbWU6IGZsYXRseQ0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgd29yZF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQo8Y2VudGVyPg0KIVtdKGRvd25sb2FkLmpwZykNCjwvY2VudGVyPg0KPGNlbnRlcj4NCiAgICA8Yj5GaW5hemFzIGRlIGxhIGVtcHJlc2EgSTwvYj48YnI+DQogICAgPGJyPg0KICAgIDxiPlNlYmFzdGnDoW4gQWxvbnNvIFNvc2EgUMOpcmV6PC9iPjxicj4NCiAgICA8Yj5BdG9jaGUgVGF2YXJhIEFuZ2VsaWNhPC9iPjxicj4NCiAgICA8Yj5Sb2Ryw61ndWV6IFB1bGFjaGUgRXZlbHluIE1hcmlseTwvYj48YnI+DQogICAgPGI+VGF2YXJhIE9yZGlub2xhIFJ1dGg8L2I+PGJyPg0KICAgIDxiPllhcmxlcXVlIFBlw7FhIE1hcmNvPC9iPg0KPGJyPg0KPGgxPlRlb3LDrWEgTW9kZXJuYSBkZSBQb3J0YWZvbGlvczwvaDE+DQo8L2NlbnRlcj4NCjxjZW50ZXI+DQo8aT5DcmVhZG86ICAgICA5LTAxLTIwMjENCjwvY2VudGVyPg0KDQoqKkRlc2NyaXBjacOzbioqOiBFbiBlbCBzaWd1aWVudGUgZW50cmVuYW1pZW50byBzZSBhcGxpY2Fyw6EgbGEgdGVvcsOtYSBkZSBwb3J0YWZvbGlvcyBlZmljaWVudGVzIHBhcmEgbGEgZWxhYm9yYWNpw7NuIGRlIHVuYSBjYXJ0ZXJhIGRlIGludmVyc2nDs24gY29uIHNlaXMgdmFsb3JlcyBidXJzw6F0aWxlcywgY29ycmVzcG9uZGllbnRlcyBhIGVtcHJlc2FzIHBlcnRlbmVjaWVudGVzIGEgZGlmZXJlbnRlcyBzZWN0b3JlcyBlY29uw7NtaWNvcyB5IHJlZ2lzdHJhZGFzIGVuIGVsIMOtbmRpY2UgU3RhbmRhcmQgYW5kIFBvb3IgNTAwIChTJlAgNTAwKS4gDQoNCiMgSW50cm9kdWNjacOzbiBhIGxhIHRlb3LDrWEgZGUgTWFya293aXR6DQoNCkVuIDE5NTIsIHVuIGVjb25vbWlzdGEgbGxhbWFkbyBIYXJyeSBNYXJrb3dpdHogZXNjcmliacOzIHVuIGFydMOtY3VsbyB0aXR1bGFkbyDigJxQb3J0Zm9saW8gU2VsZWN0aW9u4oCdLCB1biBkb2N1bWVudG8gcXVlIGNvbnRlbsOtYSB0ZW9yw61hcyBxdWUgdHJhbnNmb3JtYXJvbiBlbCBwYW5vcmFtYSBkZSBsYSBnZXN0acOzbiBkZSBjYXJ0ZXJhLCBww6FnaW5hcyBxdWUgbGUgb3RvcmdhcsOtYW4gZWwgUHJlbWlvIE5vYmVsIGRlIEVjb25vbcOtYSBjYXNpIGN1YXRybyBkw6ljYWRhcyBkZXNwdcOpcy4NCg0KQW50YWfDs25pY2EgYSBsYXMgZXN0cmF0ZWdpYXMgY2zDoXNpY2FzIGRlIGludmVyc2nDs24sIHN1IFRlb3LDrWEgZGUgUG9ydGFmb2xpbyBNb2Rlcm5hIGNvbnRpbsO6YSBzaWVuZG8gdW5hIGVzdHJhdGVnaWEgZGUgaW52ZXJzacOzbiB1dGlsaXphZGEgcG9yIHRyYWRlcnMgcHJvZmVzaW9uYWxlcy4gRXN0YSBoZXJyYW1pZW50YSBkZSBnZXN0acOzbiBkZSBjYXJ0ZXJhLCBzaSBzZSB1c2EgY29ycmVjdGFtZW50ZSwgcHVlZGUgZGFyIGNvbW8gcmVzdWx0YWRvIHVuYSBjYXJ0ZXJhIGRlIGludmVyc2nDs24gZGl2ZXJzYSB5IHJlbnRhYmxlLg0KDQpMYSBpZGVhIGNlbnRyYWwgZGUgc3UgdGVvcsOtYSBwbGFudGVhIHF1ZSBlbCBpbnZlcnNvciByYWNpb25hbCwgYnVzY2Fyw6EgbWF4aW1pemFyIHN1cyBiZW5lZmljaW9zIChyZXRvcm5vcykgYXN1bWllbmRvIGVsIG1lbm9yIHJpZXNnbyAodm9sYXRpbGlkYWQpIHBvc2libGUsIGVzdG8gc2UgcHVlZGUgbG9ncmFyIGNvbiBsYSBkaXZlcnNpZmljYWNpw7NuIGRlIGxhIGNhcnRlcmEsIGVsaWdpZW5kbyBlbiBwcmluY2lwaW8sIGFjY2lvbmVzIHF1ZSB0ZW5nYW4gdW5hIGNvcnJlbGFjacOzbiBiYWphIG8gbmVnYXRpdmEuIFBvciBlamVtcGxvLCBjb21iaW5hbmRvIGFjY2lvbmVzIGRlIGVtcHJlc2FzIGNvbiBkaXN0aW50YSBhY3RpdmlkYWQgZWNvbsOzbWljYS4gDQoNCkxhIHRlb3LDrWEgZGUgTWFya293aXR6IHRhbWJpw6luIGVzIGNvbm9jaWRhIGNvbW8gYW7DoWxpc2lzIGRlIHZhcmlhbnphIG1lZGlhLCBxdWUgc2UgcmVzdW1lIGVuOiBlbmNvbnRyYXIgZWwgbWF5b3IgcmVuZGltaWVudG8gZW4gdW4gZGV0ZXJtaW5hZG8gbml2ZWwgZGUgcmllc2dvIG8gZWwgbWVub3Igcmllc2dvIGVuIHVuIGRldGVybWluYWRvIG5pdmVsIGRlIHJlbmRpbWllbnRvLiANCg0KIyBTdXB1ZXN0b3MgZGUgbGEgdGVvcsOtYSBkZSBNYXJrb3dpdHoNCg0KKiBMb3MgaW52ZXJzb3JlcyBzb24gcmFjaW9uYWxlcyB5IHNlIGNvbXBvcnRhbiBkZSBtYW5lcmEgcXVlIG1heGltaWNlbiBzdSB1dGlsaWRhZCBjb24gdW4gZGV0ZXJtaW5hZG8gbml2ZWwgZGUgaW5ncmVzb3MgbyBkaW5lcm8uIA0KKiBMb3MgaW52ZXJzb3JlcyB0aWVuZW4gYWNjZXNvIGdyYXR1aXRvIGEgaW5mb3JtYWNpw7NuIGp1c3RhIHkgY29ycmVjdGEgc29icmUgbG9zIHJlbmRpbWllbnRvcyB5IGVsIHJpZXNnby4gDQoqIExvcyBtZXJjYWRvcyBzb24gZWZpY2llbnRlcyB5IGFic29yYmVuIGxhIGluZm9ybWFjacOzbiBkZSBtYW5lcmEgcsOhcGlkYSB5IHBlcmZlY3RhLiANCiogTG9zIGludmVyc29yZXMgc29uIHJlYWNpb3MgYWwgcmllc2dvIHkgdHJhdGFuIGRlIG1pbmltaXphciBlbCByaWVzZ28geSBtYXhpbWl6YXIgZWwgcmVuZGltaWVudG8uDQoqIExvcyBpbnZlcnNvcmVzIGJhc2FuIHN1cyBkZWNpc2lvbmVzIGVuIGxvcyByZW5kaW1pZW50b3MgZXNwZXJhZG9zIHkgbGEgdmFyaWFuemEgbyBkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGUgZXN0b3MgcmVuZGltaWVudG9zIGRlIGxhIG1lZGlhLiANCiogTG9zIGludmVyc29yZXMgZWxpZ2VuIHJlbmRpbWllbnRvcyBtw6FzIGFsdG9zIGEgcmVuZGltaWVudG9zIG3DoXMgYmFqb3MgcGFyYSB1biBkZXRlcm1pbmFkbyBuaXZlbCBkZSByaWVzZ28uIA0KDQojIFN1cHVlc3RvcyBkZWwgbW9kZWxvDQoNCiogTG9zIHJldG9ybm9zIHNpZ3VlbiB1bmEgZGlzdHJpYnVjacOzbiBub3JtYWwuIA0KKiBVbmEgZnVuY2nDs24gZGUgdXRpbGlkYWQgY3VhZHLDoXRpY2EgcmVwcmVzZW50YSBsYXMgZGVjaXNpb25lcyBkZSBsb3MgaW52ZXJzb3Jlcy4gDQoNCiMgRGl2ZXJzaWZpY2FjacOzbiBkZSBsYSB0ZW9yw61hIGRlIE1hcmtvd2l0eiANCg0KTWFya293aXR6IHBvc3R1bMOzIHF1ZSBsYSBkaXZlcnNpZmljYWNpw7NuIG5vIHNvbG8gZGViZSBhcHVudGFyIGEgcmVkdWNpciBlbCByaWVzZ28gZGUgdW4gdmFsb3IgcmVkdWNpZW5kbyBzdSB2YXJpYWJpbGlkYWQgbyBkZXN2aWFjacOzbiBlc3TDoW5kYXIsIHNpbm8gdGFtYmnDqW4gcmVkdWNpZW5kbyBsYSBjb3ZhcmlhbnphIG8gZWwgcmllc2dvIGludGVyYWN0aXZvIGRlIGRvcyBvIG3DoXMgdmFsb3JlcyBlbiB1bmEgY2FydGVyYS4gQ29tbyBwb3IgY29tYmluYWNpw7NuIGRlIGRpZmVyZW50ZXMgdmFsb3JlcywgZXMgdGXDs3JpY2FtZW50ZSBwb3NpYmxlIHRlbmVyIHVuIHJhbmdvIGRlIHJpZXNnbyBxdWUgdmFyw61hIGRlIGNlcm8gYSBpbmZpbml0by4NCg0KIyBGcm9udGVyYSBlZmljaWVudGUNCg0KTGEgZnJvbnRlcmEgZWZpY2llbnRlIGVzIGVsIGNvbmp1bnRvIGRlIGNhcnRlcmFzIMOzcHRpbWFzIHF1ZSBvZnJlY2VuIGVsIHJlbmRpbWllbnRvIGVzcGVyYWRvIG3DoXMgYWx0byBwYXJhIHVuIG5pdmVsIGRlIHJpZXNnbyBkZWZpbmlkbyBvIGVsIHJpZXNnbyBtw6FzIGJham8gcGFyYSB1biBuaXZlbCBkYWRvIGRlIHJlbmRpbWllbnRvIGVzcGVyYWRvLg0KDQohW0Zyb250ZXJhIGVmaWNpZW50ZV0oZnJvbnRlcmEucG5nKQ0KDQojIERhdG9zIGEgdXRpbGl6YXI6IA0KDQpMb3MgYWN0aXZvcyBhIHVzYXIgc29uIGxvcyBzaWd1aWVudGVzIGFjdGl2b3MgZmluYW5jaWVyb3M6DQoNCmBgYHtyLGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KHF1YW50bW9kKQ0KdGlja2VyczwtYygiSUJNIiwiQkFCQSIsIkFNWk4iLCJGQiIsIk1TRlQiLCJMTlZHWSIsIlRTTEEiKQ0KcHJlY2lvczwtTlVMTA0KZmVjaGE8LSIyMDE1LTAxLTAxIg0KZm9yKGEgaW4gdGlja2Vycyl7DQogIHByZWNpb3MgPC0gY2JpbmQocHJlY2lvcywgZ2V0U3ltYm9scy55YWhvbyhhLCBmcm9tPWZlY2hhICwgcGVyaW9kaWNpdHkgPSAiZGFpbHkiICwgYXV0by5hc3NpZ249RilbLDZdKQ0KfQ0KbmFtZXMocHJlY2lvcyk8LXRpY2tlcnMNCmBgYA0KDQpgYGB7cixlY2hvPUZBTFNFfQ0KbGlicmFyeShEVCkNCnByZWNpb3MgJT4lIHJvdW5kKGRpZ2l0cyA9IDQpICU+JSBkYXRhdGFibGUoZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywNCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRvbSA9ICdCbGZydGlwJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ1dHRvbnMgPSBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnLCAncHJpbnQnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aE1lbnUgPSBsaXN0KGMoMTAsMjUsNTAsLTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygxMCwyNSw1MCwiQWxsIikpKSkNCmBgYA0KDQojIFJpZXNnbyBkZSBjYXJ0ZXJhIHkgcmVuZGltaWVudG8gZXNwZXJhZG8NCg0KRWwgcmVuZGltaWVudG8gZXNwZXJhZG8gZGUgbGEgY2FydGVyYSBzZSBjYWxjdWxhIGNvbW8gdW5hIHN1bWEgcG9uZGVyYWRhIGRlIGxvcyByZW5kaW1pZW50b3MgZGUgbG9zIGFjdGl2b3MgaW5kaXZpZHVhbGVzLiBQYXJhIHN1IGPDoWxjdWxvIHNlIHV0aWxpemEgbGEgc2lndWllbnRlIGbDs3JtdWxhOiANCiQkDQpSZXA6IFxzdW1fe2k9MX1ee259KFJlcF8xV18xKSsoUmVwXzJXXzIpKy4uLi4uLi4uLi4rKFJlcF9uV19uKykNCiQkDQoNCiMgQ292YXJpYW56YQ0KDQpMYSBjb3ZhcmlhbnphIGVzIHVuYSBtZWRpZGEgZGUgbGEgcmVsYWNpw7NuIGVudHJlIGRvcyB2YXJpYWJsZXMgYWxlYXRvcmlhcy4gTGEgbcOpdHJpY2EgZXZhbMO6YSBjdcOhbnRvLCBlbiBxdcOpIG1lZGlkYSwgbGFzIHZhcmlhYmxlcyBjYW1iaWFuIGp1bnRhcy4gRW4gb3RyYXMgcGFsYWJyYXMsIGVzIGVzZW5jaWFsbWVudGUgdW5hIG1lZGlkYSBkZSBsYSB2YXJpYW56YSBlbnRyZSBkb3MgdmFyaWFibGVzLiBTaW4gZW1iYXJnbywgbGEgbcOpdHJpY2Egbm8gZXZhbMO6YSBsYSBkZXBlbmRlbmNpYSBlbnRyZSB2YXJpYWJsZXMuIFN1IGPDoWxjdWxvIHNlIHJlYWxpemEgbWVkaWFudGUgbGEgc3VtYXRvcmlhIGRlIGxhIGRpZmVyZW5jaWEgZW50cmUgbGFzIHJlbnRhYmlsaWRhZGVzIGRlIGNhZGEgdW5vIGRlIGxvcyBhY3Rpdm9zIChSaSkgeSBzdSByZW50YWJpbGlkYWQgZXNwZXJhZGEgKFJlKSwgbXVsdGlwbGljYWRvcyBwb3IgbGEgcHJvYmFiaWxpZGFkIGRlIG9jdXJyZW5jaWEgKHApLg0KDQokJA0KQ09WPVxzdW1fe2k9MX1ebihSaV94LVJlX3gpKFJpX3ktUmVfeSkocCkNCiQkDQoNCiMgQ29lZmljaWVudGUgZGUgY29ycmVsYWNpw7NuDQoNCkxhIGNvcnJlbGFjacOzbiBtaWRlIGxhIGZ1ZXJ6YSBkZSBsYSByZWxhY2nDs24gZW50cmUgdmFyaWFibGVzLiBMYSBjb3JyZWxhY2nDs24gZXMgbGEgbWVkaWRhIGEgZXNjYWxhIGRlIGxhIGNvdmFyaWFuemEuIEVzIGFkaW1lbnNpb25hbC4gRW4gb3RyYXMgcGFsYWJyYXMsIGVsIGNvZWZpY2llbnRlIGRlIGNvcnJlbGFjacOzbiBzaWVtcHJlIGVzIHVuIHZhbG9yIHB1cm8geSBubyBzZSBtaWRlIGVuIG5pbmd1bmEgdW5pZGFkLg0KDQokJA0Kcj1cZnJhY3tDT1Zfe3h5fX17XHNpZ21hX3hcc2lnbWFfeX0NCiQkDQoNCiMgUmllc2dvDQoNCkxhIHZhcmlhY2nDs24gZGUgbGEgY2FydGVyYSBlcyB1biB2YWxvciBlc3RhZMOtc3RpY28gcXVlIGV2YWzDumEgZWwgZ3JhZG8gZGUgZGlzcGVyc2nDs24gZGUgbG9zIHJlbmRpbWllbnRvcyBkZSB1bmEgY2FydGVyYS4gRXMgdW4gY29uY2VwdG8gaW1wb3J0YW50ZSBlbiBsYSB0ZW9yw61hIG1vZGVybmEgZGUgbGEgaW52ZXJzacOzbi4gQXVucXVlIGxhIG1lZGlkYSBlc3RhZMOtc3RpY2EgZW4gc8OtIG1pc21hIHB1ZWRlIG5vIHByb3BvcmNpb25hciBpbmZvcm1hY2nDs24gc2lnbmlmaWNhdGl2YSwgcG9kZW1vcyBjYWxjdWxhciBsYSBkZXN2aWFjacOzbiBlc3TDoW5kYXIgZGUgbGEgY2FydGVyYSB1dGlsaXphbmRvIGxhIHZhcmlhbnphIGRlIGxhIGNhcnRlcmEuDQoNCiQkDQpcc2lnbWFeMj1cc3VtX3tpPTF9XntufShXeFd5Q09WX3soeCx5KX0pDQokJA0KDQoNCiMgUmVzdWx0YWRvcyANCg0KYGBge3IsZXZhbD1GQUxTRX0NCmxpYnJhcnkocXVhbnRtb2QpDQpsaWJyYXJ5KFBlcmZvcm1hbmNlQW5hbHl0aWNzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShJbnRyb0NvbXBGaW5SKQ0KbGlicmFyeShwYWNtYW4pDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQpgYGB7cixldmFsPUZBTFNFfQ0KdGlja2VyczwtYygiSUJNIiwiQkFCQSIsIkFNWk4iLCJGQiIsIk1TRlQiLCJMTlZHWSIsIlRTTEEiKQ0KcHJlY2lvczwtTlVMTA0KZmVjaGE8LSIyMDE1LTAxLTAxIg0KZm9yKGEgaW4gdGlja2Vycyl7DQogIHByZWNpb3MgPC0gY2JpbmQocHJlY2lvcywgZ2V0U3ltYm9scy55YWhvbyhhLCBmcm9tPWZlY2hhICwgcGVyaW9kaWNpdHkgPSAiZGFpbHkiICwgYXV0by5hc3NpZ249RilbLDZdKQ0KfQ0KbmFtZXMocHJlY2lvcyk8LXRpY2tlcnMNCmBgYA0KDQpgYGB7cn0NClBlcmZvcm1hbmNlQW5hbHl0aWNzOjpjaGFydC5UaW1lU2VyaWVzKHNjYWxlKHByZWNpb3MpLGxlZ2VuZC5sb2MgPSAidG9wbGVmdCIpDQpgYGANCg0KYGBge3Isd2FybmluZz1UUlVFLG1lc3NhZ2U9VFJVRX0NCm1lYW48LSBhcHBseShQZXJmb3JtYW5jZUFuYWx5dGljczo6UmV0dXJuLmNhbGN1bGF0ZShwcmVjaW9zLG1ldGhvZCA9ICJsb2ciKVstMSxdLA0KICAgICAgICAgICAgIDIsZnVuY3Rpb24oeCkgbWVhbih4KSkNCnNkPC0gYXBwbHkoUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5jYWxjdWxhdGUocHJlY2lvcyxtZXRob2QgPSAibG9nIilbLTEsXSwNCiAgICAgICAgICAgMixmdW5jdGlvbih4KSBzZCh4KSkNCm08LSBhcy5kYXRhLmZyYW1lKG1lYW4pDQptPC0gdChtKQ0KczwtIHQoYXMuZGF0YS5mcmFtZShzZCkpDQp4PC0gcmJpbmQobSxzKSoxMDANCmxpYnJhcnkoa2FibGVFeHRyYSkNCnggJT4lIHQoKSAlPiUga2JsKGNhcHRpb24gPSAiVGFibGUgMTogTWVkaWEgJiBkZXN2aWFjacOzbiBlc3RhbmRhciBkZSBsb3MgYWN0aXZvcyIsIGVzY2FwZSA9IFRSVUUpICU+JQ0KICBrYWJsZV9jbGFzc2ljKGZ1bGxfd2lkdGggPSBGQUxTRSwgaHRtbF9mb250ID0gIkNhbWJyaWEiKQ0KYGBgDQoNCg0KDQoNCmBgYHtyLGV2YWw9VFJVRSx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KGdncGxvdDIpDQpnMTwtIGdncGxvdChtYXBwaW5nID0gYWVzKHNkLG1lYW4sDQogICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsPWMoIklCTSIsIkJBQkEiLCJBTVpOIiwiRkIiLCJNU0ZUIiwiTE5WR1kiLCJUU0xBIikpKSsNCiAgZ2VvbV9wb2ludCgpDQoNCmcxPC0gZzErZ2VvbV90ZXh0KGhqdXN0PTAsdmp1c3Q9MCkNCg0KZzE8LSBnMSt0aGVtZV9idygpK3hsYWIoIlJpZXNnbyIpKw0KICB5bGFiKCJSZXRvcm5vIikNCmcyPC0gZzENCmcyPC1nMitzY2FsZV95X2NvbnRpbnVvdXMoDQogIGJyZWFrcyA9IHNlcSgwLDAuMDAzLGJ5PTAuMDAxKSwNCiAgbGltaXRzID0gYygwLDAuMDAzKQ0KKQ0KZzI8LSBnMitzY2FsZV94X2NvbnRpbnVvdXMoDQogIGJyZWFrcyA9IHNlcSgwLjAxNSwwLjAzNyxieT0wLjAwNyksDQogIGxpbWl0cyA9IGMoMC4wMTUsMC4wMzcpDQopDQpnMjwtIGcyK2dndGl0bGUoIlRyYWRlLW9mZiBSaWVzZ28tUmV0b3JubyIsDQogICAgICAgICAgICAgICAgc3VidGl0bGUgPSAiNyBBY3Rpdm9zIFJpZXNnb3NvcyIpK3RoZW1lX21pbmltYWwoKQ0KZzINCmBgYA0KYGBge3Isd2FybmluZz1UUlVFLG1lc3NhZ2U9VFJVRX0NCnJldG9ybm9zPC0gUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5jYWxjdWxhdGUocHJlY2lvcyxtZXRob2QgPSAibG9nIilbLTEsXQ0KY292PC1jb3YocmV0b3Jub3MpKjEwMA0KY292ICU+JSByb3VuZChkaWdpdHMgPSA0KSAlPiUga2JsKGNhcHRpb24gPSAiVGFibGUgMjogTWF0cml6IGRlIGNvdmFyaWFuemFzIGRlIGxvcyBhY3Rpdm9zIiwgZXNjYXBlID0gVFJVRSkgJT4lDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IEZBTFNFLCBodG1sX2ZvbnQgPSAiQ2FtYnJpYSIpDQpgYGANCg0KYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShncGxvdHMpDQpnZW5lcmF0ZV9oZWF0X21hcCA8LSBmdW5jdGlvbihjb3JyZWxhdGlvbk1hdHJpeCwgdGl0bGUpDQp7DQogIA0KICBoZWF0bWFwLjIoeCA9IGNvcnJlbGF0aW9uTWF0cml4LCAgICANCiAgICAgICAgICAgIGNlbGxub3RlID0gY29ycmVsYXRpb25NYXRyaXgsICAgDQogICAgICAgICAgICBtYWluID0gdGl0bGUsICAgICAgICAgICANCiAgICAgICAgICAgIHN5bW0gPSBUUlVFLCAgICAgICAgICAgIA0KICAgICAgICAgICAgZGVuZHJvZ3JhbT0ibm9uZSIsICAgICAgDQogICAgICAgICAgICBSb3d2ID0gRkFMU0UsICAgICAgICAgICANCiAgICAgICAgICAgIHRyYWNlPSJub25lIiwgICAgICAgICAgIA0KICAgICAgICAgICAgZGVuc2l0eS5pbmZvPSJub25lIiwgICAgICAgIA0KICAgICAgICAgICAgbm90ZWNvbD0iYmxhY2siKSAgICAgICAgICANCn0NCg0KY29ycjEgPC0gcm91bmQoY29yKHJldG9ybm9zKSAqIDEwMCwgMikNCmdlbmVyYXRlX2hlYXRfbWFwKGNvcnIxLCJIZWF0bWFwOiBDb3JyZWxhY2lvbmVzIikNCmBgYA0KDQoNCiMjIFBvcnRhZm9saW8gZGUgbWluaW1hIHZhcmlhbnphIA0KDQpQcmltZXJvIGNhbGN1bGFtb3MgbGEgbWVkaWEsIGxhIGRlc3ZpYXNpw7NuIGVzdGFuZGFyIHkgbGEgbWF0cml6IGRlIHZhcmlhbnphIHkgY292YXJpYW56YXMgcXVlIHNvbiByZXF1ZXJpZGFzLCBlc3RvIHNlIHB1ZWRlIHZlcmlmaWNhciBlbiBlbCB0ZW9yZW1hIHJldmlzYWRvIGVuIGxhIHNlY2Npw7NuIGFudGVyaW8uIA0KDQpgYGB7cixldmFsPUZBTFNFfQ0KbWVhbjwtIGFwcGx5KHJldG9ybm9zLDIsZnVuY3Rpb24oeCkgbWVhbih4KSkNCnNkPC0gYXBwbHkocmV0b3Jub3MsIDIsZnVuY3Rpb24oeCkgc2QoeCkpDQpjb3Y8LSBjb3YocmV0b3Jub3MpDQpgYGANCg0KRGVzcHXDqXMgZGUgaGFiZXIgcmVhbGl6YWRvIGVzdG9zIGPDoWxjdWxvcyBzZSBwcm9jZWRlIGFsIHJlYWxpemFyIGVsIGPDoWxjdWxvIGRlbCBwb3J0YWZvbGlvIGRlIG1pbmltYSB2YXJpYW56YS4NCg0KDQoNCg0KDQpgYGByDQpsaWJyYXJ5KEludHJvQ29tcEZpblIpDQpsaWJyYXJ5KGdncGxvdDIpDQojIFBlc29zICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0Kd2VpZ2h0czwtIHJlcCgxLDcpLzcNCg0KDQojIGNvbnN0cnVpbW9zIGVsIHBvcnRhZm9saW8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KZ2V0UG9ydGZvbGlvKG1lYW4sY292LHdlaWdodHMgPSANCiAgICAgICAgICAgICAgIHdlaWdodHMpDQoNCg0KIyBQb3J0YWZvbGlvIGRlIG1pbmltYSB2YXJpYW56YSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmdsb2JhbG1pbj1nbG9iYWxNaW4ucG9ydGZvbGlvKG1lYW4sY292LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvcnRzID0gRikNCg0KDQojIEdyYWZpY28gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KZzM8LSBnZ3Bsb3QoKStnZW9tX3BvaW50KA0KICBtYXBwaW5nID0gYWVzKGdsb2JhbG1pbiRzZCwNCiAgICAgICAgICAgICAgICBnbG9iYWxtaW4kZXIsDQogICAgICAgICAgICAgICAgY29sb3I9IjEiKSxzaXplPTQNCikNCmczPC0gZzMrZ2VvbV9wb2ludChtYXBwaW5nID0gDQogICAgICAgICAgICAgICAgICAgICBhZXMoc2QsbWVhbixjb2xvcj0iMiIpLA0KICAgICAgICAgICAgICAgICAgIHNpemU9NCkNCg0KZzM8LSBnMysgc2NhbGVfY29sb3JfbWFudWFsKA0KICAiIix2YWx1ZXMgPSBjKCJibHVlIiwicHVycGxlIiksDQogIGxhYmVscz1jKCJNaW4gVmFyLiIsIlN0b2NrcyAxIikNCikNCmczPC0gZzMrIHhsYWIoIlJpZXNnbyIpK3lsYWIoIlJldG9ybm8iKSsNCiAgZ2d0aXRsZSgiVHJhZGUtb2ZmIFJpZXNnby1SZXRvcm5vIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJTaWV0ZSBBY3Rpdm9zIHJpZXNnb3MgJiBtaW5pbWEgdmFyaWFuemEiKSsNCiAgdGhlbWVfbWluaW1hbCgpDQpnMw0KYGBgDQohW10oYXRhNDUucG5nKQ0KYGBgcg0KbGlicmFyeShzY2FsZXMpDQpkYXRhPWFzLmRhdGEuZnJhbWUocm91bmQoZ2xvYmFsbWluJHdlaWdodHMqMTAwLDIpKQ0KZm9yKGkgaW4gIDE6Nyl7DQogIGlmKGRhdGFbaSwxXT09MCl7DQogICAgZGF0YVtpLDFdPU5BDQogIH0NCiAgZWxzZXsNCiAgICBkYXRhW2ksMV09ZGF0YVtpLDFdDQogIH0NCn0NCm5hbWVzKGRhdGEpPWMoIlBlc29zIikNCmRhdGE9ZGF0YS5mcmFtZSgiQWNjaW9uZXMiPXRpY2tlcnMsIlBlc29zIj1kYXRhKQ0KZGF0YT1uYS5vbWl0KGRhdGEpDQpicDwtIGdncGxvdChkYXRhID0gZGF0YSxhZXMoeD0iIix5PVBlc29zLGZpbGw9QWNjaW9uZXMpKStnZW9tX2Jhcih3aWR0aCA9IDEsc3RhdCA9ICJpZGVudGl0eSIpDQpwaWU8LSBicCArIGNvb3JkX3BvbGFyKCJ5IixzdGFydD0wKQ0KYWE8LXBpZSArc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCbHVlcyIpK2dndGl0bGUoIlBvcnRhZm9saW8gZGUgbWluaW1hIHZhcmlhbnphIikNCmF0YTwtYWErZ2VvbV90ZXh0KGFlcyhsYWJlbD1wZXJjZW50KGRhdGFbLDJdKSkscG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNCksY29sb3I9ImJsYWNrIikrDQogIHRoZW1lX21pbmltYWwoKQ0KYXRhICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJsb25nZGFzaCIpDQopKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJUaW1lcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41MCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCANCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgK2xhYnMoeCA9ICJBY2Npb25lcyIpDQpgYGANCg0KIVtdKGF0YTc4NS5wbmcpDQoNCiMjIFBvcnRhZm9saW8gdGFuZ2VudGUgeSBmcm9udGVyYSBlZmljZW50ZQ0KDQpgYGByDQpsaWJyYXJ5KEludHJvQ29tcEZpblIpDQpsaWJyYXJ5KGdncGxvdDIpDQojIFBvcnRhZm9saW8gZGUgbWluaW1hIHZhcmlhbnphIHN1amV0byBhIHVuIHJldG9ybm8gb2JqZXRpdm8gIC0tLS0tLS0tLS0tLS0NCnBvcnQuaWJtPC0gZWZmaWNpZW50LnBvcnRmb2xpbyhtZWFuLGNvdixtZWFuWzFdLHNob3J0cyA9IEYpDQpwb3J0LmJhYmE8LSBlZmZpY2llbnQucG9ydGZvbGlvKG1lYW4sY292LG1lYW5bMl0sc2hvcnRzID0gRikNCnBvcnQuYW16bjwtIGVmZmljaWVudC5wb3J0Zm9saW8obWVhbixjb3YsbWVhblszXSxzaG9ydHMgPSBGKQ0KcG9ydC5mYjwtIGVmZmljaWVudC5wb3J0Zm9saW8obWVhbixjb3YsbWVhbls0XSxzaG9ydHMgPSBGKQ0KcG9ydC5tc2Z0PC0gZWZmaWNpZW50LnBvcnRmb2xpbyhtZWFuLGNvdixtZWFuWzVdLHNob3J0cyA9IEYpDQpwb3J0LmxuZ3ZneTwtIGVmZmljaWVudC5wb3J0Zm9saW8obWVhbixjb3YsbWVhbls2XSxzaG9ydHMgPSBGKQ0KcG9ydC50c2xhPC0gZWZmaWNpZW50LnBvcnRmb2xpbyhtZWFuLGNvdixtZWFuWzddLHNob3J0cyA9IEYpDQptZWFuLjI8LSBjKHBvcnQuaWJtJGVyLHBvcnQuYmFiYSRlcixwb3J0LmFtem4kZXIscG9ydC5mYiRlcixwb3J0Lm1zZnQkZXIsDQogICAgICAgICAgIHBvcnQubG5ndmd5JGVyLHBvcnQudHNsYSRlcikNCnNkLjIgPC0gYyhwb3J0LmlibSRzZCxwb3J0LmJhYmEkc2QscG9ydC5hbXpuJHNkLHBvcnQuZmIkc2QscG9ydC5tc2Z0JHNkLA0KICAgICAgICAgIHBvcnQubG5ndmd5JHNkLHBvcnQudHNsYSRzZCkNCg0KIyBDw6FsdWNsbyBQb3J0YWZvbGlvIHRhbmdlbnRlIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpyaXNrX2ZyZWU8LSAwLjAwMDENCg0KcG9yLnRhbmc8LSB0YW5nZW5jeS5wb3J0Zm9saW8obWVhbixjb3YsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByaXNrX2ZyZWUsc2hvcnRzID0gRikNCg0Kc2hhcnBlLnJhdGlvPSAocG9yLnRhbmckZXItcmlza19mcmVlKS9wb3IudGFuZyRzZCAgIA0KIyBGcm9udGVyYSBlZmljaWVudGUgIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmVmZi5mcm9udC5zaG9ydDwtIGVmZmljaWVudC5mcm9udGllcihtZWFuLGNvdiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucG9ydCA9IDUwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhLm1pbiA9IC0yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFscGhhLm1heCA9IDEuNSxzaG9ydHMgPUYgKQ0KIyBncmFmaWNhIA0KZzY8LSBnZ3Bsb3QoKStnZW9tX3BvaW50KG1hcHBpbmcgPSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhlZmYuZnJvbnQuc2hvcnQkc2QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWZmLmZyb250LnNob3J0JGVyLGNvbG9yPSIxIikpDQoNCmc2PC0gZzYrZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHBvci50YW5nJHNkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9yLnRhbmckZXIsY29sb3I9IjQiKSxzaXplPTQpDQpnNjwtIGc2ICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGdsb2JhbG1pbiRzZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsZ2xvYmFsbWluJGVyLGNvbG9yPSI1Iiksc2l6ZT00KQ0KDQpnNjwtIGc2K2dlb21fYWJsaW5lKGludGVyY2VwdCA9IHJpc2tfZnJlZSwNCiAgICAgICAgICAgICAgICAgICAgc2xvcGU9c2hhcnBlLnJhdGlvLGNvbG9yPSIjNThGQUY0IixsdHk9MixzaXplPTEpDQoNCmc2PC0gZzYrZ2d0aXRsZSgiRnJvbnRlcmEgRWZpY2llbnRlIHkgUmVjdGEgdGFuZ2VudGUiKSt4bGFiKCJSaWVzZ28iKSt5bGFiKCJSZXRvcm5vIikrDQogIHRoZW1lX21pbmltYWwoKQ0KDQpnNjwtIGc2K3NjYWxlX2NvbG9yX21hbnVhbCgiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIjMDA0MEZGIiwiI0ZFMkVGNyIsIiNCREJEQkQiICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICApLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkZyb250ZXJhIEVmaWNpZW50ZSIsIlBvcnRhZm9saW8gVGFuZ2VudGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1pbi52YXIiKSkNCmc2DQoNCmBgYA0KIVtdKDAwMDAxYi5wbmcpDQoNCmBgYHINCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZGF0YT1hcy5kYXRhLmZyYW1lKHJvdW5kKHBvci50YW5nJHdlaWdodHMqMTAwLDIpKQ0KZm9yKGkgaW4gIDE6Nyl7DQogIGlmKGRhdGFbaSwxXT09MCl7DQogICAgZGF0YVtpLDFdPU5BDQogIH0NCiAgZWxzZXsNCiAgICBkYXRhW2ksMV09ZGF0YVtpLDFdDQogIH0NCn0NCm5hbWVzKGRhdGEpPWMoIlBlc29zIikNCmRhdGE9ZGF0YS5mcmFtZSgiQWNjaW9uZXMiPXRpY2tlcnMsIlBlc29zIj1kYXRhKQ0KZGF0YT1uYS5vbWl0KGRhdGEpDQpicDwtIGdncGxvdChkYXRhID0gZGF0YSxhZXMoeD0iIix5PVBlc29zLGZpbGw9QWNjaW9uZXMpKStnZW9tX2Jhcih3aWR0aCA9IDEsc3RhdCA9ICJpZGVudGl0eSIpDQpwaWU8LSBicCArIGNvb3JkX3BvbGFyKCJ5IixzdGFydD0wKQ0KYWE8LXBpZSArc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJCbHVlcyIpK2dndGl0bGUoIlBvcnRhZm9saW8gdGFuZ2VudGUiKQ0KYXRhPC1hYStnZW9tX3RleHQoYWVzKGxhYmVsPXBlcmNlbnQoZGF0YVssMl0pKSxwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC40KSxjb2xvcj0iYmxhY2siKSsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmF0YSArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAibG9uZ2Rhc2giKQ0KKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiVGltZXMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNTApLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgDQogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICtsYWJzKHggPSAiQWNjaW9uZXMiKQ0KDQoNCmBgYA0KDQohW10oMDAwMDAyICgxKS5wbmcpDQoNCg0KIyBDb2RpZ28gYW5leGFkbyANCg0KYGBgcg0KI2luc3RhbGwucGFja2FnZXMoIkludHJvQ29tcEZpblIiLCByZXBvcz0iaHR0cDovL1ItRm9yZ2UuUi1wcm9qZWN0Lm9yZyIpDQpsaWJyYXJ5KEludHJvQ29tcEZpblIpDQpsaWJyYXJ5KHBhY21hbikNCmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpDQpsaWJyYXJ5KHF1YW50bW9kKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp0aWNrZXJzPC1jKCJJQk0iLCJCQUJBIiwiQU1aTiIsIkZCIiwiTVNGVCIsIkxOVkdZIiwiVFNMQSIpDQpwcmVjaW9zPC1OVUxMDQpmZWNoYTwtIjIwMTUtMDEtMDEiDQpmb3IoYSBpbiB0aWNrZXJzKXsNCiAgcHJlY2lvcyA8LSBjYmluZChwcmVjaW9zLCBnZXRTeW1ib2xzLnlhaG9vKGEsIGZyb209ZmVjaGEgLCBwZXJpb2RpY2l0eSA9ICJkYWlseSIgLCBhdXRvLmFzc2lnbj1GKVssNl0pDQp9DQpuYW1lcyhwcmVjaW9zKTwtdGlja2Vycw0KcmV0b3Jub3M8LSBSZXR1cm4uY2FsY3VsYXRlKHByZWNpb3MsbWV0aG9kID0gImxvZyIpWy0xLF0NCiMgUHJvbWVkaW8NCg0KbWVhbjwtIGFwcGx5KFBlcmZvcm1hbmNlQW5hbHl0aWNzOjpSZXR1cm4uY2FsY3VsYXRlKHByZWNpb3MsbWV0aG9kID0gImxvZyIpWy0xLF0sDQogICAgICAgICAgICAgMixmdW5jdGlvbih4KSBtZWFuKHgpKQ0KDQojIERlc3ZpYWNpw7NuIGVzdGFuZGFyDQoNCnNkPC0gYXBwbHkoUGVyZm9ybWFuY2VBbmFseXRpY3M6OlJldHVybi5jYWxjdWxhdGUocHJlY2lvcyxtZXRob2QgPSAibG9nIilbLTEsXSwNCiAgICAgICAgICAgMixmdW5jdGlvbih4KSBzZCh4KSkNCg0KIyBtYXRyaXogZGUgY292YXJpYW56YXMNCg0KY292PC0gY292KHJldG9ybm9zKQ0KDQojIFZhbW9zIGEgZ3JhZmljYXIgDQoNCmcxPC0gZ2dwbG90KG1hcHBpbmcgPSBhZXMoc2QsbWVhbiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWw9YygiSUJNIiwiQkFCQSIsIkFNWk4iLCJGQiIsIk1TRlQiLCJMTlZHWSIsIlRTTEEiKSkpKw0KICBnZW9tX3BvaW50KCkNCg0KZzE8LSBnMStnZW9tX3RleHQoaGp1c3Q9MCx2anVzdD0wKQ0KDQpnMTwtIGcxK3RoZW1lX2J3KCkreGxhYigiUmllc2dvIikrDQogIHlsYWIoIlJldG9ybm8iKQ0KZzI8LSBnMQ0KZzI8LWcyK3NjYWxlX3lfY29udGludW91cygNCiAgYnJlYWtzID0gc2VxKDAsMC4wMDMsYnk9MC4wMDEpLA0KICBsaW1pdHMgPSBjKDAsMC4wMDMpDQopDQpnMjwtIGcyK3NjYWxlX3hfY29udGludW91cygNCiAgYnJlYWtzID0gc2VxKDAuMDE1LDAuMDM3LGJ5PTAuMDA3KSwNCiAgbGltaXRzID0gYygwLjAxNSwwLjAzNykNCikNCmcyPC0gZzIrZ2d0aXRsZSgiVHJhZGUtb2ZmIFJpZXNnby1SZXRvcm5vIiwNCiAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICI3IEFjdGl2b3MgUmllc2dvc29zIikrdGhlbWVfbWluaW1hbCgpDQpnMg0KDQojIFBlc29zICAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0Kd2VpZ2h0czwtIHJlcCgxLDcpLzcNCg0KDQojIGNvbnN0cnVpbW9zIGVsIHBvcnRhZm9saW8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KZ2V0UG9ydGZvbGlvKG1lYW4sY292LHdlaWdodHMgPSANCiAgICAgICAgICAgICAgIHdlaWdodHMpDQoNCg0KIyBQb3J0YWZvbGlvIGRlIG1pbmltYSB2YXJpYW56YSAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCmdsb2JhbG1pbj1nbG9iYWxNaW4ucG9ydGZvbGlvKG1lYW4sY292LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvcnRzID0gVCkNCg0KDQojIEdyYWZpY28gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KZzM8LSBnZ3Bsb3QoKStnZW9tX3BvaW50KA0KICBtYXBwaW5nID0gYWVzKGdsb2JhbG1pbiRzZCwNCiAgICAgICAgICAgICAgICBnbG9iYWxtaW4kZXIsDQogICAgICAgICAgICAgICAgY29sb3I9IjEiKSxzaXplPTQNCikNCmczPC0gZzMrZ2VvbV9wb2ludChtYXBwaW5nID0gDQogICAgICAgICAgICAgICAgICAgICBhZXMoc2QsbWVhbixjb2xvcj0iMiIpLA0KICAgICAgICAgICAgICAgICAgIHNpemU9NCkNCg0KZzM8LSBnMysgc2NhbGVfY29sb3JfbWFudWFsKA0KICAiIix2YWx1ZXMgPSBjKCJibHVlIiwicHVycGxlIiksDQogIGxhYmVscz1jKCJNaW4gVmFyLiIsIlN0b2NrcyAxIikNCikNCmczPC0gZzMrIHhsYWIoIlJpZXNnbyIpK3lsYWIoIlJldG9ybm8iKSsNCiAgZ2d0aXRsZSgiVHJhZGUtb2ZmIFJpZXNnby1SZXRvcm5vIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJUcmVzIEFjdGl2b3Mgcmllc2dvcyAmIG1pbmltYSB2YXJpYW56YSIpKw0KICB0aGVtZV9tb2Rlcm5fcmMoKQ0KDQoNCiMgUG9ydGFmb2xpbyBkZSBtaW5pbWEgdmFyaWFuemEgc3VqZXRvIGEgdW4gcmV0b3JubyBvYmpldGl2byAgLS0tLS0tLS0tLS0tLQ0KYygiSUJNIiwiQkFCQSIsIkFNWk4iLCJGQiIsIk1TRlQiLCJMTlZHWSIsIlRTTEEiKQ0KcG9ydC5pYm08LSBlZmZpY2llbnQucG9ydGZvbGlvKG1lYW4sY292LG1lYW5bMV0sc2hvcnRzID0gVCkNCnBvcnQuYmFiYTwtIGVmZmljaWVudC5wb3J0Zm9saW8obWVhbixjb3YsbWVhblsyXSxzaG9ydHMgPSBUKQ0KcG9ydC5hbXpuPC0gZWZmaWNpZW50LnBvcnRmb2xpbyhtZWFuLGNvdixtZWFuWzNdLHNob3J0cyA9IFQpDQpwb3J0LmZiPC0gZWZmaWNpZW50LnBvcnRmb2xpbyhtZWFuLGNvdixtZWFuWzRdLHNob3J0cyA9IFQpDQpwb3J0Lm1zZnQ8LSBlZmZpY2llbnQucG9ydGZvbGlvKG1lYW4sY292LG1lYW5bNV0sc2hvcnRzID0gVCkNCnBvcnQubG5ndmd5PC0gZWZmaWNpZW50LnBvcnRmb2xpbyhtZWFuLGNvdixtZWFuWzZdLHNob3J0cyA9IFQpDQpwb3J0LnRzbGE8LSBlZmZpY2llbnQucG9ydGZvbGlvKG1lYW4sY292LG1lYW5bN10sc2hvcnRzID0gVCkNCm1lYW4uMjwtIGMocG9ydC5pYm0kZXIscG9ydC5iYWJhJGVyLHBvcnQuYW16biRlcixwb3J0LmZiJGVyLHBvcnQubXNmdCRlciwNCiAgICAgICAgICAgcG9ydC5sbmd2Z3kkZXIscG9ydC50c2xhJGVyKQ0Kc2QuMiA8LSBjKHBvcnQuaWJtJHNkLHBvcnQuYmFiYSRzZCxwb3J0LmFtem4kc2QscG9ydC5mYiRzZCxwb3J0Lm1zZnQkc2QsDQogICAgICAgICAgcG9ydC5sbmd2Z3kkc2QscG9ydC50c2xhJHNkKQ0KDQojIEPDoWx1Y2xvIFBvcnRhZm9saW8gdGFuZ2VudGUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnJpc2tfZnJlZTwtIDAuMDAwMQ0KDQpwb3IudGFuZzwtIHRhbmdlbmN5LnBvcnRmb2xpbyhtZWFuLGNvdiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpc2tfZnJlZSxzaG9ydHMgPSBUKQ0KDQpzaGFycGUucmF0aW89IChwb3IudGFuZyRlci1yaXNrX2ZyZWUpL3Bvci50YW5nJHNkICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCmc0PC0gZ2dwbG90KCkrZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHNkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbixjb2xvcj0iMSIpKQ0KZzQ8LWc0ICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKHNkLjIsbWVhbi4yKSxjb2xvcj0iMiIpDQpnNDwtIGc0K2dlb21fcG9pbnQobWFwcGluZyA9IGFlcyhwb3IudGFuZyRzZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvci50YW5nJGVyLGNvbG9yPSIzIikpDQpnNDwtIGc0ICsgZ2VvbV9wb2ludChtYXBwaW5nID0gYWVzKGdsb2JhbG1pbiRzZA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsZ2xvYmFsbWluJGVyLGNvbG9yPSI0IikpDQoNCmc0PC0gZzQrZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gcmlza19mcmVlLA0KICAgICAgICAgICAgICAgICAgICBzbG9wZT1zaGFycGUucmF0aW8sY29sb3I9IiM1OEZBRjQiKQ0KZzQ8LSBnNCtzY2FsZV9jb2xvcl9tYW51YWwoIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicmVkIiwicGluayIsImJsdWUiLCJncmVlbiIgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9Yygic3RvY2sxIiwidGFuZy5Qb3J0Ig0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0b2NrMiIsIk1pbi52YXIiKSkNCmc0PC0gZzQrdGhlbWVfbW9kZXJuX3JjKCkreGxhYigiUmllc2dvIikrDQogIHlsYWIoIlJldG9ybm8iKSsNCiAgZ2d0aXRsZSgiVHJhZGUtb2ZmIFJpZXNnby1SZXRvcm5vIiwNCiAgICAgICAgICBzdWJ0aXRsZSA9ICJUcmVzIGFjdGl2b3Mgcmllc2dvc29zICYgbWluaW1hIHZhcmlhbnphIikNCg0KDQojIEZyb250ZXJhIGVmaWNpZW50ZSAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KZWZmLmZyb250LnNob3J0PC0gZWZmaWNpZW50LmZyb250aWVyKG1lYW4sY292LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5wb3J0ID0gNTAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEubWluID0gLTIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxwaGEubWF4ID0gMS41LHNob3J0cyA9IFQpDQojIGdyYWZpY2EgDQpnNjwtIGdncGxvdCgpK2dlb21fcG9pbnQobWFwcGluZyA9IA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGVmZi5mcm9udC5zaG9ydCRzZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlZmYuZnJvbnQuc2hvcnQkZXIsY29sb3I9IjEiKSkNCg0KZzY8LSBnNitnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMocG9yLnRhbmckc2QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3IudGFuZyRlcixjb2xvcj0iNCIpLHNpemU9NCkNCmc2PC0gZzYgKyBnZW9tX3BvaW50KG1hcHBpbmcgPSBhZXMoZ2xvYmFsbWluJHNkDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxnbG9iYWxtaW4kZXIsY29sb3I9IjUiKSxzaXplPTQpDQoNCmc2PC0gZzYrZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gcmlza19mcmVlLA0KICAgICAgICAgICAgICAgICAgICBzbG9wZT1zaGFycGUucmF0aW8sY29sb3I9IiM1OEZBRjQiLGx0eT0yLHNpemU9MSkNCg0KZzY8LSBnNitnZ3RpdGxlKCJGcm9udGVyYSBFZmljaWVudGUgeSBSZWN0YSB0YW5nZW50ZSIpK3hsYWIoIlJpZXNnbyIpK3lsYWIoIlJldG9ybm8iKSsNCiAgdGhlbWVfbW9kZXJuX3JjKCkNCg0KZzY8LSBnNitzY2FsZV9jb2xvcl9tYW51YWwoIiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIzAwNDBGRiIsIiNGRTJFRjciLCIjQkRCREJEIiAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJGcm9udGVyYSBFZmljaWVudGUiLCJQb3J0YWZvbGlvIFRhbmdlbnRlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNaW4udmFyIikpDQoNCmxpYnJhcnkoc2NhbGVzKQ0KZGF0YT1hcy5kYXRhLmZyYW1lKHJvdW5kKGdsb2JhbG1pbiR3ZWlnaHRzKjEwMCwyKSkNCmZvcihpIGluICAxOjcpew0KICBpZihkYXRhW2ksMV09PTApew0KICAgIGRhdGFbaSwxXT1OQQ0KICB9DQogIGVsc2V7DQogICAgZGF0YVtpLDFdPWRhdGFbaSwxXQ0KICB9DQp9DQpuYW1lcyhkYXRhKT1jKCJQZXNvcyIpDQpkYXRhPWRhdGEuZnJhbWUoIkFjY2lvbmVzIj10aWNrZXJzLCJQZXNvcyI9ZGF0YSkNCmRhdGE9bmEub21pdChkYXRhKQ0KYnA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEsYWVzKHg9IiIseT1QZXNvcyxmaWxsPUFjY2lvbmVzKSkrZ2VvbV9iYXIod2lkdGggPSAxLHN0YXQgPSAiaWRlbnRpdHkiKQ0KYnANCnBpZTwtIGJwICsgY29vcmRfcG9sYXIoInkiLHN0YXJ0PTApDQphYTwtcGllICtzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJsdWVzIikrZ2d0aXRsZSgiUG9ydGFmb2xpbyBkZSBtaW5pbWEgdmFyaWFuemEiKQ0KYXRhPC1hYStnZW9tX3RleHQoYWVzKGxhYmVsPXBlcmNlbnQoZGF0YVssMl0pKSxwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC40KSxjb2xvcj0iYmxhY2siKSsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmF0YSArIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUobGluZXR5cGUgPSAibG9uZ2Rhc2giKQ0KKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiVGltZXMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNTApLCBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgDQogICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpICtsYWJzKHggPSAiQWNjaW9uZXMiKQ0KDQoNCg0KDQpsaWJyYXJ5KHNjYWxlcykNCmRhdGE9YXMuZGF0YS5mcmFtZShyb3VuZChwb3IudGFuZyR3ZWlnaHRzKjEwMCwyKSkNCmZvcihpIGluICAxOjcpew0KICBpZihkYXRhW2ksMV09PTApew0KICAgIGRhdGFbaSwxXT1OQQ0KICB9DQogIGVsc2V7DQogICAgZGF0YVtpLDFdPWRhdGFbaSwxXQ0KICB9DQp9DQpuYW1lcyhkYXRhKT1jKCJQZXNvcyIpDQpkYXRhPWRhdGEuZnJhbWUoIkFjY2lvbmVzIj10aWNrZXJzLCJQZXNvcyI9ZGF0YSkNCmRhdGE9bmEub21pdChkYXRhKQ0KYnA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEsYWVzKHg9IiIseT1QZXNvcyxmaWxsPUFjY2lvbmVzKSkrZ2VvbV9iYXIod2lkdGggPSAxLHN0YXQgPSAiaWRlbnRpdHkiKQ0KYnANCnBpZTwtIGJwICsgY29vcmRfcG9sYXIoInkiLHN0YXJ0PTApDQphYTwtcGllICtzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIkJsdWVzIikrZ2d0aXRsZSgiUG9ydGFmb2xpbyB0YW5nZW50ZSIpDQphdGE8LWFhK2dlb21fdGV4dChhZXMobGFiZWw9cGVyY2VudChkYXRhWywyXSkpLHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjQpLGNvbG9yPSJibGFjayIpKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYXRhICsgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfbGluZShsaW5ldHlwZSA9ICJsb25nZGFzaCIpDQopKw0KICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJUaW1lcyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0ID0gMC41MCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCANCiAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgK2xhYnMoeCA9ICJBY2Npb25lcyIpDQoNCg0KYGBgDQoNCg==