El proyecto de econometría trata de poner en prácica los conocimientos adquiridos durante el curso, para esto se ingresó a la competencia “Pump it Up: Data Mining the Water Table”.

Esta competencia utiliza data de “taarifa” y de “Tanzanian Ministry of Water” el objetivo es predecir cuales de las bombas de agua son funcionales, cuales necesitan reparaciones y cuales no funcionan. El entendimiento de esta data puede ayudar a prevenir fallas, a generar una serie de operaciones de mantenimiento con el objetivo de poder tener disponibilidad de agua potable en las comunidades en Tanzania.

Como ya se dijo, el objetivo es la predicción de la condición de las bombas de agua, algunas de las variables que el DataSet contiene son:

  1. id: identificación de cada pozo
  2. gps_height: Altitud del pozo
  3. longitude & latitude : longitud y latitud (Coordenadas GPS)
  4. region: Región geográfica del pozo
  5. population: cantidadde población en los alrededores del pozo
  6. construction_year: Año del construcción ddel pozo
  7. extraction_type: Forma de extracción utilizada en el pozo
  8. managemente: Como es manejado el pozo
  9. payment: Costo del agua
  10. quality_gropu: Calidad del agua extraída del pozo
  11. quantity_group: Cantidad de agua extraída del pozo
  12. source: La fuente del agua
  13. waterpoint_type: Tipo de punto de agua

Como primer paso, se van a importar las librerías a utilizar

La Data consta de 3 partes, “train_values” en dónde se encuentran las variables independientes (las que se utilizarán para la predicción), “train_labels” en dónde se encuentra la variable dependiente (aquella que se va a predecir) y “test_values” en dónde se encuentran las variables independientes para el test

train_value<-read.csv("train_values.csv", stringsAsFactors = FALSE, na.strings = "")
train_labels<-read.csv("train_labels.csv", stringsAsFactors = FALSE, na.strings = "")
test<-read.csv("test_values.csv", stringsAsFactors = FALSE, na.strings = "")

Se unirá en un solo dataframe el train y el test, el modelo de predicción generado más adelante utiliza factores y para evitar que algunas de las variables en el train y el test no coincidan, se convertirán a factores antes de volverlos a separar y unir el train (labels y value). También se observa que cuenta con muchas variables, algunas de las cuales se repiten por diversas situaciones (previamente visualizado), por lo que se reducirá el dataframe con aquellas que son únicas y necesarias.

#Unir todo la data
all_data <- bind_rows(train_value,test)
#Reducir el dataframe
all_data <- select(all_data,id,amount_tsh,gps_height,longitude,latitude,basin,region,population,
                    construction_year,extraction_type_class,management_group,payment_type,
                    quality_group,quantity_group,source_class,waterpoint_type_group)
#convertir a factores
all_data$basin <- as.factor(all_data$basin)
all_data$region<- as.factor(all_data$region)
all_data$extraction_type_class<- as.factor(all_data$extraction_type_class)
all_data$management_group<- as.factor(all_data$management_group)
all_data$payment_type<- as.factor(all_data$payment_type)
all_data$quality_group<- as.factor(all_data$quality_group)
all_data$quantity_group<- as.factor(all_data$quantity_group)
all_data$source_class<- as.factor(all_data$source_class)
all_data$waterpoint_type_group<- as.factor(all_data$waterpoint_type_group)
#Train-Test
train <-right_join(all_data[0:59400,],train_labels,by="id")
train$status_group<-as.factor(train$status_group)
test<-all_data[59401:nrow(all_data),]
rm(train_value)
rm(train_labels)
rm(all_data)

Observar el DataSet (train)

