A continuación se muestra un estudio utilizando regresiones logísticas simples y múltiples para predecir una variable categórtica.
El Estudio pretende encontrar un modelo de regresión logística para predecir si un alumno del curso de Matemáticas puede aprobar o no dicho curso utiliando las notas obtenidas del primer, segundo parcial y examen final como variables explicatorias.
Se mostaran seis modelos, en los primeros tres se pretende predecir si el alumno gana o no la clase con una regresión logística simple, asignando una probabilidad en función de la nota del primer parcial, segundo parcial y el examen final.
Despues se mostrará una regresión logística múltiple, para asignar un valor de probabilidad al par de notas formado por las notas del (primer parcial -segundo parcial), (segundo parcial - examen final) y por último (primer parcial - examen final).
Es importante recalcar que estamos asumiendo que todos los examenes tanto parciales como final, tiene la misma ponderación en el curso:
#Cargamos librerias
library(dplyr)
library(ggplot2)
#Cargamos DataSet.
data_set<- read.csv('Research2filePreng.csv')
#View(data_set)
Primero Limparemos y Ajustaremos la Data
#Personalizamos el data frame
data_set<-data_set %>%
select(Curso02, Seccion03, Carrera04, Fecha06, Ciclo07,
Genero10, PrimerParcial11, SegundoParcial13, ExamenFinal15, NotaFinal17) %>%
filter(Ciclo07 != "NA") #Eliminamos Filas con NA
#Cambio de Nombres de Columna
colnames(data_set)<-c("Curso", "Sec", "Carrera", "Smst", "Año", "Genero", "EP1",
"EP2", "EF", "NF")
El dataset quedaría de esta manera
head(data_set)
En General Cuantos Alumnos Hombres y Mujeres Han Habido:
gen_summary<- data_set %>%
select(Genero)%>%
group_by(Genero) %>%
summarise(Total=sum(Genero))
gen_summary$Genero[1]="Masculino"
gen_summary$Genero[2]="Femenino"
gen_summary
Graficando tenemos:
gen_summary %>%
ggplot(aes(x=Genero, y=Total)) +
geom_bar(stat = "identity", fill=c("pink", "light blue"))

Agregamos columna si el alumno aprobo o no (variable para clasificación)
data_set$Aprob<-ifelse((data_set$NF >= 61), yes=1, no=0)
Separaremos la data en 70% para entrenamiento y 30% para pruebas:
#Subsets de datos para entrenamiento y pruebas Matematica 1
train_mate1<-mate1[c(1:round(0.7*nrow(mate1))),]
test_mate1<-mate1[c(round(0.7*nrow(mate1)) + 1: nrow(mate1)),]
test_mate1<- test_mate1 %>%
filter(Sec != "NA")
Modelo 1: Influencia en la Nota del Primer Parcial en Aprobar el Curso:
#Modelo para Matematica 1:
mod1<-glm(data=train_mate1, formula = Aprob ~ EP1, family = binomial(link="logit"))
#Agregando el Modelo:
xv<-seq(min(train_mate1$EP1), max(train_mate1$EP1), 0.01)
yv<-predict(object=mod1, list(EP1=xv), type="response")
demo_plot1<-data.frame(xv, yv)
names(demo_plot1)<-c("Nota", "Probabilidad")
logitplot_1<-ggplot(data=demo_plot1, aes(x=Nota, y=Probabilidad)) + geom_line()
plotm1 <- train_mate1 %>%
arrange(EP1) %>%
ggplot(aes(x=EP1, y=Aprob)) +
geom_point() + geom_line(data=demo_plot1, aes(x=Nota, y=Probabilidad), color="blue") +
labs(x = "Nota Parcial 1") +
labs(y = "Probabilidad")+
ggtitle("Probabilidad de Ganar el Curso en función de la Nota de Parcial 1") +
theme_minimal()
#Graficando tenemos.
plotm1

La ecución del modelo es:
\[ P(\small{EP1})=\frac{1}{1+e^{-[(0.0765)-4.447*(EP1)]}}\]
Calculando efectividad:
a<-0
i<-1
for(i in (1:nrow(resultado1))){
if(resultado1$`Aprobo Clase`[[i]] == resultado1$Prediccion[[i]]){
a<-a+1
} else {
a<-a
}
}
ef1<-a/nrow(resultado1)
El modelo 1 tiene un 0.7625418 de efectividad.
Modelo 2: Influencia en la Nota del Segundo Parcial en Aprobar el Curso
#Modelo para Matematica 1:
mod2<-glm(data=train_mate1, formula = Aprob ~ EP2, family = binomial(link="logit"))
#Agregando el Modelo:
xv<-seq(min(train_mate1$EP2), max(train_mate1$EP2), 0.01)
yv<-predict(object=mod2, list(EP2=xv), type="response")
demo_plot2<-data.frame(xv, yv)
names(demo_plot2)<-c("Nota", "Probabilidad")
logitplot_2<-ggplot(data=demo_plot2, aes(x=Nota, y=Probabilidad)) + geom_line()
plotm2 <- train_mate1 %>%
arrange(EP2) %>%
ggplot(aes(x=EP2, y=Aprob)) +
geom_point() + geom_line(data=demo_plot2, aes(x=Nota, y=Probabilidad), color="red") +
labs(x = "Nota Parcial 2") +
labs(y = "Probabilidad")+
ggtitle("Probabilidad de Ganar el Curso en función de la Nota de Parcial 2") +
theme_minimal()
#Graficando tenemos.
plotm2

La ecución del modelo es:
\[ P(\small{EP2})=\frac{1}{1+e^{-[(0.1059)-5.6135*(EP2)]}}\]
Calculando efectividad:
a<-0
i<-1
for(i in (1:nrow(resultado2))){
if(resultado2$`Aprobo Clase`[[i]] == resultado2$Prediccion[[i]]){
a<-a+1
} else {
a<-a
}
}
ef2<-a/nrow(resultado2)
El modelo 2 tiene un 0.8595318 de efectividad.
Modelo 3: Influencia en la Nota del Examen Final en Aprobar el Curso
#Modelo para Matematica 1:
mod3<-glm(data=train_mate1, formula = Aprob ~ EF , family = binomial(link="logit"))
#Agregando el Modelo:
xv<-seq(min(train_mate1$EF), max(train_mate1$EF), 0.01)
yv<-predict(object=mod3, list(EF=xv), type="response")
demo_plot3<-data.frame(xv, yv)
names(demo_plot3)<-c("Nota", "Probabilidad")
logitplot_3<-ggplot(data=demo_plot3, aes(x=Nota, y=Probabilidad)) + geom_line()
Graficando Tenemos:
plotm3 <- train_mate1 %>%
arrange(EF) %>%
ggplot(aes(x=EF, y=Aprob)) +
geom_point() + geom_line(data=demo_plot3, aes(x=Nota, y=Probabilidad), color="green") +
labs(x = "Nota Examen Final") +
labs(y = "Probabilidad")+
ggtitle("Probabilidad de Ganar el Curso en función de la Nota del Examen Final") +
theme_minimal()
plotm3

La ecución del modelo es:
\[ P(\small{EP2})=\frac{1}{1+e^{-[(0.0934)-3.9102*(EP2)]}}\] ##### ##### A continuación mostratemos una predicciones en base al modelo 3 asumiendo que si la probabilidad es mayor a 0.5, entonces el alumno aprobara el curso, el frame de datos esta formado por la nota del examen final, la probabilidad obtenida por el modelo, si el alumno aprobo o no el curso y la predicción obtenida con el modelo si la probabilidad es mayor a 0.5:
prediccion3<-predict(object=mod3, newdata=test_mate1, type="response")
resultado3<-data.frame(test_mate1$EF, (as.data.frame(prediccion3))$prediccion3)
resultado3$Aprobo<- test_mate1$Aprob #Agregamos columna del resultado
names(resultado3)<-c("Nota", "Probabilidad", "Aprobo Clase")
resultado3$Prediccion<- ifelse((resultado3$Probabilidad >= 0.5), yes=1, no=0)
resultado3 %>% arrange(Nota) ## Orgenamos el arreglo
Calculando efectividad:
a<-0
i<-1
for(i in (1:nrow(resultado3))){
if(resultado3$`Aprobo Clase`[[i]] == resultado3$Prediccion[[i]]){
a<-a+1
} else {
a<-a
}
}
ef3<-a/nrow(resultado3)
El modelo 3 tiene un 0.8528428 de efectividad.
Gráficas de Probabilidad:
xv<-seq(0, 100, 0.01)
yv1<-predict(object=mod1, list(EP1=xv), type="response")
yv2<-predict(object=mod2, list(EP2=xv), type="response")
yv3<-predict(object=mod3, list(EF=xv), type="response")
demo_plot1<-data.frame(xv, yv1)
demo_plot2<-data.frame(xv, yv2)
demo_plot3<-data.frame(xv, yv3)
names(demo_plot1)<-c("Nota", "Probabilidad")
names(demo_plot2)<-c("Nota", "Probabilidad")
names(demo_plot3)<-c("Nota", "Probabilidad")
test_mate1 %>%
ggplot(aes(x=EF, y=Aprob)) +
geom_blank() + geom_line(data=demo_plot1, aes(x=Nota, y=Probabilidad, fill=Probabilidad), color="blue")+
geom_line(data=demo_plot2, aes(x=Nota, y=Probabilidad), color="red") +
geom_line(data=demo_plot3, aes(x=Nota, y=Probabilidad), color="green") +
labs(x = "Nota Examen (0-100)") +
labs(y = "Probabilidad" )+
ggtitle("Probabilidad de Ganar el Curso en función de la Nota obtenida") +
scale_x_continuous(breaks=seq(0,100,by=10)) +
theme_minimal()
Ignoring unknown aesthetics: fill