glimpse(train)
Observations: 59,400
Variables: 17
$ id                    <int> 69572, 8776, 34310, 67743, 19728, 9944, 19816, 54551, 53934, 4614...
$ amount_tsh            <dbl> 6000, 0, 25, 0, 0, 20, 0, 0, 0, 0, 0, 200, 0, 0, 0, 0, 500, 0, 0,...
$ gps_height            <int> 1390, 1399, 686, 263, 0, 0, 0, 0, 0, 0, 62, 1062, 0, 1368, 0, 164...
$ longitude             <dbl> 34.93809, 34.69877, 37.46066, 38.48616, 31.13085, 39.17280, 33.36...
$ latitude              <dbl> -9.85632177, -2.14746569, -3.82132853, -11.15529772, -1.82535885,...
$ basin                 <fctr> Lake Nyasa, Lake Victoria, Pangani, Ruvuma / Southern Coast, Lak...
$ region                <fctr> Iringa, Mara, Manyara, Mtwara, Kagera, Tanga, Shinyanga, Shinyan...
$ population            <int> 109, 280, 250, 58, 0, 1, 0, 0, 0, 0, 345, 250, 0, 1, 0, 200, 35, ...
$ construction_year     <int> 1999, 2010, 2009, 1986, 0, 2009, 0, 0, 0, 0, 2011, 1987, 0, 2009,...
$ extraction_type_class <fctr> gravity, gravity, gravity, submersible, gravity, submersible, ha...
$ management_group      <fctr> user-group, user-group, user-group, user-group, other, user-grou...
$ payment_type          <fctr> annually, never pay, per bucket, never pay, never pay, per bucke...
$ quality_group         <fctr> good, good, good, good, good, salty, good, milky, salty, good, s...
$ quantity_group        <fctr> enough, insufficient, enough, dry, seasonal, enough, enough, eno...
$ source_class          <fctr> groundwater, surface, surface, groundwater, surface, unknown, gr...
$ waterpoint_type_group <fctr> communal standpipe, communal standpipe, communal standpipe, comm...
$ status_group          <fctr> functional, functional, functional, non functional, functional, ...
table(train$status_group)

             functional functional needs repair          non functional 
                  32259                    4317                   22824 

Observar si existe alguna de las variables independientes que contenga data no válida

sapply(train, num_na)
                   id            amount_tsh            gps_height             longitude 
                    0                     0                     0                     0 
             latitude                 basin                region            population 
                    0                     0                     0                     0 
    construction_year extraction_type_class      management_group          payment_type 
                    0                     0                     0                     0 
        quality_group        quantity_group          source_class waterpoint_type_group 
                    0                     0                     0                     0 
         status_group 
                    0 
sapply(test, num_na)
                   id            amount_tsh            gps_height             longitude 
                    0                     0                     0                     0 
             latitude                 basin                region            population 
                    0                     0                     0                     0 
    construction_year extraction_type_class      management_group          payment_type 
                    0                     0                     0                     0 
        quality_group        quantity_group          source_class waterpoint_type_group 
                    0                     0                     0                     0 

Utilizar gráficas para visualizar características de la data

train %>%
    ggplot(aes(reorder(basin),fill=status_group))+
    geom_bar()

train %>%
    ggplot(aes(reorder(extraction_type_class),fill=status_group))+
    geom_bar()

train %>%
    ggplot(aes(reorder(management_group),fill=status_group))+
    geom_bar()

train %>%
    ggplot(aes(reorder(quality_group),fill=status_group))+
    geom_bar()

train %>%
    ggplot(aes(reorder(quantity_group),fill=status_group))+
    geom_bar()

train %>%
    ggplot(aes(reorder(source_class),fill=status_group))+
    geom_bar()

train %>%
    ggplot(aes(reorder(waterpoint_type_group),fill=status_group))+
    geom_bar()

Mapear longitud y latitud en un plano cartesiano y observar el estrado de los pozos

train%>%
    subset(latitude < 0 & longitude > 0)%>%
    ggplot(aes(x = latitude, y = longitude, color = status_group)) + 
    geom_point()

Para generar el modelo de predicción, se utilizarán árboles, en este caso, se trata de generar una clasificación no binaria, dado esto, se utiliza la librería randomForest con la cual se pueden realizar clasificaciones para un máximo de 32 clases.

set.seed(100)
#Generar modelo
modelo <- randomForest(status_group~longitude+latitude+extraction_type_class+
                               quality_group+quantity_group+waterpoint_type_group+
                               construction_year+payment_type+amount_tsh+source_class, 
                             data = train, importance = TRUE, ntree = 30, nodesize = 3)
print(modelo)

Call:
 randomForest(formula = status_group ~ longitude + latitude +      extraction_type_class + quality_group + quantity_group +      waterpoint_type_group + construction_year + payment_type +      amount_tsh + source_class, data = train, importance = TRUE,      ntree = 30, nodesize = 3) 
               Type of random forest: classification
                     Number of trees: 30
No. of variables tried at each split: 3

        OOB estimate of  error rate: 20.65%
Confusion matrix:
                        functional functional needs repair non functional class.error
functional                   29062                     487           2710  0.09910413
functional needs repair       2634                     952            731  0.77947649
non functional                5452                     254          17118  0.25000000
#Generar predicciones
prediccion <- predict(object= modelo,newdata= test)
prediccion <- data.frame(id = test$id, status_group = prediccion)
write.csv(prediccion, file="Prediccion.csv",row.names = FALSE)

En la competencia no existe la posibilidad de adjuntar lo que se ha realizado, por lo tanto, adjunto un screenshot de la puntuación y la posición obtenida.

—-Función para verificar la cantidad de NA en los dataFrames—-

num_na <- function(x){
  sum(is.na(x))
}

—-Función para reordernar la data—-

reorder <- function(x) {
  factor(x, levels = names(sort(table(x))))
}
LS0tDQp0aXRsZTogIlByb3llY3RvIEZpbmFsIEVjb25vbWV0cu1hIg0Kc3VidGl0bGU6ICJNYXLtYSBBbGVqYW5kcmEgQ2VybWXxbyBHb2564Wxleg0KICAgICAgICAgICAxMjAwMjI5NA0KICAgICAgICAgICAxNi9EaWNpZW1icmUvMjAxNg0KICAgICAgICBQVU1QIElUIFVQOiBNaW5pbmcgdGhlIFdhdGVyIFRhYmxlIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KRWwgcHJveWVjdG8gZGUgZWNvbm9tZXRy7WEgdHJhdGEgZGUgcG9uZXIgZW4gcHLhY2ljYSBsb3MgY29ub2NpbWllbnRvcyBhZHF1aXJpZG9zIGR1cmFudGUgZWwgY3Vyc28sIHBhcmEgZXN0byBzZSBpbmdyZXPzIGEgbGEgY29tcGV0ZW5jaWEgIlB1bXAgaXQgVXA6IERhdGEgTWluaW5nIHRoZSBXYXRlciBUYWJsZSIuDQoNCkVzdGEgY29tcGV0ZW5jaWEgdXRpbGl6YSBkYXRhIGRlICJ0YWFyaWZhIiB5IGRlICJUYW56YW5pYW4gTWluaXN0cnkgb2YgV2F0ZXIiIGVsIG9iamV0aXZvIGVzIHByZWRlY2lyIGN1YWxlcyBkZSBsYXMgYm9tYmFzIGRlIGFndWEgc29uIGZ1bmNpb25hbGVzLCBjdWFsZXMgbmVjZXNpdGFuIHJlcGFyYWNpb25lcyB5IGN1YWxlcyBubyBmdW5jaW9uYW4uIEVsIGVudGVuZGltaWVudG8gZGUgZXN0YSBkYXRhIHB1ZWRlIGF5dWRhciBhIHByZXZlbmlyIGZhbGxhcywgYSBnZW5lcmFyIHVuYSBzZXJpZSBkZSBvcGVyYWNpb25lcyBkZSBtYW50ZW5pbWllbnRvIGNvbiBlbCBvYmpldGl2byBkZSBwb2RlciB0ZW5lciBkaXNwb25pYmlsaWRhZCBkZSBhZ3VhIHBvdGFibGUgZW4gbGFzIGNvbXVuaWRhZGVzIGVuIFRhbnphbmlhLg0KDQpDb21vIHlhIHNlIGRpam8sIGVsIG9iamV0aXZvIGVzIGxhIHByZWRpY2Np824gZGUgbGEgY29uZGljafNuIGRlIGxhcyBib21iYXMgZGUgYWd1YSwgYWxndW5hcyBkZSBsYXMgdmFyaWFibGVzIHF1ZSBlbCBEYXRhU2V0IGNvbnRpZW5lIHNvbjoNCg0KMS4gaWQ6IGlkZW50aWZpY2FjafNuIGRlIGNhZGEgcG96bw0KMi4gZ3BzX2hlaWdodDogQWx0aXR1ZCBkZWwgcG96bw0KMy4gbG9uZ2l0dWRlICYgbGF0aXR1ZGUgOiBsb25naXR1ZCB5IGxhdGl0dWQgKENvb3JkZW5hZGFzIEdQUykNCjQuIHJlZ2lvbjogUmVnafNuIGdlb2dy4WZpY2EgZGVsIHBvem8NCjUuIHBvcHVsYXRpb246IGNhbnRpZGFkZGUgcG9ibGFjafNuIGVuIGxvcyBhbHJlZGVkb3JlcyBkZWwgcG96bw0KNi4gY29uc3RydWN0aW9uX3llYXI6IEHxbyBkZWwgY29uc3RydWNjafNuIGRkZWwgcG96bw0KNy4gZXh0cmFjdGlvbl90eXBlOiBGb3JtYSBkZSBleHRyYWNjafNuIHV0aWxpemFkYSBlbiBlbCBwb3pvDQo4LiBtYW5hZ2VtZW50ZTogQ29tbyBlcyBtYW5lamFkbyBlbCBwb3pvDQo5LiBwYXltZW50OiBDb3N0byBkZWwgYWd1YQ0KMTAuIHF1YWxpdHlfZ3JvcHU6IENhbGlkYWQgZGVsIGFndWEgZXh0cmHtZGEgZGVsIHBvem8NCjExLiBxdWFudGl0eV9ncm91cDogQ2FudGlkYWQgZGUgYWd1YSBleHRyYe1kYSBkZWwgcG96bw0KMTIuIHNvdXJjZTogTGEgZnVlbnRlIGRlbCBhZ3VhDQoxMy4gd2F0ZXJwb2ludF90eXBlOiBUaXBvIGRlIHB1bnRvIGRlIGFndWENCg0KDQpDb21vIHByaW1lciBwYXNvLCBzZSB2YW4gYSBpbXBvcnRhciBsYXMgbGlicmVy7WFzIGEgdXRpbGl6YXINCmBgYHtyLHdhcm5pbmc9RkFMU0UsZXJyb3I9RkFMU0UsZWNobz1GQUxTRX0NCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoY2FyZXQpDQpgYGANCg0KTGEgRGF0YSBjb25zdGEgZGUgMyBwYXJ0ZXMsICJ0cmFpbl92YWx1ZXMiIGVuIGTzbmRlIHNlIGVuY3VlbnRyYW4gbGFzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyAobGFzIHF1ZSBzZSB1dGlsaXphcuFuIHBhcmEgbGEgcHJlZGljY2nzbiksICJ0cmFpbl9sYWJlbHMiIGVuIGTzbmRlIHNlIGVuY3VlbnRyYSBsYSB2YXJpYWJsZSBkZXBlbmRpZW50ZSAoYXF1ZWxsYSBxdWUgc2UgdmEgYSBwcmVkZWNpcikgeSAidGVzdF92YWx1ZXMiIGVuIGTzbmRlIHNlIGVuY3VlbnRyYW4gbGFzIHZhcmlhYmxlcyBpbmRlcGVuZGllbnRlcyBwYXJhIGVsIHRlc3QNCg0KYGBge3J9DQp0cmFpbl92YWx1ZTwtcmVhZC5jc3YoInRyYWluX3ZhbHVlcy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIG5hLnN0cmluZ3MgPSAiIikNCnRyYWluX2xhYmVsczwtcmVhZC5jc3YoInRyYWluX2xhYmVscy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIG5hLnN0cmluZ3MgPSAiIikNCnRlc3Q8LXJlYWQuY3N2KCJ0ZXN0X3ZhbHVlcy5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsIG5hLnN0cmluZ3MgPSAiIikNCmBgYA0KDQoNClNlIHVuaXLhIGVuIHVuIHNvbG8gZGF0YWZyYW1lIGVsIHRyYWluIHkgZWwgdGVzdCwgZWwgbW9kZWxvIGRlIHByZWRpY2Np824gZ2VuZXJhZG8gbeFzIGFkZWxhbnRlIHV0aWxpemEgZmFjdG9yZXMgeSBwYXJhIGV2aXRhciBxdWUgYWxndW5hcyBkZSBsYXMgdmFyaWFibGVzIGVuIGVsIHRyYWluIHkgZWwgdGVzdCBubyBjb2luY2lkYW4sIHNlIGNvbnZlcnRpcuFuIGEgZmFjdG9yZXMgYW50ZXMgZGUgdm9sdmVybG9zIGEgc2VwYXJhciB5IHVuaXIgZWwgdHJhaW4gKGxhYmVscyB5IHZhbHVlKS4gVGFtYmnpbiBzZSBvYnNlcnZhIHF1ZSBjdWVudGEgY29uIG11Y2hhcyB2YXJpYWJsZXMsIGFsZ3VuYXMgZGUgbGFzIGN1YWxlcyBzZSByZXBpdGVuIHBvciBkaXZlcnNhcyBzaXR1YWNpb25lcyAocHJldmlhbWVudGUgdmlzdWFsaXphZG8pLCBwb3IgbG8gcXVlIHNlIHJlZHVjaXLhIGVsIGRhdGFmcmFtZSBjb24gYXF1ZWxsYXMgcXVlIHNvbiD6bmljYXMgeSBuZWNlc2FyaWFzLiANCg0KYGBge3J9DQojVW5pciB0b2RvIGxhIGRhdGENCmFsbF9kYXRhIDwtIGJpbmRfcm93cyh0cmFpbl92YWx1ZSx0ZXN0KQ0KI1JlZHVjaXIgZWwgZGF0YWZyYW1lDQphbGxfZGF0YSA8LSBzZWxlY3QoYWxsX2RhdGEsaWQsYW1vdW50X3RzaCxncHNfaGVpZ2h0LGxvbmdpdHVkZSxsYXRpdHVkZSxiYXNpbixyZWdpb24scG9wdWxhdGlvbiwNCiAgICAgICAgICAgICAgICAgICAgY29uc3RydWN0aW9uX3llYXIsZXh0cmFjdGlvbl90eXBlX2NsYXNzLG1hbmFnZW1lbnRfZ3JvdXAscGF5bWVudF90eXBlLA0KICAgICAgICAgICAgICAgICAgICBxdWFsaXR5X2dyb3VwLHF1YW50aXR5X2dyb3VwLHNvdXJjZV9jbGFzcyx3YXRlcnBvaW50X3R5cGVfZ3JvdXApDQoNCiNjb252ZXJ0aXIgYSBmYWN0b3Jlcw0KYWxsX2RhdGEkYmFzaW4gPC0gYXMuZmFjdG9yKGFsbF9kYXRhJGJhc2luKQ0KYWxsX2RhdGEkcmVnaW9uPC0gYXMuZmFjdG9yKGFsbF9kYXRhJHJlZ2lvbikNCmFsbF9kYXRhJGV4dHJhY3Rpb25fdHlwZV9jbGFzczwtIGFzLmZhY3RvcihhbGxfZGF0YSRleHRyYWN0aW9uX3R5cGVfY2xhc3MpDQphbGxfZGF0YSRtYW5hZ2VtZW50X2dyb3VwPC0gYXMuZmFjdG9yKGFsbF9kYXRhJG1hbmFnZW1lbnRfZ3JvdXApDQphbGxfZGF0YSRwYXltZW50X3R5cGU8LSBhcy5mYWN0b3IoYWxsX2RhdGEkcGF5bWVudF90eXBlKQ0KYWxsX2RhdGEkcXVhbGl0eV9ncm91cDwtIGFzLmZhY3RvcihhbGxfZGF0YSRxdWFsaXR5X2dyb3VwKQ0KYWxsX2RhdGEkcXVhbnRpdHlfZ3JvdXA8LSBhcy5mYWN0b3IoYWxsX2RhdGEkcXVhbnRpdHlfZ3JvdXApDQphbGxfZGF0YSRzb3VyY2VfY2xhc3M8LSBhcy5mYWN0b3IoYWxsX2RhdGEkc291cmNlX2NsYXNzKQ0KYWxsX2RhdGEkd2F0ZXJwb2ludF90eXBlX2dyb3VwPC0gYXMuZmFjdG9yKGFsbF9kYXRhJHdhdGVycG9pbnRfdHlwZV9ncm91cCkNCg0KI1RyYWluLVRlc3QNCnRyYWluIDwtcmlnaHRfam9pbihhbGxfZGF0YVswOjU5NDAwLF0sdHJhaW5fbGFiZWxzLGJ5PSJpZCIpDQp0cmFpbiRzdGF0dXNfZ3JvdXA8LWFzLmZhY3Rvcih0cmFpbiRzdGF0dXNfZ3JvdXApDQp0ZXN0PC1hbGxfZGF0YVs1OTQwMTpucm93KGFsbF9kYXRhKSxdDQpybSh0cmFpbl92YWx1ZSkNCnJtKHRyYWluX2xhYmVscykNCnJtKGFsbF9kYXRhKQ0KYGBgDQoNCg0KT2JzZXJ2YXIgZWwgRGF0YVNldCAodHJhaW4pDQpgYGB7cn0NCmdsaW1wc2UodHJhaW4pDQp0YWJsZSh0cmFpbiRzdGF0dXNfZ3JvdXApDQpgYGANCg0KT2JzZXJ2YXIgc2kgZXhpc3RlIGFsZ3VuYSBkZSBsYXMgdmFyaWFibGVzIGluZGVwZW5kaWVudGVzIHF1ZSBjb250ZW5nYSBkYXRhIG5vIHbhbGlkYQ0KDQpgYGB7cn0NCnNhcHBseSh0cmFpbiwgbnVtX25hKQ0KYGBgDQoNCmBgYHtyfQ0Kc2FwcGx5KHRlc3QsIG51bV9uYSkNCmBgYA0KDQoNClV0aWxpemFyIGdy4WZpY2FzIHBhcmEgdmlzdWFsaXphciBjYXJhY3Rlcu1zdGljYXMgZGUgbGEgZGF0YQ0KDQpgYGB7cn0NCnRyYWluICU+JQ0KICAgIGdncGxvdChhZXMocmVvcmRlcihiYXNpbiksZmlsbD1zdGF0dXNfZ3JvdXApKSsNCiAgICBnZW9tX2JhcigpDQp0cmFpbiAlPiUNCiAgICBnZ3Bsb3QoYWVzKHJlb3JkZXIoZXh0cmFjdGlvbl90eXBlX2NsYXNzKSxmaWxsPXN0YXR1c19ncm91cCkpKw0KICAgIGdlb21fYmFyKCkNCnRyYWluICU+JQ0KICAgIGdncGxvdChhZXMocmVvcmRlcihtYW5hZ2VtZW50X2dyb3VwKSxmaWxsPXN0YXR1c19ncm91cCkpKw0KICAgIGdlb21fYmFyKCkNCnRyYWluICU+JQ0KICAgIGdncGxvdChhZXMocmVvcmRlcihxdWFsaXR5X2dyb3VwKSxmaWxsPXN0YXR1c19ncm91cCkpKw0KICAgIGdlb21fYmFyKCkNCnRyYWluICU+JQ0KICAgIGdncGxvdChhZXMocmVvcmRlcihxdWFudGl0eV9ncm91cCksZmlsbD1zdGF0dXNfZ3JvdXApKSsNCiAgICBnZW9tX2JhcigpDQp0cmFpbiAlPiUNCiAgICBnZ3Bsb3QoYWVzKHJlb3JkZXIoc291cmNlX2NsYXNzKSxmaWxsPXN0YXR1c19ncm91cCkpKw0KICAgIGdlb21fYmFyKCkNCnRyYWluICU+JQ0KICAgIGdncGxvdChhZXMocmVvcmRlcih3YXRlcnBvaW50X3R5cGVfZ3JvdXApLGZpbGw9c3RhdHVzX2dyb3VwKSkrDQogICAgZ2VvbV9iYXIoKQ0KYGBgDQoNCk1hcGVhciBsb25naXR1ZCB5IGxhdGl0dWQgZW4gdW4gcGxhbm8gY2FydGVzaWFubyB5IG9ic2VydmFyIGVsIGVzdHJhZG8gZGUgbG9zIHBvem9zDQpgYGB7cn0NCnRyYWluJT4lDQogICAgc3Vic2V0KGxhdGl0dWRlIDwgMCAmIGxvbmdpdHVkZSA+IDApJT4lDQogICAgZ2dwbG90KGFlcyh4ID0gbGF0aXR1ZGUsIHkgPSBsb25naXR1ZGUsIGNvbG9yID0gc3RhdHVzX2dyb3VwKSkgKyANCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQpQYXJhIGdlbmVyYXIgZWwgbW9kZWxvIGRlIHByZWRpY2Np824sIHNlIHV0aWxpemFy4W4g4XJib2xlcywgZW4gZXN0ZSBjYXNvLCBzZSB0cmF0YSBkZSBnZW5lcmFyIHVuYSBjbGFzaWZpY2FjafNuIG5vIGJpbmFyaWEsIGRhZG8gZXN0bywgc2UgdXRpbGl6YSBsYSBsaWJyZXLtYSByYW5kb21Gb3Jlc3QgY29uIGxhIGN1YWwgc2UgcHVlZGVuIHJlYWxpemFyIGNsYXNpZmljYWNpb25lcyBwYXJhIHVuIG3heGltbyBkZSAzMiBjbGFzZXMuIA0KDQpgYGB7cn0NCnNldC5zZWVkKDEwMCkNCiNHZW5lcmFyIG1vZGVsbw0KbW9kZWxvIDwtIHJhbmRvbUZvcmVzdChzdGF0dXNfZ3JvdXB+bG9uZ2l0dWRlK2xhdGl0dWRlK2V4dHJhY3Rpb25fdHlwZV9jbGFzcysNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdWFsaXR5X2dyb3VwK3F1YW50aXR5X2dyb3VwK3dhdGVycG9pbnRfdHlwZV9ncm91cCsNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdHJ1Y3Rpb25feWVhcitwYXltZW50X3R5cGUrYW1vdW50X3RzaCtzb3VyY2VfY2xhc3MsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gdHJhaW4sIGltcG9ydGFuY2UgPSBUUlVFLCBudHJlZSA9IDMwLCBub2Rlc2l6ZSA9IDMpDQpwcmludChtb2RlbG8pDQojR2VuZXJhciBwcmVkaWNjaW9uZXMNCnByZWRpY2Npb24gPC0gcHJlZGljdChvYmplY3Q9IG1vZGVsbyxuZXdkYXRhPSB0ZXN0KQ0KcHJlZGljY2lvbiA8LSBkYXRhLmZyYW1lKGlkID0gdGVzdCRpZCwgc3RhdHVzX2dyb3VwID0gcHJlZGljY2lvbikNCg0Kd3JpdGUuY3N2KHByZWRpY2Npb24sIGZpbGU9IlByZWRpY2Npb24uY3N2Iixyb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQpFbiBsYSBjb21wZXRlbmNpYSBubyBleGlzdGUgbGEgcG9zaWJpbGlkYWQgZGUgYWRqdW50YXIgbG8gcXVlIHNlIGhhIHJlYWxpemFkbywgcG9yIGxvIHRhbnRvLCBhZGp1bnRvIHVuIHNjcmVlbnNob3QgZGUgbGEgcHVudHVhY2nzbiB5IGxhIHBvc2ljafNuIG9idGVuaWRhLg0KDQpgYGB7cn0NCmxpYnJhcnkoaW1hZ2VyKQ0KaW08LWxvYWQuaW1hZ2UoIkM6L1VzZXJzL0FsZWphQ2VybWUvRG9jdW1lbnRzL1IvUHJveWVjdG8vaW1hZ2VuLnBuZyIpDQpwbG90KGltKQ0KYGBgDQoNCi0tLS1GdW5jafNuIHBhcmEgdmVyaWZpY2FyIGxhIGNhbnRpZGFkIGRlIE5BIGVuIGxvcyBkYXRhRnJhbWVzLS0tLQ0KDQpgYGB7cn0NCm51bV9uYSA8LSBmdW5jdGlvbih4KXsNCiAgc3VtKGlzLm5hKHgpKQ0KfQ0KYGBgDQotLS0tRnVuY2nzbiBwYXJhIHJlb3JkZXJuYXIgbGEgZGF0YS0tLS0NCmBgYHtyfQ0KcmVvcmRlciA8LSBmdW5jdGlvbih4KSB7DQogIGZhY3Rvcih4LCBsZXZlbHMgPSBuYW1lcyhzb3J0KHRhYmxlKHgpKSkpDQp9DQpgYGANCg==