En la grafica anterior se muestan la regresión logistica simple de cada modelo obtenido para compararlos, donde la azul es la probabilidad de aprobar el curso en función de la nota del primer parcial, la roja es la probabilidad de ganar el curso en función de la nota del segundo parcial, y la verde es la probabilidad de ganar el curso en función de la nota del examen final.
Ahora mostraremos tres modelos de regresión logistica múltiple, utilizando combinaciones de dos examens para encontrar la probabilidad de que un alumno apreuebe/repruebe la clase.
Modelo 4: Influencia del Primer y Segundo Parcial:
library(plotly)
#Modelo Regresión Logistica Multiple, Examen Parcial 1 y Examen Parcial 2:
mod4<-glm(data=train_mate1, formula = Aprob ~ EP1 + EP2 , family = binomial(link="logit"))
#Definiendo Dominio:
x4<-seq(0, 100, 1)
y4<-seq(0, 100, 1)
domain<-expand.grid(x4,y4)
names(domain)<-c("EP1", "EP2")
z4<-predict(mod4, domain, type="response")
d<-data.frame(z4)
z4<-d$z4
data_xy=data.frame(domain$EP1, domain$EP2, z4)
Graficando Tenemos:
#plot_ly(z = z4, type = "surface", )
p <- plot_ly(x =domain$EP1, y = domain$EP2, z = z4, type = "heatmap", xaxis=domain$EP1, yaxis=domain$EP2,
colorbar = list(title = "Probabilidad")) %>%
layout(title = 'Probabilidad de Ganar el Curso vrs Parcial 1 y Parcial 2', xaxis = list(title = 'Nota Parcial 1') , yaxis = list(title = 'Nota Parcial 2'))
p
La ecución del modelo es:
\[ P(\small{EP1, EP2})=\frac{1}{1+e^{-[-8.6398+0.0582*(EP1)+0.09931*(EP2)]}}\] #####A continuación mostratemos una predicciones en base al modelo 4 asumiendo que si la probabilidad es mayor a 0.5, entonces el alumno aprobara el curso, el frame de datos esta formado por la nota del primer parcial, la nota del segundo parcial, la probabilidad obtenida por el modelo, si el alumno aprobo o no el curso y la predicción obtenida con el modelo si la probabilidad es mayor a 0.5:
prediccion4<-predict(object=mod4, newdata=test_mate1, type="response")
resultado4<-data.frame(test_mate1$EP1, test_mate1$EP2, (as.data.frame(prediccion4))$prediccion4)
resultado4$Aprobo<- test_mate1$Aprob #Agregamos columna del resultado
names(resultado4)<-c("Nota EP1", "Nota EP2", "Probabilidad", "Aprobo Clase")
resultado4$Prediccion<- ifelse((resultado4$Probabilidad >= 0.5), yes=1, no=0)
resultado4 %>% arrange(`Nota EP1`) ## Orgenamos el arreglo
Calculando efectividad:
b<-0
i<-1
for(i in (1:nrow(resultado4))){
if(resultado4$`Aprobo Clase`[[i]] == resultado4$Prediccion[[i]]){
b<-b+1
} else {
b<-b
}
}
ef4<-b/nrow(resultado4)
El modelo 4 tiene un 0.8528428 de efectividad.
Modelo 5: Influencia del Primer Parcial y Examen Final:
#Modelo para Matematica 1:
mod5<-glm(data=train_mate1, formula = Aprob ~ EP1 + EF , family = binomial(link="logit"))
#Agregando el Modelo:
domain<-expand.grid(x4,y4)
x4<-seq(0, 100, 1)
y4<-seq(0, 100, 1)
names(domain)<-c("EP1", "EF")
z4<-predict(mod5, domain, type="response")
d<-data.frame(z4)
z4<-d$z4
data_xy=data.frame(domain$EP1, domain$EF, z4)
Graficando tenemos:
#plot_ly(z = z4, type = "surface", )
p <- plot_ly(x =domain$EP1, y = domain$EF, z = z4, type = "heatmap", xaxis=domain$EP1, yaxis=domain$EF,
colorbar = list(title = "Probabilidad")) %>%
layout(title = 'Probabilidad de Ganar el Curso vrs Parcial 1 y Final', xaxis = list(title = 'Nota Parcial 1') , yaxis = list(title = 'Nota Examen Final'))
p
La ecución del modelo es:
\[ P(\small{EP1, EF})=\frac{1}{1+e^{-[-7.9954+0.06821*(EP1)+0.0935*(EF)]}}\] ##### A continuación mostratemos una predicciones en base al modelo 5 asumiendo que si la probabilidad es mayor a 0.5, entonces el alumno aprobara el curso, el frame de datos esta formado por la nota del primer parcial, la nota del examen final, la probabilidad obtenida por el modelo, si el alumno aprobo o no el curso y la predicción obtenida con el modelo si la probabilidad es mayor a 0.5::
prediccion5<-predict(object=mod5, newdata=test_mate1, type="response")
resultado5<-data.frame(test_mate1$EP1, test_mate1$EF, (as.data.frame(prediccion5))$prediccion5)
resultado5$Aprobo<- test_mate1$Aprob #Agregamos columna del resultado
names(resultado5)<-c("Nota EP1", "Nota EF", "Probabilidad", "Aprobo Clase")
resultado5$Prediccion<- ifelse((resultado5$Probabilidad >= 0.5), yes=1, no=0)
resultado5 %>% arrange(`Nota EP1`) ## Orgenamos el arreglo
Calculando efectividad:
a<-0
i<-1
for(i in (1:nrow(resultado5))){
if(resultado5$`Aprobo Clase`[[i]] == resultado5$Prediccion[[i]]){
a<-a+1
} else {
a<-a
}
}
ef5<-a/nrow(resultado5)
El modelo 5 tiene un 0.9565217 de efectividad.
Modelo 6: Influencia del Segundo Parcial y Examen Final:
#Modelo para Matematica 1:
mod6<-glm(data=train_mate1, formula = Aprob ~ EP2 + EF , family = binomial(link="logit"))
#Agregando el Modelo:
domain<-expand.grid(x4,y4)
x4<-seq(0, 100, 1)
y4<-seq(0, 100, 1)
names(domain)<-c("EP2", "EF")
z4<-predict(mod6, domain, type="response")
d<-data.frame(z4)
z4<-d$z4
data_xy=data.frame(domain$EP2, domain$EF, z4)
Graficando Tenemos:
#plot_ly(z = z4, type = "surface", )
p <- plot_ly(x =domain$EP2, y = domain$EF, z = z4, type = "heatmap", xaxis=domain$EP2, yaxis=domain$EF,
colorbar = list(title = "Probabilidad")) %>%
layout(title = 'Probabilidad de Ganar el Curso vrs Parcial 2 y Final', xaxis = list(title = 'Nota Parcial 2') , yaxis = list(title = 'Nota Examen Final'))
p
La ecución del modelo es:
\[ P(\small{EP2, EF})=\frac{1}{1+e^{-[-9.8516+0.1098*(EP2)+0.09282*(EF)]}}\] #####A continuación mostratemos una predicciones en base al modelo 4 asumiendo que si la probabilidad es mayor a 0.5, entonces el alumno aprobara el curso, el frame de datos esta formado por la nota del segundo parcial, la nota del examen final, la probabilidad obtenida por el modelo, si el alumno aprobo o no el curso y la predicción obtenida con el modelo si la probabilidad es mayor a 0.5::
prediccion6<-predict(object=mod6, newdata=test_mate1, type="response")
resultado6<-data.frame(test_mate1$EP2, test_mate1$EF, (as.data.frame(prediccion6))$prediccion6)
resultado6$Aprobo<- test_mate1$Aprob #Agregamos columna del resultado
names(resultado6)<-c("Nota EP1", "Nota EF", "Probabilidad", "Aprobo Clase")
resultado6$Prediccion<- ifelse((resultado6$Probabilidad >= 0.5), yes=1, no=0)
resultado6 %>% arrange(`Nota EP1`) ## Orgenamos el arreglo
Calculando efectividad:
a<-0
i<-1
for(i in (1:nrow(resultado6))){
if(resultado6$`Aprobo Clase`[[i]] == resultado6$Prediccion[[i]]){
a<-a+1
} else {
a<-a
}
}
ef6<-a/nrow(resultado5)
El modelo 1 tiene un 0.9698997 de efectividad.
Conclisiones:
De los modelos mostrados anteriormente, podemos notar que en general todos los examens tiene una influencia importante en aprobar el curso de matematica o no, sin embargo, es interesante ver que la eficiencia de los modelos que involucran al examen final son superiores, por tanto es vital que los alumnos le pongan mas empeño al examen final, según nuestro estudio.
LS0tDQp0aXRsZTogIlByb3llY3RvIEVjb25vbWV0cu1hIDEiDQphdXRob3I6IFByZW5nIEJpYmENCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojIyMjIEEgY29udGludWFjafNuIHNlIG11ZXN0cmEgdW4gZXN0dWRpbyB1dGlsaXphbmRvIHJlZ3Jlc2lvbmVzIGxvZ+1zdGljYXMgc2ltcGxlcyB5IG36bHRpcGxlcyBwYXJhIHByZWRlY2lyIHVuYSB2YXJpYWJsZSBjYXRlZ/NydGljYS4NCg0KIyMjIyBFbCBFc3R1ZGlvIHByZXRlbmRlIGVuY29udHJhciB1biBtb2RlbG8gZGUgcmVncmVzafNuIGxvZ+1zdGljYSBwYXJhIHByZWRlY2lyIHNpIHVuIGFsdW1ubyBkZWwgY3Vyc28gZGUgTWF0ZW3hdGljYXMgcHVlZGUgYXByb2JhciBvIG5vIGRpY2hvIGN1cnNvIHV0aWxpYW5kbyBsYXMgbm90YXMgb2J0ZW5pZGFzIGRlbCBwcmltZXIsIHNlZ3VuZG8gcGFyY2lhbCB5IGV4YW1lbiBmaW5hbCBjb21vIHZhcmlhYmxlcyBleHBsaWNhdG9yaWFzLg0KDQojIyMjU2UgbW9zdGFyYW4gc2VpcyBtb2RlbG9zLCBlbiBsb3MgcHJpbWVyb3MgdHJlcyBzZSBwcmV0ZW5kZSBwcmVkZWNpciBzaSBlbCBhbHVtbm8gZ2FuYSBvIG5vIGxhIGNsYXNlIGNvbiB1bmEgcmVncmVzafNuIGxvZ+1zdGljYSBzaW1wbGUsIGFzaWduYW5kbyB1bmEgcHJvYmFiaWxpZGFkIGVuIGZ1bmNp824gZGUgbGEgbm90YSBkZWwgcHJpbWVyIHBhcmNpYWwsIHNlZ3VuZG8gcGFyY2lhbCB5IGVsIGV4YW1lbiBmaW5hbC4NCg0KIyMjI0Rlc3B1ZXMgc2UgbW9zdHJhcuEgdW5hIHJlZ3Jlc2nzbiBsb2ftc3RpY2EgbfpsdGlwbGUsIHBhcmEgYXNpZ25hciB1biB2YWxvciBkZSBwcm9iYWJpbGlkYWQgYWwgcGFyIGRlIG5vdGFzIGZvcm1hZG8gcG9yIGxhcyBub3RhcyBkZWwgKHByaW1lciBwYXJjaWFsIC1zZWd1bmRvIHBhcmNpYWwpLCAoc2VndW5kbyBwYXJjaWFsIC0gZXhhbWVuIGZpbmFsKSB5IHBvciD6bHRpbW8gKHByaW1lciBwYXJjaWFsIC0gZXhhbWVuIGZpbmFsKS4NCg0KIyMjIyBFcyBpbXBvcnRhbnRlIHJlY2FsY2FyIHF1ZSBlc3RhbW9zIGFzdW1pZW5kbyBxdWUgdG9kb3MgbG9zIGV4YW1lbmVzIHRhbnRvIHBhcmNpYWxlcyBjb21vIGZpbmFsLCB0aWVuZSBsYSBtaXNtYSBwb25kZXJhY2nzbiBlbiBlbCBjdXJzbzoNCg0KDQpgYGB7cn0NCiNDYXJnYW1vcyBsaWJyZXJpYXMNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQoNCiNDYXJnYW1vcyBEYXRhU2V0Lg0KZGF0YV9zZXQ8LSByZWFkLmNzdignUmVzZWFyY2gyZmlsZVByZW5nLmNzdicpDQojVmlldyhkYXRhX3NldCkNCmBgYA0KDQojIyMjIFByaW1lcm8gTGltcGFyZW1vcyB5IEFqdXN0YXJlbW9zIGxhIERhdGENCmBgYHtyfQ0KI1BlcnNvbmFsaXphbW9zIGVsIGRhdGEgZnJhbWUNCmRhdGFfc2V0PC1kYXRhX3NldCAlPiUNCiAgc2VsZWN0KEN1cnNvMDIsIFNlY2Npb24wMywgQ2FycmVyYTA0LCBGZWNoYTA2LCBDaWNsbzA3LCANCiAgICBHZW5lcm8xMCwgUHJpbWVyUGFyY2lhbDExLCBTZWd1bmRvUGFyY2lhbDEzLCBFeGFtZW5GaW5hbDE1LCBOb3RhRmluYWwxNykgJT4lDQogIGZpbHRlcihDaWNsbzA3ICE9ICJOQSIpICNFbGltaW5hbW9zIEZpbGFzIGNvbiBOQQ0KDQojQ2FtYmlvIGRlIE5vbWJyZXMgZGUgQ29sdW1uYQ0KY29sbmFtZXMoZGF0YV9zZXQpPC1jKCJDdXJzbyIsICJTZWMiLCAiQ2FycmVyYSIsICJTbXN0IiwgIkHxbyIsICJHZW5lcm8iLCAiRVAxIiwNCiAgICAgICAgICAgICAgICAgICAgICAiRVAyIiwgIkVGIiwgIk5GIikgDQpgYGANCiMjIyMgRWwgZGF0YXNldCBxdWVkYXLtYSBkZSBlc3RhIG1hbmVyYQ0KYGBge3J9DQpoZWFkKGRhdGFfc2V0KQ0KYGBgDQojIyMjIEVuIEdlbmVyYWwgQ3VhbnRvcyBBbHVtbm9zIEhvbWJyZXMgeSBNdWplcmVzIEhhbiBIYWJpZG86DQpgYGB7cn0NCmdlbl9zdW1tYXJ5PC0gZGF0YV9zZXQgJT4lDQogIHNlbGVjdChHZW5lcm8pJT4lDQogIGdyb3VwX2J5KEdlbmVybykgJT4lDQogIHN1bW1hcmlzZShUb3RhbD1zdW0oR2VuZXJvKSkNCmdlbl9zdW1tYXJ5JEdlbmVyb1sxXT0iTWFzY3VsaW5vIg0KZ2VuX3N1bW1hcnkkR2VuZXJvWzJdPSJGZW1lbmlubyINCmdlbl9zdW1tYXJ5DQpgYGANCiMjIyMgR3JhZmljYW5kbyB0ZW5lbW9zOg0KYGBge3J9DQpnZW5fc3VtbWFyeSAlPiUNCiAgZ2dwbG90KGFlcyh4PUdlbmVybywgeT1Ub3RhbCkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGZpbGw9YygicGluayIsICJsaWdodCBibHVlIikpDQpgYGANCg0KIyMjIyBBZ3JlZ2Ftb3MgY29sdW1uYSBzaSBlbCBhbHVtbm8gYXByb2JvIG8gbm8gKHZhcmlhYmxlIHBhcmEgY2xhc2lmaWNhY2nzbikNCmBgYHtyfQ0KZGF0YV9zZXQkQXByb2I8LWlmZWxzZSgoZGF0YV9zZXQkTkYgPj0gNjEpLCB5ZXM9MSwgbm89MCkNCmBgYA0KDQojIyMjIEFob3JhIGV4dHJhZW1vcyBsYSBkYXRhIGRlbCBjdXJzbyBkZSBtYXRlbWF0aWNhIHF1ZSBub3MgaW50ZXJlc2E6DQpgYGB7cn0NCiNNYXRlbeF0aWNhIDENCm1hdGUxPC1kYXRhX3NldCAlPiUNCiAgZmlsdGVyKEN1cnNvID09ICJNYXRlbWF0aWNhIEkiKSAlPiUNCiAgZmlsdGVyKEN1cnNvICE9ICJOQSIpDQpgYGANCg0KIyMjIFNlcGFyYXJlbW9zIGxhIGRhdGEgZW4gNzAlIHBhcmEgZW50cmVuYW1pZW50byB5IDMwJSBwYXJhIHBydWViYXM6DQpgYGB7cn0NCiNTdWJzZXRzIGRlIGRhdG9zIHBhcmEgZW50cmVuYW1pZW50byB5IHBydWViYXMgTWF0ZW1hdGljYSAxDQp0cmFpbl9tYXRlMTwtbWF0ZTFbYygxOnJvdW5kKDAuNypucm93KG1hdGUxKSkpLF0NCnRlc3RfbWF0ZTE8LW1hdGUxW2Mocm91bmQoMC43Km5yb3cobWF0ZTEpKSArIDE6IG5yb3cobWF0ZTEpKSxdDQp0ZXN0X21hdGUxPC0gdGVzdF9tYXRlMSAlPiUNCiAgZmlsdGVyKFNlYyAhPSAiTkEiKQ0KYGBgDQoNCiMjI01vZGVsbyAxOiBJbmZsdWVuY2lhIGVuIGxhIE5vdGEgZGVsIFByaW1lciBQYXJjaWFsIGVuIEFwcm9iYXIgZWwgQ3Vyc286DQpgYGB7ciwgZWNobz1UUlVFfQ0KI01vZGVsbyBwYXJhIE1hdGVtYXRpY2EgMToNCm1vZDE8LWdsbShkYXRhPXRyYWluX21hdGUxLCAgZm9ybXVsYSA9IEFwcm9iIH4gRVAxLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rPSJsb2dpdCIpKQ0KDQojQWdyZWdhbmRvIGVsIE1vZGVsbzoNCnh2PC1zZXEobWluKHRyYWluX21hdGUxJEVQMSksIG1heCh0cmFpbl9tYXRlMSRFUDEpLCAwLjAxKQ0KeXY8LXByZWRpY3Qob2JqZWN0PW1vZDEsIGxpc3QoRVAxPXh2KSwgdHlwZT0icmVzcG9uc2UiKQ0KZGVtb19wbG90MTwtZGF0YS5mcmFtZSh4diwgeXYpDQpuYW1lcyhkZW1vX3Bsb3QxKTwtYygiTm90YSIsICJQcm9iYWJpbGlkYWQiKQ0KbG9naXRwbG90XzE8LWdncGxvdChkYXRhPWRlbW9fcGxvdDEsIGFlcyh4PU5vdGEsIHk9UHJvYmFiaWxpZGFkKSkgKyBnZW9tX2xpbmUoKQ0KDQpwbG90bTEgPC0gdHJhaW5fbWF0ZTEgJT4lDQogIGFycmFuZ2UoRVAxKSAlPiUNCiAgZ2dwbG90KGFlcyh4PUVQMSwgeT1BcHJvYikpICsNCiAgZ2VvbV9wb2ludCgpICsgZ2VvbV9saW5lKGRhdGE9ZGVtb19wbG90MSwgYWVzKHg9Tm90YSwgeT1Qcm9iYWJpbGlkYWQpLCBjb2xvcj0iYmx1ZSIpICsNCiAgbGFicyh4ID0gIk5vdGEgUGFyY2lhbCAxIikgKw0KICBsYWJzKHkgPSAiUHJvYmFiaWxpZGFkIikrDQogIGdndGl0bGUoIlByb2JhYmlsaWRhZCBkZSBHYW5hciBlbCBDdXJzbyBlbiBmdW5jafNuIGRlIGxhIE5vdGEgZGUgUGFyY2lhbCAxIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KI0dyYWZpY2FuZG8gdGVuZW1vcy4NCnBsb3RtMQ0KYGBgDQojIyMjIyBBIGNvbnRpbnVhY2nzbiBzZSBtdWVzdHJhIGxhIGluZm9ybWFhY2nzbiBtYXMgaW1wb3J0YW50ZSBkZWwgbW9kZWxvIDE6DQoqIExvcyBjb2VmaWNpZW50ZXMgc29uOg0KICArIEVsIGludGVyY2VwdG8gZGVsIG1vZGVsbyBlcyAqKmByIGNvZWYobW9kMSlbWzFdXWAqKi4NCiAgKyBFbCBjb2VmaWNpZW50ZSBkZWwgbW9kZWxvIGVzICoqYHIgY29lZihtb2QxKVtbMl1dYCoqLg0KKiBQb2RlbW9zIGRlY2lyIHF1ZSBhbWJvcyBjb2VmaWNpZW50ZXMgdGllbmVuIHNpZ25pZmljYWNuY2lhIGVuIGVsIG1vZGVsbyB5YSBxdWUgZWwgdmFsb3IgUCBkZSBhbWJvcyBlcyAqKmByIDJlLTE2YCoqLg0KDQojIyMjIExhIGVjdWNp824gZGVsIG1vZGVsbyBlczoNCiQkIFAoXHNtYWxse0VQMX0pPVxmcmFjezF9ezErZV57LVsoMC4wNzY1KS00LjQ0NyooRVAxKV19fSQkIA0KDQojIyMjIyBBIGNvbnRpbnVhY2nzbiBtb3N0cmF0ZW1vcyB1bmEgcHJlZGljY2lvbmVzIGVuIGJhc2UgYWwgbW9kZWxvIDEgYXN1bWllbmRvIHF1ZSBzaSBsYSBwcm9iYWJpbGlkYWQgZXMgbWF5b3IgYSAqKjAuNSoqLCBlbnRvbmNlcyBlbCBhbHVtbm8gYXByb2JhcmEgZWwgY3Vyc28sIGVsIGZyYW1lIGRlIGRhdG9zIGVzdGEgZm9ybWFkbyBwb3IgbGEgbm90YSBkZWwgcHJpbWVyIHBhcmNpYWwsIGxhIHByb2JhYmlsaWRhZCBvYnRlbmlkYSBwb3IgZWwgbW9kZWxvLCBzaSBlbCBhbHVtbm8gYXByb2JvIG8gbm8gZWwgY3Vyc28geSBsYSBwcmVkaWNjafNuIG9idGVuaWRhIGNvbiBlbCBtb2RlbG8gc2kgbGEgcHJvYmFiaWxpZGFkIGVzIG1heW9yIGEgMC41Og0KYGBge3J9DQpwcmVkaWNjaW9uMTwtcHJlZGljdChvYmplY3Q9bW9kMSwgbmV3ZGF0YT10ZXN0X21hdGUxLCB0eXBlPSJyZXNwb25zZSIpDQpyZXN1bHRhZG8xPC1kYXRhLmZyYW1lKHRlc3RfbWF0ZTEkRVAxLCAoYXMuZGF0YS5mcmFtZShwcmVkaWNjaW9uMSkpJHByZWRpY2Npb24xKQ0KcmVzdWx0YWRvMSRBcHJvYm88LSB0ZXN0X21hdGUxJEFwcm9iICNBZ3JlZ2Ftb3MgY29sdW1uYSBkZWwgcmVzdWx0YWRvDQpuYW1lcyhyZXN1bHRhZG8xKTwtYygiTm90YSIsICJQcm9iYWJpbGlkYWQiLCAiQXByb2JvIENsYXNlIikNCnJlc3VsdGFkbzEkUHJlZGljY2lvbjwtIGlmZWxzZSgocmVzdWx0YWRvMSRQcm9iYWJpbGlkYWQgPj0gMC41KSwgeWVzPTEsIG5vPTApDQpyZXN1bHRhZG8xICU+JSBhcnJhbmdlKE5vdGEpDQpgYGANCiMjIyNDYWxjdWxhbmRvIGVmZWN0aXZpZGFkOg0KYGBge3J9DQphPC0wDQppPC0xDQoNCmZvcihpIGluICgxOm5yb3cocmVzdWx0YWRvMSkpKXsNCiAgaWYocmVzdWx0YWRvMSRgQXByb2JvIENsYXNlYFtbaV1dID09IHJlc3VsdGFkbzEkUHJlZGljY2lvbltbaV1dKXsNCiAgICBhPC1hKzENCiAgfSBlbHNlIHsNCiAgICBhPC1hDQogIH0NCn0NCmVmMTwtYS9ucm93KHJlc3VsdGFkbzEpDQpgYGANCiMjIyMgRWwgbW9kZWxvIDEgdGllbmUgdW4gKipgciBlZjFgKiogZGUgZWZlY3RpdmlkYWQuDQoNCg0KIyMjTW9kZWxvIDI6IEluZmx1ZW5jaWEgZW4gbGEgTm90YSBkZWwgU2VndW5kbyBQYXJjaWFsIGVuIEFwcm9iYXIgZWwgQ3Vyc28gDQpgYGB7cn0NCiNNb2RlbG8gcGFyYSBNYXRlbWF0aWNhIDE6DQptb2QyPC1nbG0oZGF0YT10cmFpbl9tYXRlMSwgIGZvcm11bGEgPSBBcHJvYiB+IEVQMiwgZmFtaWx5ID0gYmlub21pYWwobGluaz0ibG9naXQiKSkNCg0KI0FncmVnYW5kbyBlbCBNb2RlbG86DQp4djwtc2VxKG1pbih0cmFpbl9tYXRlMSRFUDIpLCBtYXgodHJhaW5fbWF0ZTEkRVAyKSwgMC4wMSkNCnl2PC1wcmVkaWN0KG9iamVjdD1tb2QyLCBsaXN0KEVQMj14diksIHR5cGU9InJlc3BvbnNlIikNCmRlbW9fcGxvdDI8LWRhdGEuZnJhbWUoeHYsIHl2KQ0KbmFtZXMoZGVtb19wbG90Mik8LWMoIk5vdGEiLCAiUHJvYmFiaWxpZGFkIikNCmxvZ2l0cGxvdF8yPC1nZ3Bsb3QoZGF0YT1kZW1vX3Bsb3QyLCBhZXMoeD1Ob3RhLCB5PVByb2JhYmlsaWRhZCkpICsgZ2VvbV9saW5lKCkNCg0KcGxvdG0yIDwtIHRyYWluX21hdGUxICU+JQ0KICBhcnJhbmdlKEVQMikgJT4lDQogIGdncGxvdChhZXMoeD1FUDIsIHk9QXByb2IpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fbGluZShkYXRhPWRlbW9fcGxvdDIsIGFlcyh4PU5vdGEsIHk9UHJvYmFiaWxpZGFkKSwgY29sb3I9InJlZCIpICsNCiAgbGFicyh4ID0gIk5vdGEgUGFyY2lhbCAyIikgKw0KICBsYWJzKHkgPSAiUHJvYmFiaWxpZGFkIikrDQogIGdndGl0bGUoIlByb2JhYmlsaWRhZCBkZSBHYW5hciBlbCBDdXJzbyBlbiBmdW5jafNuIGRlIGxhIE5vdGEgZGUgUGFyY2lhbCAyIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KI0dyYWZpY2FuZG8gdGVuZW1vcy4NCnBsb3RtMg0KYGBgDQojIyMjIyBBIGNvbnRpbnVhY2nzbiBzZSBtdWVzdHJhIGxhIGluZm9ybWFhY2nzbiBtYXMgaW1wb3J0YW50ZSBkZWwgbW9kZWxvIDI6DQoqIExvcyBQYXJhbWV0cm9zIGRlbCBtb2RlbG8gc29uOg0KICArIEVsIGludGVyY2VwdG8gZGVsIG1vZGVsbyBlcyAqKmByIGNvZWYobW9kMilbWzFdXWAqKi4NCiAgKyBFbCBjb2VmaWNpZW50ZSBkZWwgbW9kZWxvIGVzICoqYHIgY29lZihtb2QyKVtbMl1dYCoqLg0KKiBQb2RlbW9zIGRlY2lyIHF1ZSBhbWJvcyBwYXJhbWV0cm9zIHRpZW5lbiBzaWduaWZpY2FjbmNpYSBlbiBlbCBtb2RlbG8geWEgcXVlIGVsIHZhbG9yIFAgZGUgYW1ib3MgZXMgKipgciAyZS0xNmAqKi4NCg0KIyMjIyBMYSBlY3VjafNuIGRlbCBtb2RlbG8gZXM6DQokJCBQKFxzbWFsbHtFUDJ9KT1cZnJhY3sxfXsxK2Veey1bKDAuMTA1OSktNS42MTM1KihFUDIpXX19JCQgDQoNCiMjIyMjICMjIyMjIEEgY29udGludWFjafNuIG1vc3RyYXRlbW9zIHVuYSBwcmVkaWNjaW9uZXMgZW4gYmFzZSBhbCBtb2RlbG8gMiBhc3VtaWVuZG8gcXVlIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhICoqMC41KiosIGVudG9uY2VzIGVsIGFsdW1ubyBhcHJvYmFyYSBlbCBjdXJzbywgZWwgZnJhbWUgZGUgZGF0b3MgZXN0YSBmb3JtYWRvIHBvciBsYSBub3RhIGRlbCBwcmltZXIgc2VndW5kbywgbGEgcHJvYmFiaWxpZGFkIG9idGVuaWRhIHBvciBlbCBtb2RlbG8sIHNpIGVsIGFsdW1ubyBhcHJvYm8gbyBubyBlbCBjdXJzbyB5IGxhIHByZWRpY2Np824gb2J0ZW5pZGEgY29uIGVsIG1vZGVsbyBzaSBsYSBwcm9iYWJpbGlkYWQgZXMgbWF5b3IgYSAwLjU6DQpgYGB7cn0NCnByZWRpY2Npb24yPC1wcmVkaWN0KG9iamVjdD1tb2QyLCBuZXdkYXRhPXRlc3RfbWF0ZTEsIHR5cGU9InJlc3BvbnNlIikNCnJlc3VsdGFkbzI8LWRhdGEuZnJhbWUodGVzdF9tYXRlMSRFUDIsIChhcy5kYXRhLmZyYW1lKHByZWRpY2Npb24yKSkkcHJlZGljY2lvbjIpDQpyZXN1bHRhZG8yJEFwcm9ibzwtIHRlc3RfbWF0ZTEkQXByb2IgI0FncmVnYW1vcyBjb2x1bW5hIGRlbCByZXN1bHRhZG8NCm5hbWVzKHJlc3VsdGFkbzIpPC1jKCJOb3RhIiwgIlByb2JhYmlsaWRhZCIsICJBcHJvYm8gQ2xhc2UiKQ0KcmVzdWx0YWRvMiRQcmVkaWNjaW9uPC0gaWZlbHNlKChyZXN1bHRhZG8yJFByb2JhYmlsaWRhZCA+PSAwLjUpLCB5ZXM9MSwgbm89MCkNCnJlc3VsdGFkbzIgJT4lIGFycmFuZ2UoTm90YSkgIyMgT3JkZW5hbW9zIGVsIGFycmVnbG8NCmBgYA0KIyMjI0NhbGN1bGFuZG8gZWZlY3RpdmlkYWQ6DQpgYGB7cn0NCmE8LTANCmk8LTENCg0KZm9yKGkgaW4gKDE6bnJvdyhyZXN1bHRhZG8yKSkpew0KICBpZihyZXN1bHRhZG8yJGBBcHJvYm8gQ2xhc2VgW1tpXV0gPT0gcmVzdWx0YWRvMiRQcmVkaWNjaW9uW1tpXV0pew0KICAgIGE8LWErMQ0KICB9IGVsc2Ugew0KICAgIGE8LWENCiAgfQ0KfQ0KZWYyPC1hL25yb3cocmVzdWx0YWRvMikNCmBgYA0KIyMjIyBFbCBtb2RlbG8gMiB0aWVuZSB1biAqKmByIGVmMmAqKiBkZSBlZmVjdGl2aWRhZC4NCg0KDQojIyNNb2RlbG8gMzogSW5mbHVlbmNpYSBlbiBsYSBOb3RhIGRlbCBFeGFtZW4gRmluYWwgZW4gQXByb2JhciBlbCBDdXJzbyANCmBgYHtyfQ0KI01vZGVsbyBwYXJhIE1hdGVtYXRpY2EgMToNCm1vZDM8LWdsbShkYXRhPXRyYWluX21hdGUxLCAgZm9ybXVsYSA9IEFwcm9iIH4gRUYgLCBmYW1pbHkgPSBiaW5vbWlhbChsaW5rPSJsb2dpdCIpKQ0KDQojQWdyZWdhbmRvIGVsIE1vZGVsbzoNCnh2PC1zZXEobWluKHRyYWluX21hdGUxJEVGKSwgbWF4KHRyYWluX21hdGUxJEVGKSwgMC4wMSkNCnl2PC1wcmVkaWN0KG9iamVjdD1tb2QzLCBsaXN0KEVGPXh2KSwgdHlwZT0icmVzcG9uc2UiKQ0KZGVtb19wbG90MzwtZGF0YS5mcmFtZSh4diwgeXYpDQpuYW1lcyhkZW1vX3Bsb3QzKTwtYygiTm90YSIsICJQcm9iYWJpbGlkYWQiKQ0KbG9naXRwbG90XzM8LWdncGxvdChkYXRhPWRlbW9fcGxvdDMsIGFlcyh4PU5vdGEsIHk9UHJvYmFiaWxpZGFkKSkgKyBnZW9tX2xpbmUoKQ0KYGBgDQoNCiMjIyNHcmFmaWNhbmRvIFRlbmVtb3M6DQpgYGB7ciB9DQpwbG90bTMgPC0gdHJhaW5fbWF0ZTEgJT4lDQogIGFycmFuZ2UoRUYpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9RUYsIHk9QXByb2IpKSArDQogIGdlb21fcG9pbnQoKSArIGdlb21fbGluZShkYXRhPWRlbW9fcGxvdDMsIGFlcyh4PU5vdGEsIHk9UHJvYmFiaWxpZGFkKSwgY29sb3I9ImdyZWVuIikgKw0KICBsYWJzKHggPSAiTm90YSBFeGFtZW4gRmluYWwiKSArDQogIGxhYnMoeSA9ICJQcm9iYWJpbGlkYWQiKSsNCiAgZ2d0aXRsZSgiUHJvYmFiaWxpZGFkIGRlIEdhbmFyIGVsIEN1cnNvIGVuIGZ1bmNp824gZGUgbGEgTm90YSBkZWwgRXhhbWVuIEZpbmFsIikgKw0KICB0aGVtZV9taW5pbWFsKCkNCnBsb3RtMw0KYGBgDQojIyMjIyBBIGNvbnRpbnVhY2nzbiBzZSBtdWVzdHJhIGxhIGluZm9ybWFhY2nzbiBtYXMgaW1wb3J0YW50ZSBkZWwgbW9kZWxvIDM6DQoqIExvcyBQYXJhbWV0cm9zIGRlbCBtb2RlbG8gc29uOg0KICArIEVsIGludGVyY2VwdG8gZGVsIG1vZGVsbyBlcyAqKmByIGNvZWYobW9kMylbWzFdXWAqKi4NCiAgKyBFbCBjb2VmaWNpZW50ZSBkZWwgbW9kZWxvIGVzICoqYHIgY29lZihtb2QzKVtbMl1dYCoqLg0KKiBQb2RlbW9zIGRlY2lyIHF1ZSBhbWJvcyBwYXJhbWV0cm9zIHRpZW5lbiBzaWduaWZpY2FjbmNpYSBlbiBlbCBtb2RlbG8geWEgcXVlIGVsIHZhbG9yIFAgZGUgYW1ib3MgZXMgKipgciAyZS0xNmAqKi4NCg0KIyMjIyBMYSBlY3VjafNuIGRlbCBtb2RlbG8gZXM6DQokJCBQKFxzbWFsbHtFUDJ9KT1cZnJhY3sxfXsxK2Veey1bKDAuMDkzNCktMy45MTAyKihFUDIpXX19JCQNCiMjIyMjICMjIyMjIEEgY29udGludWFjafNuIG1vc3RyYXRlbW9zIHVuYSBwcmVkaWNjaW9uZXMgZW4gYmFzZSBhbCBtb2RlbG8gMyBhc3VtaWVuZG8gcXVlIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhICoqMC41KiosIGVudG9uY2VzIGVsIGFsdW1ubyBhcHJvYmFyYSBlbCBjdXJzbywgZWwgZnJhbWUgZGUgZGF0b3MgZXN0YSBmb3JtYWRvIHBvciBsYSBub3RhIGRlbCBleGFtZW4gZmluYWwsIGxhIHByb2JhYmlsaWRhZCBvYnRlbmlkYSBwb3IgZWwgbW9kZWxvLCBzaSBlbCBhbHVtbm8gYXByb2JvIG8gbm8gZWwgY3Vyc28geSBsYSBwcmVkaWNjafNuIG9idGVuaWRhIGNvbiBlbCBtb2RlbG8gc2kgbGEgcHJvYmFiaWxpZGFkIGVzIG1heW9yIGEgMC41Og0KYGBge3J9DQpwcmVkaWNjaW9uMzwtcHJlZGljdChvYmplY3Q9bW9kMywgbmV3ZGF0YT10ZXN0X21hdGUxLCB0eXBlPSJyZXNwb25zZSIpDQpyZXN1bHRhZG8zPC1kYXRhLmZyYW1lKHRlc3RfbWF0ZTEkRUYsIChhcy5kYXRhLmZyYW1lKHByZWRpY2Npb24zKSkkcHJlZGljY2lvbjMpDQpyZXN1bHRhZG8zJEFwcm9ibzwtIHRlc3RfbWF0ZTEkQXByb2IgI0FncmVnYW1vcyBjb2x1bW5hIGRlbCByZXN1bHRhZG8NCm5hbWVzKHJlc3VsdGFkbzMpPC1jKCJOb3RhIiwgIlByb2JhYmlsaWRhZCIsICJBcHJvYm8gQ2xhc2UiKQ0KcmVzdWx0YWRvMyRQcmVkaWNjaW9uPC0gaWZlbHNlKChyZXN1bHRhZG8zJFByb2JhYmlsaWRhZCA+PSAwLjUpLCB5ZXM9MSwgbm89MCkNCnJlc3VsdGFkbzMgJT4lIGFycmFuZ2UoTm90YSkgIyMgT3JnZW5hbW9zIGVsIGFycmVnbG8NCmBgYA0KIyMjI0NhbGN1bGFuZG8gZWZlY3RpdmlkYWQ6DQpgYGB7cn0NCmE8LTANCmk8LTENCg0KZm9yKGkgaW4gKDE6bnJvdyhyZXN1bHRhZG8zKSkpew0KICBpZihyZXN1bHRhZG8zJGBBcHJvYm8gQ2xhc2VgW1tpXV0gPT0gcmVzdWx0YWRvMyRQcmVkaWNjaW9uW1tpXV0pew0KICAgIGE8LWErMQ0KICB9IGVsc2Ugew0KICAgIGE8LWENCiAgfQ0KfQ0KZWYzPC1hL25yb3cocmVzdWx0YWRvMykNCmBgYA0KIyMjIyBFbCBtb2RlbG8gMyB0aWVuZSB1biAqKmByIGVmM2AqKiBkZSBlZmVjdGl2aWRhZC4NCg0KDQoNCiMjI0dy4WZpY2FzIGRlIFByb2JhYmlsaWRhZDoNCmBgYHtyfQ0KDQp4djwtc2VxKDAsIDEwMCwgMC4wMSkNCnl2MTwtcHJlZGljdChvYmplY3Q9bW9kMSwgbGlzdChFUDE9eHYpLCB0eXBlPSJyZXNwb25zZSIpDQp5djI8LXByZWRpY3Qob2JqZWN0PW1vZDIsIGxpc3QoRVAyPXh2KSwgdHlwZT0icmVzcG9uc2UiKQ0KeXYzPC1wcmVkaWN0KG9iamVjdD1tb2QzLCBsaXN0KEVGPXh2KSwgdHlwZT0icmVzcG9uc2UiKQ0KDQpkZW1vX3Bsb3QxPC1kYXRhLmZyYW1lKHh2LCB5djEpDQpkZW1vX3Bsb3QyPC1kYXRhLmZyYW1lKHh2LCB5djIpDQpkZW1vX3Bsb3QzPC1kYXRhLmZyYW1lKHh2LCB5djMpDQoNCm5hbWVzKGRlbW9fcGxvdDEpPC1jKCJOb3RhIiwgIlByb2JhYmlsaWRhZCIpDQpuYW1lcyhkZW1vX3Bsb3QyKTwtYygiTm90YSIsICJQcm9iYWJpbGlkYWQiKQ0KbmFtZXMoZGVtb19wbG90Myk8LWMoIk5vdGEiLCAiUHJvYmFiaWxpZGFkIikNCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD02LCBmaWcuaGVpZ2h0PTV9DQogdGVzdF9tYXRlMSAlPiUgDQogIGdncGxvdChhZXMoeD1FRiwgeT1BcHJvYikpICsNCiAgZ2VvbV9ibGFuaygpICsgZ2VvbV9saW5lKGRhdGE9ZGVtb19wbG90MSwgYWVzKHg9Tm90YSwgeT1Qcm9iYWJpbGlkYWQpLCBjb2xvcj0iYmx1ZSIpKw0KICBnZW9tX2xpbmUoZGF0YT1kZW1vX3Bsb3QyLCBhZXMoeD1Ob3RhLCB5PVByb2JhYmlsaWRhZCksIGNvbG9yPSJyZWQiKSArDQogIGdlb21fbGluZShkYXRhPWRlbW9fcGxvdDMsIGFlcyh4PU5vdGEsIHk9UHJvYmFiaWxpZGFkKSwgY29sb3I9ImdyZWVuIikgKw0KICBsYWJzKHggPSAiTm90YSBFeGFtZW4gKDAtMTAwKSIpICsNCiAgbGFicyh5ID0gIlByb2JhYmlsaWRhZCIgKSsNCiAgZ2d0aXRsZSgiUHJvYmFiaWxpZGFkIGRlIEdhbmFyIGVsIEN1cnNvIGVuIGZ1bmNp824gZGUgbGEgTm90YSBvYnRlbmlkYSIpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEoMCwxMDAsYnk9MTApKSArDQogIHRoZW1lX21pbmltYWwoKQ0KYGBgDQojIyMjRW4gbGEgZ3JhZmljYSBhbnRlcmlvciBzZSBtdWVzdGFuIGxhIHJlZ3Jlc2nzbiBsb2dpc3RpY2Egc2ltcGxlIGRlIGNhZGEgbW9kZWxvIG9idGVuaWRvIHBhcmEgY29tcGFyYXJsb3MsIGRvbmRlIGxhIGF6dWwgZXMgbGEgcHJvYmFiaWxpZGFkIGRlIGFwcm9iYXIgZWwgY3Vyc28gZW4gZnVuY2nzbiBkZSBsYSBub3RhIGRlbCBwcmltZXIgcGFyY2lhbCwgbGEgcm9qYSBlcyBsYSBwcm9iYWJpbGlkYWQgZGUgZ2FuYXIgZWwgY3Vyc28gZW4gZnVuY2nzbiBkZSBsYSBub3RhIGRlbCBzZWd1bmRvIHBhcmNpYWwsIHkgbGEgdmVyZGUgZXMgbGEgcHJvYmFiaWxpZGFkIGRlIGdhbmFyIGVsIGN1cnNvIGVuIGZ1bmNp824gZGUgbGEgbm90YSBkZWwgZXhhbWVuIGZpbmFsLg0KDQoNCg0KIyMjICoqQWhvcmEgbW9zdHJhcmVtb3MgdHJlcyBtb2RlbG9zIGRlIHJlZ3Jlc2nzbiBsb2dpc3RpY2EgbfpsdGlwbGUsIHV0aWxpemFuZG8gY29tYmluYWNpb25lcyBkZSBkb3MgZXhhbWVucyBwYXJhIGVuY29udHJhciBsYSBwcm9iYWJpbGlkYWQgZGUgcXVlIHVuIGFsdW1ubyBhcHJldWViZS9yZXBydWViZSBsYSBjbGFzZS4qKg0KDQojIyNNb2RlbG8gNDogSW5mbHVlbmNpYSBkZWwgUHJpbWVyIHkgU2VndW5kbyBQYXJjaWFsOg0KYGBge3J9DQpsaWJyYXJ5KHBsb3RseSkNCiNNb2RlbG8gUmVncmVzafNuIExvZ2lzdGljYSBNdWx0aXBsZSwgRXhhbWVuIFBhcmNpYWwgMSB5IEV4YW1lbiBQYXJjaWFsIDI6DQptb2Q0PC1nbG0oZGF0YT10cmFpbl9tYXRlMSwgIGZvcm11bGEgPSBBcHJvYiB+IEVQMSArIEVQMiAsIGZhbWlseSA9IGJpbm9taWFsKGxpbms9ImxvZ2l0IikpDQoNCiNEZWZpbmllbmRvIERvbWluaW86DQp4NDwtc2VxKDAsIDEwMCwgMSkNCnk0PC1zZXEoMCwgMTAwLCAxKQ0KZG9tYWluPC1leHBhbmQuZ3JpZCh4NCx5NCkNCm5hbWVzKGRvbWFpbik8LWMoIkVQMSIsICJFUDIiKQ0KDQp6NDwtcHJlZGljdChtb2Q0LCBkb21haW4sIHR5cGU9InJlc3BvbnNlIikNCmQ8LWRhdGEuZnJhbWUoejQpDQp6NDwtZCR6NA0KZGF0YV94eT1kYXRhLmZyYW1lKGRvbWFpbiRFUDEsIGRvbWFpbiRFUDIsIHo0KQ0KYGBgDQoNCiMjIyNHcmFmaWNhbmRvIFRlbmVtb3M6DQpgYGB7ciBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9N30NCiNwbG90X2x5KHogPSB6NCwgdHlwZSA9ICJzdXJmYWNlIiwgKQ0KcCA8LSBwbG90X2x5KHggPWRvbWFpbiRFUDEsIHkgPSBkb21haW4kRVAyLCB6ID0gejQsIHR5cGUgPSAiaGVhdG1hcCIsIHhheGlzPWRvbWFpbiRFUDEsIHlheGlzPWRvbWFpbiRFUDIsDQogIGNvbG9yYmFyID0gbGlzdCh0aXRsZSA9ICJQcm9iYWJpbGlkYWQiKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdQcm9iYWJpbGlkYWQgZGUgR2FuYXIgZWwgQ3Vyc28gdnJzIFBhcmNpYWwgMSB5IFBhcmNpYWwgMicsIHhheGlzID0gbGlzdCh0aXRsZSA9ICdOb3RhIFBhcmNpYWwgMScpICwgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ05vdGEgUGFyY2lhbCAyJykpDQpwDQpgYGANCiMjIyMjIEEgY29udGludWFjafNuIHNlIG11ZXN0cmEgbGEgaW5mb3JtYWFjafNuIG1hcyBpbXBvcnRhbnRlIGRlbCBtb2RlbG8gNDoNCiogTG9zIFBhcmFtZXRyb3MgZGVsIG1vZGVsbyBzb246DQogICsgRWwgaW50ZXJjZXB0byBkZWwgbW9kZWxvIGVzICoqYHIgY29lZihtb2Q0KVtbMV1dYCoqLg0KICArIEVsIGNvZWZpY2llbnRlIGRlbCBtb2RlbG8gZXMgKipgciBjb2VmKG1vZDQpW1syXV1gKiouDQogICsgRWwgY29lZmljaWVudGUgZGVsIG1vZGVsbyBlcyAqKmByIGNvZWYobW9kNClbWzNdXWAqKi4NCiogUG9kZW1vcyBkZWNpciBxdWUgYW1ib3MgcGFyYW1ldHJvcyB0aWVuZW4gc2lnbmlmaWNhY25jaWEgZW4gZWwgbW9kZWxvIHlhIHF1ZSBlbCB2YWxvciBQIGRlIGFtYm9zIGVzICoqYHIgMmUtMTZgKiouDQoNCiMjIyMgTGEgZWN1Y2nzbiBkZWwgbW9kZWxvIGVzOg0KJCQgUChcc21hbGx7RVAxLCBFUDJ9KT1cZnJhY3sxfXsxK2Veey1bLTguNjM5OCswLjA1ODIqKEVQMSkrMC4wOTkzMSooRVAyKV19fSQkDQojIyMjI0EgY29udGludWFjafNuIG1vc3RyYXRlbW9zIHVuYSBwcmVkaWNjaW9uZXMgZW4gYmFzZSBhbCBtb2RlbG8gNCBhc3VtaWVuZG8gcXVlIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhICoqMC41KiosIGVudG9uY2VzIGVsIGFsdW1ubyBhcHJvYmFyYSBlbCBjdXJzbywgZWwgZnJhbWUgZGUgZGF0b3MgZXN0YSBmb3JtYWRvIHBvciBsYSBub3RhIGRlbCBwcmltZXIgcGFyY2lhbCwgbGEgbm90YSBkZWwgc2VndW5kbyBwYXJjaWFsLCBsYSBwcm9iYWJpbGlkYWQgb2J0ZW5pZGEgcG9yIGVsIG1vZGVsbywgc2kgZWwgYWx1bW5vIGFwcm9ibyBvIG5vIGVsIGN1cnNvIHkgbGEgcHJlZGljY2nzbiBvYnRlbmlkYSBjb24gZWwgbW9kZWxvIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhIDAuNToNCmBgYHtyfQ0KcHJlZGljY2lvbjQ8LXByZWRpY3Qob2JqZWN0PW1vZDQsIG5ld2RhdGE9dGVzdF9tYXRlMSwgdHlwZT0icmVzcG9uc2UiKQ0KcmVzdWx0YWRvNDwtZGF0YS5mcmFtZSh0ZXN0X21hdGUxJEVQMSwgdGVzdF9tYXRlMSRFUDIsIChhcy5kYXRhLmZyYW1lKHByZWRpY2Npb240KSkkcHJlZGljY2lvbjQpDQpyZXN1bHRhZG80JEFwcm9ibzwtIHRlc3RfbWF0ZTEkQXByb2IgI0FncmVnYW1vcyBjb2x1bW5hIGRlbCByZXN1bHRhZG8NCm5hbWVzKHJlc3VsdGFkbzQpPC1jKCJOb3RhIEVQMSIsICJOb3RhIEVQMiIsICJQcm9iYWJpbGlkYWQiLCAiQXByb2JvIENsYXNlIikNCnJlc3VsdGFkbzQkUHJlZGljY2lvbjwtIGlmZWxzZSgocmVzdWx0YWRvNCRQcm9iYWJpbGlkYWQgPj0gMC41KSwgeWVzPTEsIG5vPTApDQpyZXN1bHRhZG80ICU+JSBhcnJhbmdlKGBOb3RhIEVQMWApICMjIE9yZ2VuYW1vcyBlbCBhcnJlZ2xvDQpgYGANCiMjIyNDYWxjdWxhbmRvIGVmZWN0aXZpZGFkOg0KYGBge3J9DQpiPC0wDQppPC0xDQoNCmZvcihpIGluICgxOm5yb3cocmVzdWx0YWRvNCkpKXsNCiAgaWYocmVzdWx0YWRvNCRgQXByb2JvIENsYXNlYFtbaV1dID09IHJlc3VsdGFkbzQkUHJlZGljY2lvbltbaV1dKXsNCiAgICBiPC1iKzENCiAgfSBlbHNlIHsNCiAgICBiPC1iDQogIH0NCn0NCmVmNDwtYi9ucm93KHJlc3VsdGFkbzQpDQpgYGANCiMjIyMgRWwgbW9kZWxvIDQgdGllbmUgdW4gKipgciBlZjRgKiogZGUgZWZlY3RpdmlkYWQuDQoNCg0KDQojIyNNb2RlbG8gNTogSW5mbHVlbmNpYSBkZWwgUHJpbWVyIFBhcmNpYWwgeSBFeGFtZW4gRmluYWw6DQpgYGB7cn0NCiNNb2RlbG8gcGFyYSBNYXRlbWF0aWNhIDE6DQptb2Q1PC1nbG0oZGF0YT10cmFpbl9tYXRlMSwgIGZvcm11bGEgPSBBcHJvYiB+IEVQMSArIEVGICwgZmFtaWx5ID0gYmlub21pYWwobGluaz0ibG9naXQiKSkNCg0KI0FncmVnYW5kbyBlbCBNb2RlbG86DQoNCmRvbWFpbjwtZXhwYW5kLmdyaWQoeDQseTQpDQp4NDwtc2VxKDAsIDEwMCwgMSkNCnk0PC1zZXEoMCwgMTAwLCAxKQ0KbmFtZXMoZG9tYWluKTwtYygiRVAxIiwgIkVGIikNCg0KejQ8LXByZWRpY3QobW9kNSwgZG9tYWluLCB0eXBlPSJyZXNwb25zZSIpDQpkPC1kYXRhLmZyYW1lKHo0KQ0KejQ8LWQkejQNCmRhdGFfeHk9ZGF0YS5mcmFtZShkb21haW4kRVAxLCBkb21haW4kRUYsIHo0KQ0KYGBgDQoNCiMjIyMgR3JhZmljYW5kbyB0ZW5lbW9zOg0KYGBge3IgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTd9DQojcGxvdF9seSh6ID0gejQsIHR5cGUgPSAic3VyZmFjZSIsICkNCnAgPC0gcGxvdF9seSh4ID1kb21haW4kRVAxLCB5ID0gZG9tYWluJEVGLCB6ID0gejQsIHR5cGUgPSAiaGVhdG1hcCIsIHhheGlzPWRvbWFpbiRFUDEsIHlheGlzPWRvbWFpbiRFRiwNCiAgY29sb3JiYXIgPSBsaXN0KHRpdGxlID0gIlByb2JhYmlsaWRhZCIpKSAlPiUNCiAgbGF5b3V0KHRpdGxlID0gJ1Byb2JhYmlsaWRhZCBkZSBHYW5hciBlbCBDdXJzbyB2cnMgUGFyY2lhbCAxIHkgRmluYWwnLCB4YXhpcyA9IGxpc3QodGl0bGUgPSAnTm90YSBQYXJjaWFsIDEnKSAsIHlheGlzID0gbGlzdCh0aXRsZSA9ICdOb3RhIEV4YW1lbiBGaW5hbCcpKQ0KcA0KYGBgDQojIyMjIyBBIGNvbnRpbnVhY2nzbiBzZSBtdWVzdHJhIGxhIGluZm9ybWFhY2nzbiBtYXMgaW1wb3J0YW50ZSBkZWwgbW9kZWxvIDU6DQoqIExvcyBQYXJhbWV0cm9zIGRlbCBtb2RlbG8gc29uOg0KICArIEVsIGludGVyY2VwdG8gZGVsIG1vZGVsbyBlcyAqKmByIGNvZWYobW9kNSlbWzFdXWAqKi4NCiAgKyBFbCBjb2VmaWNpZW50ZSBkZWwgbW9kZWxvIGVzICoqYHIgY29lZihtb2Q1KVtbMl1dYCoqLg0KICArIEVsIGNvZWZpY2llbnRlIGRlbCBtb2RlbG8gZXMgKipgciBjb2VmKG1vZDUpW1szXV1gKiouDQoqIFBvZGVtb3MgZGVjaXIgcXVlIGFtYm9zIHBhcmFtZXRyb3MgdGllbmVuIHNpZ25pZmljYWNuY2lhIGVuIGVsIG1vZGVsbyB5YSBxdWUgZWwgdmFsb3IgUCBkZSBhbWJvcyBlcyAqKiQyKjEwXnstMTZ9JCoqLg0KDQojIyMjIExhIGVjdWNp824gZGVsIG1vZGVsbyBlczoNCiQkIFAoXHNtYWxse0VQMSwgRUZ9KT1cZnJhY3sxfXsxK2Veey1bLTcuOTk1NCswLjA2ODIxKihFUDEpKzAuMDkzNSooRUYpXX19JCQNCiMjIyMjIEEgY29udGludWFjafNuIG1vc3RyYXRlbW9zIHVuYSBwcmVkaWNjaW9uZXMgZW4gYmFzZSBhbCBtb2RlbG8gNSBhc3VtaWVuZG8gcXVlIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhICoqMC41KiosIGVudG9uY2VzIGVsIGFsdW1ubyBhcHJvYmFyYSBlbCBjdXJzbywgZWwgZnJhbWUgZGUgZGF0b3MgZXN0YSBmb3JtYWRvIHBvciBsYSBub3RhIGRlbCBwcmltZXIgcGFyY2lhbCwgbGEgbm90YSBkZWwgZXhhbWVuIGZpbmFsLCBsYSBwcm9iYWJpbGlkYWQgb2J0ZW5pZGEgcG9yIGVsIG1vZGVsbywgc2kgZWwgYWx1bW5vIGFwcm9ibyBvIG5vIGVsIGN1cnNvIHkgbGEgcHJlZGljY2nzbiBvYnRlbmlkYSBjb24gZWwgbW9kZWxvIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhIDAuNTo6DQpgYGB7cn0NCnByZWRpY2Npb241PC1wcmVkaWN0KG9iamVjdD1tb2Q1LCBuZXdkYXRhPXRlc3RfbWF0ZTEsIHR5cGU9InJlc3BvbnNlIikNCnJlc3VsdGFkbzU8LWRhdGEuZnJhbWUodGVzdF9tYXRlMSRFUDEsIHRlc3RfbWF0ZTEkRUYsIChhcy5kYXRhLmZyYW1lKHByZWRpY2Npb241KSkkcHJlZGljY2lvbjUpDQpyZXN1bHRhZG81JEFwcm9ibzwtIHRlc3RfbWF0ZTEkQXByb2IgI0FncmVnYW1vcyBjb2x1bW5hIGRlbCByZXN1bHRhZG8NCm5hbWVzKHJlc3VsdGFkbzUpPC1jKCJOb3RhIEVQMSIsICJOb3RhIEVGIiwgIlByb2JhYmlsaWRhZCIsICJBcHJvYm8gQ2xhc2UiKQ0KcmVzdWx0YWRvNSRQcmVkaWNjaW9uPC0gaWZlbHNlKChyZXN1bHRhZG81JFByb2JhYmlsaWRhZCA+PSAwLjUpLCB5ZXM9MSwgbm89MCkNCnJlc3VsdGFkbzUgJT4lIGFycmFuZ2UoYE5vdGEgRVAxYCkgIyMgT3JnZW5hbW9zIGVsIGFycmVnbG8NCmBgYA0KIyMjI0NhbGN1bGFuZG8gZWZlY3RpdmlkYWQ6DQpgYGB7cn0NCmE8LTANCmk8LTENCg0KZm9yKGkgaW4gKDE6bnJvdyhyZXN1bHRhZG81KSkpew0KICBpZihyZXN1bHRhZG81JGBBcHJvYm8gQ2xhc2VgW1tpXV0gPT0gcmVzdWx0YWRvNSRQcmVkaWNjaW9uW1tpXV0pew0KICAgIGE8LWErMQ0KICB9IGVsc2Ugew0KICAgIGE8LWENCiAgfQ0KfQ0KZWY1PC1hL25yb3cocmVzdWx0YWRvNSkNCmBgYA0KIyMjIyBFbCBtb2RlbG8gNSB0aWVuZSB1biAqKmByIGVmNWAqKiBkZSBlZmVjdGl2aWRhZC4NCg0KDQojIyNNb2RlbG8gNjogSW5mbHVlbmNpYSBkZWwgU2VndW5kbyBQYXJjaWFsIHkgRXhhbWVuIEZpbmFsOg0KYGBge3J9DQojTW9kZWxvIHBhcmEgTWF0ZW1hdGljYSAxOg0KbW9kNjwtZ2xtKGRhdGE9dHJhaW5fbWF0ZTEsICBmb3JtdWxhID0gQXByb2IgfiBFUDIgKyBFRiAsIGZhbWlseSA9IGJpbm9taWFsKGxpbms9ImxvZ2l0IikpDQoNCiNBZ3JlZ2FuZG8gZWwgTW9kZWxvOg0KDQpkb21haW48LWV4cGFuZC5ncmlkKHg0LHk0KQ0KeDQ8LXNlcSgwLCAxMDAsIDEpDQp5NDwtc2VxKDAsIDEwMCwgMSkNCm5hbWVzKGRvbWFpbik8LWMoIkVQMiIsICJFRiIpDQoNCno0PC1wcmVkaWN0KG1vZDYsIGRvbWFpbiwgdHlwZT0icmVzcG9uc2UiKQ0KZDwtZGF0YS5mcmFtZSh6NCkNCno0PC1kJHo0DQpkYXRhX3h5PWRhdGEuZnJhbWUoZG9tYWluJEVQMiwgZG9tYWluJEVGLCB6NCkNCmBgYA0KIyMjI0dyYWZpY2FuZG8gVGVuZW1vczoNCmBgYHtyIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD03fQ0KI3Bsb3RfbHkoeiA9IHo0LCB0eXBlID0gInN1cmZhY2UiLCApDQpwIDwtIHBsb3RfbHkoeCA9ZG9tYWluJEVQMiwgeSA9IGRvbWFpbiRFRiwgeiA9IHo0LCB0eXBlID0gImhlYXRtYXAiLCB4YXhpcz1kb21haW4kRVAyLCB5YXhpcz1kb21haW4kRUYsDQogIGNvbG9yYmFyID0gbGlzdCh0aXRsZSA9ICJQcm9iYWJpbGlkYWQiKSkgJT4lDQogIGxheW91dCh0aXRsZSA9ICdQcm9iYWJpbGlkYWQgZGUgR2FuYXIgZWwgQ3Vyc28gdnJzIFBhcmNpYWwgMiB5IEZpbmFsJywgeGF4aXMgPSBsaXN0KHRpdGxlID0gJ05vdGEgUGFyY2lhbCAyJykgLCB5YXhpcyA9IGxpc3QodGl0bGUgPSAnTm90YSBFeGFtZW4gRmluYWwnKSkNCnANCmBgYA0KIyMjIyMgQSBjb250aW51YWNp824gc2UgbXVlc3RyYSBsYSBpbmZvcm1hYWNp824gbWFzIGltcG9ydGFudGUgZGVsIG1vZGVsbyA2Og0KKiBMb3MgUGFyYW1ldHJvcyBkZWwgbW9kZWxvIHNvbjoNCiAgKyBFbCBpbnRlcmNlcHRvIGRlbCBtb2RlbG8gZXMgKipgciBjb2VmKG1vZDYpW1sxXV1gKiouDQogICsgRWwgY29lZmljaWVudGUgZGVsIG1vZGVsbyBlcyAqKmByIGNvZWYobW9kNilbWzJdXWAqKi4NCiAgKyBFbCBjb2VmaWNpZW50ZSBkZWwgbW9kZWxvIGVzICoqYHIgY29lZihtb2Q2KVtbM11dYCoqLg0KKiBQb2RlbW9zIGRlY2lyIHF1ZSBhbWJvcyBwYXJhbWV0cm9zIHRpZW5lbiBzaWduaWZpY2FjbmNpYSBlbiBlbCBtb2RlbG8geWEgcXVlIGVsIHZhbG9yIFAgZGUgYW1ib3MgZXMgKiokMioxMF57LTE2fSQqKi4NCg0KIyMjIyBMYSBlY3VjafNuIGRlbCBtb2RlbG8gZXM6DQokJCBQKFxzbWFsbHtFUDIsIEVGfSk9XGZyYWN7MX17MStlXnstWy05Ljg1MTYrMC4xMDk4KihFUDIpKzAuMDkyODIqKEVGKV19fSQkDQojIyMjI0EgY29udGludWFjafNuIG1vc3RyYXRlbW9zIHVuYSBwcmVkaWNjaW9uZXMgZW4gYmFzZSBhbCBtb2RlbG8gNCBhc3VtaWVuZG8gcXVlIHNpIGxhIHByb2JhYmlsaWRhZCBlcyBtYXlvciBhICoqMC41KiosIGVudG9uY2VzIGVsIGFsdW1ubyBhcHJvYmFyYSBlbCBjdXJzbywgZWwgZnJhbWUgZGUgZGF0b3MgZXN0YSBmb3JtYWRvIHBvciBsYSBub3RhIGRlbCBzZWd1bmRvIHBhcmNpYWwsIGxhIG5vdGEgZGVsIGV4YW1lbiBmaW5hbCwgbGEgcHJvYmFiaWxpZGFkIG9idGVuaWRhIHBvciBlbCBtb2RlbG8sIHNpIGVsIGFsdW1ubyBhcHJvYm8gbyBubyBlbCBjdXJzbyB5IGxhIHByZWRpY2Np824gb2J0ZW5pZGEgY29uIGVsIG1vZGVsbyBzaSBsYSBwcm9iYWJpbGlkYWQgZXMgbWF5b3IgYSAwLjU6Og0KYGBge3J9DQpwcmVkaWNjaW9uNjwtcHJlZGljdChvYmplY3Q9bW9kNiwgbmV3ZGF0YT10ZXN0X21hdGUxLCB0eXBlPSJyZXNwb25zZSIpDQpyZXN1bHRhZG82PC1kYXRhLmZyYW1lKHRlc3RfbWF0ZTEkRVAyLCB0ZXN0X21hdGUxJEVGLCAoYXMuZGF0YS5mcmFtZShwcmVkaWNjaW9uNikpJHByZWRpY2Npb242KQ0KcmVzdWx0YWRvNiRBcHJvYm88LSB0ZXN0X21hdGUxJEFwcm9iICNBZ3JlZ2Ftb3MgY29sdW1uYSBkZWwgcmVzdWx0YWRvDQpuYW1lcyhyZXN1bHRhZG82KTwtYygiTm90YSBFUDEiLCAiTm90YSBFRiIsICJQcm9iYWJpbGlkYWQiLCAiQXByb2JvIENsYXNlIikNCnJlc3VsdGFkbzYkUHJlZGljY2lvbjwtIGlmZWxzZSgocmVzdWx0YWRvNiRQcm9iYWJpbGlkYWQgPj0gMC41KSwgeWVzPTEsIG5vPTApDQpyZXN1bHRhZG82ICU+JSBhcnJhbmdlKGBOb3RhIEVQMWApICMjIE9yZ2VuYW1vcyBlbCBhcnJlZ2xvDQpgYGANCiMjIyNDYWxjdWxhbmRvIGVmZWN0aXZpZGFkOg0KYGBge3J9DQphPC0wDQppPC0xDQoNCmZvcihpIGluICgxOm5yb3cocmVzdWx0YWRvNikpKXsNCiAgaWYocmVzdWx0YWRvNiRgQXByb2JvIENsYXNlYFtbaV1dID09IHJlc3VsdGFkbzYkUHJlZGljY2lvbltbaV1dKXsNCiAgICBhPC1hKzENCiAgfSBlbHNlIHsNCiAgICBhPC1hDQogIH0NCn0NCmVmNjwtYS9ucm93KHJlc3VsdGFkbzUpDQpgYGANCiMjIyMgRWwgbW9kZWxvIDEgdGllbmUgdW4gKipgciBlZjZgKiogZGUgZWZlY3RpdmlkYWQuDQoNCiMjI0NvbmNsaXNpb25lczoNCiMjIyMgRGUgbG9zIG1vZGVsb3MgbW9zdHJhZG9zIGFudGVyaW9ybWVudGUsIHBvZGVtb3Mgbm90YXIgcXVlIGVuIGdlbmVyYWwgdG9kb3MgbG9zIGV4YW1lbnMgdGllbmUgdW5hIGluZmx1ZW5jaWEgaW1wb3J0YW50ZSBlbiBhcHJvYmFyIGVsIGN1cnNvIGRlIG1hdGVtYXRpY2EgbyBubywgc2luIGVtYmFyZ28sIGVzIGludGVyZXNhbnRlIHZlciBxdWUgbGEgZWZpY2llbmNpYSBkZSBsb3MgbW9kZWxvcyBxdWUgaW52b2x1Y3JhbiBhbCBleGFtZW4gZmluYWwgc29uIHN1cGVyaW9yZXMsIHBvciB0YW50byBlcyB2aXRhbCBxdWUgbG9zIGFsdW1ub3MgbGUgcG9uZ2FuIG1hcyBlbXBl8W8gYWwgZXhhbWVuIGZpbmFsLCBzZWf6biBudWVzdHJvIGVzdHVkaW8uDQo=