library(OneR)
Error in library(OneR) : there is no package called ‘OneR’

  1. Regresión lineal múltiple


  1. Crear un modelo para predecir el precio con todas las covariables.

model <- lm(price ~ l3 + rooms + bathrooms + surface_total + surface_covered + price + property_type, data = properties )
the response appeared on the right-hand side and was droppedproblem with term 6 in model.matrix: no columns are assigned
summary(model)

Call:
lm(formula = price ~ l3 + rooms + bathrooms + surface_total + 
    surface_covered + price + property_type, data = properties)

Residuals:
    Min      1Q  Median      3Q     Max 
-400904  -33817   -3307   24660  560915 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -109406.61    4788.67 -22.847  < 2e-16 ***
l3Agronomía                   623.53    8846.14   0.070 0.943807    
l3Almagro                   -4520.04    4295.24  -1.052 0.292650    
l3Balvanera                -24788.27    4551.65  -5.446 5.18e-08 ***
l3Barracas                 -10128.24    5351.06  -1.893 0.058397 .  
l3Barrio Norte              49921.81    4417.82  11.300  < 2e-16 ***
l3Belgrano                  69648.12    4283.55  16.259  < 2e-16 ***
l3Boca                     -47540.60    7076.20  -6.718 1.86e-11 ***
l3Boedo                    -19034.38    5219.54  -3.647 0.000266 ***
l3Caballito                  6220.15    4301.29   1.446 0.148153    
l3Catalinas                -76321.95   33563.74  -2.274 0.022974 *  
l3Centro / Microcentro     -29046.49    6781.80  -4.283 1.85e-05 ***
l3Chacarita                 11903.39    5299.02   2.246 0.024687 *  
l3Coghlan                   40820.55    5462.90   7.472 8.02e-14 ***
l3Colegiales                34073.02    4816.54   7.074 1.52e-12 ***
l3Congreso                 -32314.97    5494.75  -5.881 4.10e-09 ***
l3Constitución             -47292.98    6321.63  -7.481 7.50e-14 ***
l3Flores                   -22510.27    4536.15  -4.962 6.99e-07 ***
l3Floresta                 -28315.65    5069.38  -5.586 2.34e-08 ***
l3Las Cañitas               90455.90    5883.38  15.375  < 2e-16 ***
l3Liniers                  -20080.34    5366.27  -3.742 0.000183 ***
l3Mataderos                -33863.43    5424.79  -6.242 4.35e-10 ***
l3Monserrat                -32431.49    5228.46  -6.203 5.59e-10 ***
l3Monte Castro              -8770.72    5949.63  -1.474 0.140445    
l3Nuñez                     56958.42    4559.69  12.492  < 2e-16 ***
l3Once                     -30757.83    5456.51  -5.637 1.74e-08 ***
l3Palermo                   66169.58    4221.50  15.674  < 2e-16 ***
l3Parque Avellaneda        -34398.95    7598.09  -4.527 5.99e-06 ***
l3Parque Centenario        -12288.30    5016.45  -2.450 0.014305 *  
l3Parque Chacabuco         -22537.83    5314.36  -4.241 2.23e-05 ***
l3Parque Chas                5195.26    7542.97   0.689 0.490981    
l3Parque Patricios         -36808.02    5973.29  -6.162 7.24e-10 ***
l3Paternal                 -13314.50    5189.69  -2.566 0.010304 *  
l3Pompeya                  -79977.17    8035.74  -9.953  < 2e-16 ***
l3Puerto Madero            259015.83    5095.12  50.836  < 2e-16 ***
l3Recoleta                  64088.22    4360.34  14.698  < 2e-16 ***
l3Retiro                    26067.40    5281.27   4.936 8.01e-07 ***
l3Saavedra                  19492.00    4914.18   3.966 7.31e-05 ***
l3San Cristobal            -23739.75    4955.13  -4.791 1.67e-06 ***
l3San Nicolás              -26247.55    5168.96  -5.078 3.83e-07 ***
l3San Telmo                 -5653.85    4877.12  -1.159 0.246356    
l3Tribunales               -34608.17    8924.63  -3.878 0.000106 ***
l3Velez Sarsfield          -25943.69    8303.75  -3.124 0.001783 ** 
l3Versalles                -22232.13    6758.40  -3.290 0.001004 ** 
l3Villa Crespo               1595.26    4317.54   0.369 0.711770    
l3Villa del Parque          -3290.17    4866.59  -0.676 0.498997    
l3Villa Devoto              13301.39    4807.08   2.767 0.005659 ** 
l3Villa General Mitre      -19170.08    6802.25  -2.818 0.004831 ** 
l3Villa Lugano             -83039.18    6533.35 -12.710  < 2e-16 ***
l3Villa Luro                -7579.11    5404.78  -1.402 0.160833    
l3Villa Ortuzar             18667.61    6829.18   2.734 0.006269 ** 
l3Villa Pueyrredón          10516.80    5349.56   1.966 0.049314 *  
l3Villa Real                -8823.37    8745.56  -1.009 0.313030    
l3Villa Riachuelo          -32775.66   17171.10  -1.909 0.056298 .  
l3Villa Santa Rita          -5767.71    6383.86  -0.903 0.366274    
l3Villa Soldati           -136489.91   18944.29  -7.205 5.90e-13 ***
l3Villa Urquiza             30648.43    4418.91   6.936 4.09e-12 ***
rooms                       -3961.27     444.58  -8.910  < 2e-16 ***
bathrooms                   34040.98     644.28  52.836  < 2e-16 ***
surface_total                 919.08      23.52  39.069  < 2e-16 ***
surface_covered              1457.18      28.73  50.715  < 2e-16 ***
property_typeDepartamento   92653.32    2191.23  42.284  < 2e-16 ***
property_typePH             46779.37    2274.94  20.563  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66580 on 45841 degrees of freedom
Multiple R-squared:  0.7764,    Adjusted R-squared:  0.7761 
F-statistic:  2568 on 62 and 45841 DF,  p-value: < 2.2e-16

Analizar los resultados del modelo:


i.Interpretación de los coeficientes estimados


En la columna Estimate podemos ver los coeficientes estimados para los Beta correspondientes a las variables en la izquierda de la tabla. Se interpretan de la siguiente forma: por cada unidad que aumente la variable en la izquierda de la tabla, el precio de la vivienda aumenta(o disminuye, depende del signo) según la cantidad estimada manteniendo el resto de variables constantes. EJ: El aumento de una unidad de la superficie total de una vivienda, conlleva a un aumento de su precio, en promedio, de 919.08 manteniendo el resto de variables fijas. Para este caso, la interpretación del Intercept no tiene sentido, no hay propiedades que tengan 0 habitaciones, 0 baños, etc.


ii.¿Qué observan respecto de la significatividad de las variables dummy?


Muchas de las variables dummy no presentan una diferencia significativa con la variable basal Abasto.


iii.Medidas de evaluación del modelo

plot(model)
the response appeared on the right-hand side and was droppedproblem with term 6 in model.matrix: no columns are assigned

En el primer gráfico se puede ver un scatter plot de los residuos y los valores que estima el modelo. Se puede observar un patron en los datos, las varianza aumenta para los valores mas grandes estimados indicando que el modelo no cumple con la homocedasticidad de los residuos

El segundo gráfico “Normal QQ” sirve para ver si los residuos siguen una distribución normal, si el modelo esta bien definido, los círculos que se ven en el gráfico deberian seguir el patron lineal de la recta puntueada. En este caso, en los limites del gráfico se observa un desvio de los puntos con respecto a la recta. Los residuos estandarizados no siguen esta distribución.

Por último, el graficó de los residuos en función del Leverage. Se pueden observar valores atípicos que desplazan la recta generada por el modelo hacia ellos, aumentado el valor de los residuos y el error éstandar.

Una manera más de evaluar el modelo es observando el R2, que es igual a 0,7764. Significa que las variables seleccionadas para el modello estan explicando aproximadamente, el 77% de la variabilidad del precio de las viviendas.

paste("Depto en abasto:" , abasto[2],"Ph en balvanera",balva[2])
[1] "Depto en abasto: 316347.45339207 Ph en balvanera 210389.213518492"

La predicción para el departamento en Abasto da 316.348 dólares, mientras que el PH en Balvanera 210.389 mil dólares. Es preferible tener el departamento en Abasto mirando solamente el valor de venta estimado.


Realizar un modelo sin la covariable l3 e interpretar sus resultados (todas las partes de la salida que consideren relevantes)

model2 <- lm(price ~ rooms + bathrooms + surface_total + surface_covered + price + property_type, data = properties )
the response appeared on the right-hand side and was droppedproblem with term 5 in model.matrix: no columns are assigned
summary(model2)

Call:
lm(formula = price ~ rooms + bathrooms + surface_total + surface_covered + 
    price + property_type, data = properties)

Residuals:
    Min      1Q  Median      3Q     Max 
-518799  -36177   -9643   25740  724251 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -131096.86    2750.50  -47.66   <2e-16 ***
rooms                      -13348.53     519.02  -25.72   <2e-16 ***
bathrooms                   42664.68     756.37   56.41   <2e-16 ***
surface_total                 877.03      27.59   31.79   <2e-16 ***
surface_covered              1783.80      33.53   53.21   <2e-16 ***
property_typeDepartamento  135177.47    2513.93   53.77   <2e-16 ***
property_typePH             68598.52    2677.46   25.62   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 79210 on 45897 degrees of freedom
Multiple R-squared:  0.6832,    Adjusted R-squared:  0.6831 
F-statistic: 1.649e+04 on 6 and 45897 DF,  p-value: < 2.2e-16

Sacando la covariable l3, el modelo pierde poder para explicar la variable precio ya que su R2 es menor que en el caso antorior. Esto significa que la variable l3 contiene informacion valiosa para explicar la variable precio.

¿Cuál es el modelo que mejor explica la variabilidad del precio?

El primer modelo con la variable l3 explica en mayor medida, la variabilidad del precio de las viviendas.

Creación de variables

  1. En el ejercicio anterior encontramos que algunos barrios son significativos, aunque no todos. Crear una nueva variable barrios que divida a los barrios según el precio por metro cuadrado promedio de las propiedades en ellos, con los grupos c(‘alto’, ‘medio’, ‘bajo’). Realizar un análisis exploratorio para definir los puntos de corte de la nueva variable y explicar los criterios utilizados en la construcción de la misma.

Al no encontrar un patron especificon en los datos, voy a dividir el dataset en 3 partes iguales.

metros_barrio= metros_barrio %>% filter(mean_price != min(mean_price),mean_price!=max(mean_price)) %>% mutate(barrios = bin(mean_price, nbins = 3, labels = c("BAJO", "MEDIO", "ALTO"))) 
ggplot(metros_barrio,aes(barrios))+geom_histogram(stat="count")  + ggtitle('Grupos por Barrios')
Ignoring unknown parameters: binwidth, bins, pad

Calcular el modelo que predice el precio en función de las nuevas covariables e interpretar sus resultados (todas las partes de la salida que consideren relevantes)

summary(model_new)

Call:
lm(formula = price ~ rooms + bathrooms + surface_total + surface_covered + 
    barrios + property_type, data = properties_new)

Residuals:
    Min      1Q  Median      3Q     Max 
-414977  -34231   -3624   24754  568608 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -145313.14    2600.35  -55.88   <2e-16 ***
rooms                       -4587.80     446.78  -10.27   <2e-16 ***
bathrooms                   34693.01     651.93   53.22   <2e-16 ***
surface_total                 947.14      23.58   40.16   <2e-16 ***
surface_covered              1427.37      28.81   49.55   <2e-16 ***
barriosMEDIO                29515.62    1447.82   20.39   <2e-16 ***
barriosALTO                 94081.21    1476.53   63.72   <2e-16 ***
property_typeDepartamento   93732.32    2178.55   43.02   <2e-16 ***
property_typePH             48176.13    2294.12   21.00   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66810 on 45014 degrees of freedom
  (881 observations deleted due to missingness)
Multiple R-squared:  0.7566,    Adjusted R-squared:  0.7566 
F-statistic: 1.749e+04 on 8 and 45014 DF,  p-value: < 2.2e-16

Con el nuevo modelo podemos ver que todas las covariables son significativas. El R2 no varió mucho.


  1. ¿Qué modelo explica mejor la variabilidad de los datos, el que utiliza la variable l3 o el que utiliza barrio? En su opinión, ¿Qué modelo es más útil? ¿Porqué?

El modelo que parece explicar mejor la variabilidad es el que contiene a l3 por tener un R2 Ajustado mayor(un poco). Tambien, con el primer modelo tenemos informacion sobre los barrios de forma individual, que con el modelo de barrios no.

La interpretación de los coeficientes de las variables surface_covered y surface_total puede ser un poco problemática ya que se encuentran correlacionadas. Entonces, podemos construir una nueva variable surface_patio para la diferencia entre ambas superficies: Construir una nueva variable surface_patio. Dado que algunos registros pueden contener la contradicción de que surface_total<surface_covered, explicitar cómo se procede para dichos casos. Calcular nuevamente el modelo lineal para todas las covariables previas (excepto surface_total), surface_covered y surface_patio e interpretar los coeficientes de estas dos últimas variables

summary(model_new)

Call:
lm(formula = price ~ rooms + bathrooms + surface_patio + surface_covered + 
    barrios + property_type, data = properties_new)

Residuals:
    Min      1Q  Median      3Q     Max 
-414977  -34231   -3624   24754  568608 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)               -145313.14    2600.35  -55.88   <2e-16 ***
rooms                       -4587.80     446.78  -10.27   <2e-16 ***
bathrooms                   34693.01     651.93   53.22   <2e-16 ***
surface_patio                 947.14      23.58   40.16   <2e-16 ***
surface_covered              2374.51      15.25  155.72   <2e-16 ***
barriosMEDIO                29515.62    1447.82   20.39   <2e-16 ***
barriosALTO                 94081.21    1476.53   63.72   <2e-16 ***
property_typeDepartamento   93732.32    2178.55   43.02   <2e-16 ***
property_typePH             48176.13    2294.12   21.00   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 66810 on 45014 degrees of freedom
  (881 observations deleted due to missingness)
Multiple R-squared:  0.7566,    Adjusted R-squared:  0.7566 
F-statistic: 1.749e+04 on 8 and 45014 DF,  p-value: < 2.2e-16

surface_patio no contiene contradicciones. Si las tuviera, lo consideraria un error y las borraría. El coeficiente estimado para la variable surface_covered se interpreta como el aumento del precio promedio de una propiedad cuando la superficie cubierta aumenta en un metro cuadrado. Análogamente se puede interpretar surface_patio. El aumento del precio es mayor cuando aumenta la superficie cubierta que cuando aumenta la superficie descubierta.

Evaluación del modelo

Analizar los residuos del modelo elaborado en 2.d

El análisis es el mismo que en el caso anterior, no se presenta homoedasticidad de la varianza y los residuos no siguen una distribución normal. En el último gráficose observa que hay puntos alejados pero el leverage es menor que en la evalución del modelo anterior.

Calcular el modelo Comparar la performance del modelo de 2.d con éste, tanto en términos de la variabilidad explicada cómo de su relación con los supuestos del modelo lineal. Re-interpretar los parámetros del modelo.

summary(model_log)

Call:
lm(formula = price ~ rooms + bathrooms + surface_patio + surface_covered + 
    property_type + barrios, data = properties_log)

Residuals:
     Min       1Q   Median       3Q      Max 
-1.31025 -0.14448 -0.00264  0.13551  1.12351 

Coefficients:
                            Estimate Std. Error t value Pr(>|t|)    
(Intercept)                8.3158671  0.0187899 442.571  < 2e-16 ***
rooms                     -0.0161561  0.0037708  -4.285 1.84e-05 ***
bathrooms                  0.1838038  0.0038009  48.358  < 2e-16 ***
surface_patio              0.0041961  0.0000793  52.913  < 2e-16 ***
surface_covered            0.7805892  0.0044459 175.576  < 2e-16 ***
property_typeDepartamento  0.2115691  0.0072020  29.377  < 2e-16 ***
property_typePH            0.0593620  0.0076157   7.795 6.59e-15 ***
barriosMEDIO               0.1926193  0.0048566  39.662  < 2e-16 ***
barriosALTO                0.4739848  0.0049543  95.672  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2241 on 45014 degrees of freedom
  (881 observations deleted due to missingness)
Multiple R-squared:  0.8203,    Adjusted R-squared:  0.8202 
F-statistic: 2.568e+04 on 8 and 45014 DF,  p-value: < 2.2e-16

Este nuevo modelo mejora la explicación de la variabilidad con un R2 Ajustado=0.8202 frente al anterior, aunque se pierde la interpretabilidad.

En este caso los residuos no parecen tener una estructura acercandoce mas a la homocedasticidad de la varianza. La recta en el QQ plot se apega mucho mas los puntos del grafico, indicando que los residuos siguen la distribución normal.

Dataframes anidados

Anidar por la variable property_type

properties_nested <- properties %>% 
  group_by(property_type) %>% 
  nest()
head(properties_nested)

Construir para cada tipo de propiedad el modelo de 2.d e interpretar los resultados en cada caso. Qué diferencias encuentran entre los distintos modelos?


df_model <- function(datos) {
      datos_temp <- datos %>% dummy_cols() %>% select(-barrios)
      modelo <- lm("price ~. " , data = datos_temp)
      return(modelo)
}

model_nested <- properties_nested %>%
                  mutate(modelo = map(data, df_model)) %>%
                  mutate(descripcion = map(modelo, glance)) %>% 
                  unnest(descripcion)
                   
model_nested
LS0tDQp0aXRsZTogIlRQIDIgLSBHYXN0b24gTWFydGluZXoiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCiNTZSBjYXJnYW4gbG9zIHBhcXVldGVzIMO6dGlsZXMgcGFyYSBlbCBUUA0KbGlicmFyeSgidGlkeXZlcnNlIikNCmxpYnJhcnkoImRwbHlyIikNCmxpYnJhcnkoIk9uZVIiKQ0KbGlicmFyeSgiYnJvb20iKQ0KbGlicmFyeSgiZmFzdER1bW1pZXMiKSANCg0KI1NlIGxlZSBlbCBhcmNoaXZvIHNlIG11ZXN0cmFuIGRvcyBkYXRvcyBzaW1wbGVzIHBhcmEgdmVyIGxhIGRpbWVuc2lvbiB5IGxhcyBjb2x1bW5hcyBkZWwgc2V0IGRlIGRhdG9zDQpwcm9wZXJ0aWVzID0gcmVhZFJEUygiYXJfcHJvcGVydGllcy5yZHMiKQ0KcHJpbnQocGFzdGUoJ0NhbnRpZGFkIGRlIHJlZ2lzdHJvczogJyAsIG5yb3cocHJvcGVydGllcykpKQ0KcHJpbnQoY29sbmFtZXMocHJvcGVydGllcykpIA0KYGBgDQoNCjxoMT4xLiBSZWdyZXNpw7NuIGxpbmVhbCBtw7psdGlwbGU8L2gxPg0KPGJyLz48aDI+YS4gQ3JlYXIgdW4gbW9kZWxvIHBhcmEgcHJlZGVjaXIgZWwgcHJlY2lvIGNvbiB0b2RhcyBsYXMgY292YXJpYWJsZXMuPC9oMj4NCmBgYHtyfQ0KI01vZGVsbyBkZSBSZWcuIGxpbmVhbCBtdWx0aXBsZSBxdWUgY29udGVtcGxhIHRvZGFzIGxhcyB2YXJpYWJsZXMgbWVub3MgaWQobm8gdGllbmUgbmFkYSBxdWUgaGFlciBwYXJhIGVzdGltYXIgbG9zIHZhbG9yZXMgZGUgbGFzIHByb3BpZWRhZGVzKQ0KbW9kZWwgPC0gbG0ocHJpY2UgfiBsMyArIHJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHN1cmZhY2VfY292ZXJlZCArIHByaWNlICsgcHJvcGVydHlfdHlwZSwgZGF0YSA9IHByb3BlcnRpZXMgKQ0Kc3VtbWFyeShtb2RlbCkNCmBgYA0KPGJyLz48aDI+QW5hbGl6YXIgbG9zIHJlc3VsdGFkb3MgZGVsIG1vZGVsbzo8L2gyPg0KPGJyLz48aDM+aS5JbnRlcnByZXRhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlcyBlc3RpbWFkb3M8L2gzPg0KPGJyLz48cD5FbiBsYSBjb2x1bW5hIDxiPkVzdGltYXRlPC9iPiBwb2RlbW9zIHZlciBsb3MgY29lZmljaWVudGVzIGVzdGltYWRvcyBwYXJhIGxvcyBCZXRhIGNvcnJlc3BvbmRpZW50ZXMgYSBsYXMgdmFyaWFibGVzIGVuIGxhIGl6cXVpZXJkYSBkZSBsYSB0YWJsYS4gU2UgaW50ZXJwcmV0YW4gZGUgbGEgc2lndWllbnRlIGZvcm1hOiBwb3IgY2FkYSB1bmlkYWQgcXVlIGF1bWVudGUgbGEgdmFyaWFibGUgZW4gbGEgaXpxdWllcmRhIGRlIGxhIHRhYmxhLCBlbCBwcmVjaW8gZGUgbGEgdml2aWVuZGEgYXVtZW50YShvIGRpc21pbnV5ZSwgZGVwZW5kZSBkZWwgc2lnbm8pIHNlZ8O6biBsYSBjYW50aWRhZCBlc3RpbWFkYSBtYW50ZW5pZW5kbyBlbCByZXN0byBkZSB2YXJpYWJsZXMgY29uc3RhbnRlcy4gRUo6IEVsIGF1bWVudG8gZGUgdW5hIHVuaWRhZCBkZSBsYSBzdXBlcmZpY2llIHRvdGFsIGRlIHVuYSB2aXZpZW5kYSwgY29ubGxldmEgYSB1biBhdW1lbnRvIGRlIHN1IHByZWNpbywgZW4gcHJvbWVkaW8sIGRlIDkxOS4wOCBtYW50ZW5pZW5kbyBlbCByZXN0byBkZSB2YXJpYWJsZXMgZmlqYXMuIFBhcmEgZXN0ZSBjYXNvLCBsYSBpbnRlcnByZXRhY2nDs24gZGVsIDxiPkludGVyY2VwdDwvYj4gbm8gdGllbmUgc2VudGlkbywgbm8gaGF5IHByb3BpZWRhZGVzIHF1ZSB0ZW5nYW4gMCBoYWJpdGFjaW9uZXMsIDAgYmHDsW9zLCBldGMuPC9wPg0KPGJyLz48aDM+aWkuwr9RdcOpIG9ic2VydmFuIHJlc3BlY3RvIGRlIGxhIHNpZ25pZmljYXRpdmlkYWQgZGUgbGFzIHZhcmlhYmxlcyBkdW1teT88L2gzPg0KPGJyLz48cD5NdWNoYXMgZGUgbGFzIHZhcmlhYmxlcyBkdW1teSBubyBwcmVzZW50YW4gdW5hIGRpZmVyZW5jaWEgc2lnbmlmaWNhdGl2YSBjb24gbGEgdmFyaWFibGUgYmFzYWwgQWJhc3RvLjwvcD4NCjxici8+PGgzPmlpaS5NZWRpZGFzIGRlIGV2YWx1YWNpw7NuIGRlbCBtb2RlbG88L2gzPg0KYGBge3J9DQpwbG90KG1vZGVsKQ0KYGBgDQo8cD5FbiBlbCBwcmltZXIgZ3LDoWZpY28gc2UgcHVlZGUgdmVyIHVuIHNjYXR0ZXIgcGxvdCBkZSBsb3MgcmVzaWR1b3MgeSBsb3MgdmFsb3JlcyBxdWUgZXN0aW1hIGVsIG1vZGVsby4gU2UgcHVlZGUgb2JzZXJ2YXIgdW4gcGF0cm9uIGVuIGxvcyBkYXRvcywgbGFzIHZhcmlhbnphIGF1bWVudGEgcGFyYSBsb3MgdmFsb3JlcyBtYXMgZ3JhbmRlcyBlc3RpbWFkb3MgaW5kaWNhbmRvIHF1ZSBlbCBtb2RlbG8gbm8gY3VtcGxlIGNvbiBsYSBob21vY2VkYXN0aWNpZGFkIGRlIGxvcyByZXNpZHVvczwvcD4NCjxwPkVsIHNlZ3VuZG8gZ3LDoWZpY28g4oCcTm9ybWFsIFFR4oCdIHNpcnZlIHBhcmEgdmVyIHNpIGxvcyByZXNpZHVvcyBzaWd1ZW4gdW5hIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLCBzaSBlbCBtb2RlbG8gZXN0YSBiaWVuIGRlZmluaWRvLCBsb3MgY8OtcmN1bG9zIHF1ZSBzZSB2ZW4gZW4gZWwgZ3LDoWZpY28gZGViZXJpYW4gc2VndWlyIGVsIHBhdHJvbiBsaW5lYWwgZGUgbGEgcmVjdGEgcHVudHVlYWRhLiBFbiBlc3RlIGNhc28sIGVuIGxvcyBsaW1pdGVzIGRlbCBncsOhZmljbyBzZSBvYnNlcnZhIHVuIGRlc3ZpbyBkZSBsb3MgcHVudG9zIGNvbiByZXNwZWN0byBhIGxhIHJlY3RhLiBMb3MgcmVzaWR1b3MgZXN0YW5kYXJpemFkb3Mgbm8gc2lndWVuIGVzdGEgZGlzdHJpYnVjacOzbi48L3A+DQo8cD5Qb3Igw7psdGltbywgZWwgZ3JhZmljw7MgZGUgbG9zIHJlc2lkdW9zIGVuIGZ1bmNpw7NuIGRlbCBMZXZlcmFnZS4gU2UgcHVlZGVuIG9ic2VydmFyIHZhbG9yZXMgYXTDrXBpY29zIHF1ZSBkZXNwbGF6YW4gbGEgcmVjdGEgZ2VuZXJhZGEgcG9yIGVsIG1vZGVsbyBoYWNpYSBlbGxvcywgYXVtZW50YWRvIGVsIHZhbG9yIGRlIGxvcyByZXNpZHVvcyB5IGVsIGVycm9yIMOpc3RhbmRhci48L3A+DQo8cD5VbmEgbWFuZXJhIG3DoXMgZGUgZXZhbHVhciBlbCBtb2RlbG8gZXMgb2JzZXJ2YW5kbyBlbCBSMiwgcXVlIGVzIGlndWFsIGEgMCw3NzY0LiBTaWduaWZpY2EgcXVlIGxhcyB2YXJpYWJsZXMgc2VsZWNjaW9uYWRhcyBwYXJhIGVsIG1vZGVsbG8gZXN0YW4gZXhwbGljYW5kbyBhcHJveGltYWRhbWVudGUsIGVsIDc3JSBkZSBsYSB2YXJpYWJpbGlkYWQgZGVsIHByZWNpbyBkZSBsYXMgdml2aWVuZGFzLjwvcD4NCmBgYHtyfQ0KYWJhc3RvID0gcHJlZGljdChtb2RlbCxuZXdkYXRhPWRhdGEuZnJhbWUobDM9IkFiYXN0byIscm9vbXMgPSAzLCBiYXRocm9vbXMgPSAyLHN1cmZhY2VfdG90YWw9MTIwLHN1cmZhY2VfY292ZXJlZD0xMjAscHJvcGVydHlfdHlwZT0nRGVwYXJ0YW1lbnRvJyksaW50ZXJ2YWw9ImNvbmZpZGVuY2UiKQ0KYmFsdmEgPSBwcmVkaWN0KG1vZGVsLG5ld2RhdGE9ZGF0YS5mcmFtZShsMz0iQmFsdmFuZXJhIixyb29tcyA9IDIsIGJhdGhyb29tcyA9IDMsc3VyZmFjZV90b3RhbD0xMDAsc3VyZmFjZV9jb3ZlcmVkPTgwLHByb3BlcnR5X3R5cGU9J1BIJyksaW50ZXJ2YWw9ImNvbmZpZGVuY2UiKQ0KcGFzdGUoIkRlcHRvIGVuIGFiYXN0bzoiICwgYWJhc3RvWzJdLCJQaCBlbiBiYWx2YW5lcmEiLGJhbHZhWzJdKQ0KYGBgDQo8YnIvPjxwPkxhIHByZWRpY2Npw7NuIHBhcmEgZWwgZGVwYXJ0YW1lbnRvIGVuIEFiYXN0byBkYSAzMTYuMzQ4IGTDs2xhcmVzLCBtaWVudHJhcyBxdWUgZWwgUEggZW4gQmFsdmFuZXJhIDIxMC4zODkgbWlsIGTDs2xhcmVzLiBFcyBwcmVmZXJpYmxlIHRlbmVyIGVsIGRlcGFydGFtZW50byBlbiBBYmFzdG8gbWlyYW5kbyBzb2xhbWVudGUgZWwgdmFsb3IgZGUgdmVudGEgZXN0aW1hZG8uPC9wPg0KDQo8YnIvPjxwPlJlYWxpemFyIHVuIG1vZGVsbyBzaW4gbGEgY292YXJpYWJsZSBsMyBlIGludGVycHJldGFyIHN1cyByZXN1bHRhZG9zICh0b2RhcyBsYXMgcGFydGVzIGRlIGxhIHNhbGlkYSBxdWUgY29uc2lkZXJlbiByZWxldmFudGVzKTwvcD4NCmBgYHtyfQ0KbW9kZWwyIDwtIGxtKHByaWNlIH4gcm9vbXMgKyBiYXRocm9vbXMgKyBzdXJmYWNlX3RvdGFsICsgc3VyZmFjZV9jb3ZlcmVkICsgcHJpY2UgKyBwcm9wZXJ0eV90eXBlLCBkYXRhID0gcHJvcGVydGllcyApDQpzdW1tYXJ5KG1vZGVsMikNCmBgYA0KPHA+U2FjYW5kbyBsYSBjb3ZhcmlhYmxlIGwzLCBlbCBtb2RlbG8gcGllcmRlIHBvZGVyIHBhcmEgZXhwbGljYXIgbGEgdmFyaWFibGUgcHJlY2lvIHlhIHF1ZSBzdSBSMiBlcyBtZW5vciBxdWUgZW4gZWwgY2FzbyBhbnRvcmlvci4gRXN0byBzaWduaWZpY2EgcXVlIGxhIHZhcmlhYmxlIGwzIGNvbnRpZW5lIGluZm9ybWFjaW9uIHZhbGlvc2EgcGFyYSBleHBsaWNhciBsYSB2YXJpYWJsZSBwcmVjaW8uPC9wPg0KPHA+wr9DdcOhbCBlcyBlbCBtb2RlbG8gcXVlIG1lam9yIGV4cGxpY2EgbGEgdmFyaWFiaWxpZGFkIGRlbCBwcmVjaW8/PC9wPg0KPHA+RWwgcHJpbWVyIG1vZGVsbyBjb24gbGEgdmFyaWFibGUgbDMgZXhwbGljYSBlbiBtYXlvciBtZWRpZGEsIGxhIHZhcmlhYmlsaWRhZCBkZWwgcHJlY2lvIGRlIGxhcyB2aXZpZW5kYXMuPC9wPg0KPGgyPkNyZWFjacOzbiBkZSB2YXJpYWJsZXM8L2gyPg0KPGgzPmEuIEVuIGVsIGVqZXJjaWNpbyBhbnRlcmlvciBlbmNvbnRyYW1vcyBxdWUgYWxndW5vcyBiYXJyaW9zIHNvbiBzaWduaWZpY2F0aXZvcywgYXVucXVlIG5vIHRvZG9zLiBDcmVhciB1bmEgbnVldmEgdmFyaWFibGUgYmFycmlvcyBxdWUgZGl2aWRhIGEgbG9zIGJhcnJpb3Mgc2Vnw7puIGVsIHByZWNpbyBwb3IgbWV0cm8gY3VhZHJhZG8gcHJvbWVkaW8gZGUgbGFzIHByb3BpZWRhZGVzIGVuIGVsbG9zLCBjb24gbG9zIGdydXBvcyBjKOKAmGFsdG/igJksIOKAmG1lZGlv4oCZLCDigJhiYWpv4oCZKS4gUmVhbGl6YXIgdW4gYW7DoWxpc2lzIGV4cGxvcmF0b3JpbyBwYXJhIGRlZmluaXIgbG9zIHB1bnRvcyBkZSBjb3J0ZSBkZSBsYSBudWV2YSB2YXJpYWJsZSB5IGV4cGxpY2FyIGxvcyBjcml0ZXJpb3MgdXRpbGl6YWRvcyBlbiBsYSBjb25zdHJ1Y2Npw7NuIGRlIGxhIG1pc21hLjwvaDM+DQpgYGB7cn0NCm1ldHJvc19iYXJyaW8gPC0gcHJvcGVydGllcyAlPiUgDQogICAgZ3JvdXBfYnkobDMpICU+JSANCiAgICBzdW1tYXJpc2UobWVhbl9wcmljZT0gbWVhbihwcmljZS9zdXJmYWNlX3RvdGFsKSkNCg0KDQoNCmhpc3RvZ3JhbWEgPC0gZ2dwbG90KG1ldHJvc19iYXJyaW8sIGFlcyh4PW1lYW5fcHJpY2UpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoPTEwMCkNCmhpc3RvZ3JhbWEgPC0gaGlzdG9ncmFtYSArIGdndGl0bGUoIlByZWNpbyBtZXRybyBjdWFkcmFkbyBwb3IgYmFycmlvIikNCmhpc3RvZ3JhbWEgPC0gaGlzdG9ncmFtYSArIGxhYnMoeCA9ICJQcmVjaW8iLCB5ID0gIkNhbnRpZGFkIikNCmhpc3RvZ3JhbWEgPC0gaGlzdG9ncmFtYSArIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA2MDAwKSkNCmhpc3RvZ3JhbWENCnN1bW1hcnkobWV0cm9zX2JhcnJpbyRtZWFuX3ByaWNlKQ0KDQpgYGANCjxwPkFsIG5vIGVuY29udHJhciB1biBwYXRyb24gZXNwZWNpZmljb24gZW4gbG9zIGRhdG9zLCB2b3kgYSBkaXZpZGlyIGVsIGRhdGFzZXQgZW4gMyBwYXJ0ZXMgaWd1YWxlcy48L3A+DQpgYGB7cn0NCm1ldHJvc19iYXJyaW89IG1ldHJvc19iYXJyaW8gJT4lIGZpbHRlcihtZWFuX3ByaWNlICE9IG1pbihtZWFuX3ByaWNlKSxtZWFuX3ByaWNlIT1tYXgobWVhbl9wcmljZSkpICU+JSBtdXRhdGUoYmFycmlvcyA9IGJpbihtZWFuX3ByaWNlLCBuYmlucyA9IDMsIGxhYmVscyA9IGMoIkJBSk8iLCAiTUVESU8iLCAiQUxUTyIpKSkgDQpnZ3Bsb3QobWV0cm9zX2JhcnJpbyxhZXMoYmFycmlvcykpK2dlb21faGlzdG9ncmFtKHN0YXQ9ImNvdW50IikgICsgZ2d0aXRsZSgnR3J1cG9zIHBvciBCYXJyaW9zJykNCg0KYGBgDQo8aDI+Q2FsY3VsYXIgZWwgbW9kZWxvIHF1ZSBwcmVkaWNlIGVsIHByZWNpbyBlbiBmdW5jacOzbiBkZSBsYXMgbnVldmFzIGNvdmFyaWFibGVzIGUgaW50ZXJwcmV0YXIgc3VzIHJlc3VsdGFkb3MgKHRvZGFzIGxhcyBwYXJ0ZXMgZGUgbGEgc2FsaWRhIHF1ZSBjb25zaWRlcmVuIHJlbGV2YW50ZXMpPC9oMj4NCmBgYHtyfQ0KIyBDb21iaW5vIGVsIGRhdGFzZXQgb3JpZ2luYWwgY29uIGVsIG51ZXZvIHF1ZSBjb250aWVuZSBsb3MgZ3J1cG9zIGRlIGxvcyBiYXJyaW9zDQpwcm9wZXJ0aWVzIDwtIG1lcmdlKHByb3BlcnRpZXMsbWV0cm9zX2JhcnJpbyxieT0nbDMnLGFsbC54ID0gVCkNCiMgcXVpdG8gZWwgcHJlY2lvIHByb21lZGlvIHkgbG9zIGJhcnJpb3MgcGFyYSBlbCBudWV2byBtb2RlbG8NCnByb3BlcnRpZXNfbmV3IDwtIHByb3BlcnRpZXMgJT4lIHNlbGVjdCgtYyhsMyxtZWFuX3ByaWNlKSkNCiNjcmVvIGVsIG51ZXZvIG1vZGVsbw0KbW9kZWxfbmV3IDwtIGxtKHByaWNlfnJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV90b3RhbCArIHN1cmZhY2VfY292ZXJlZCArIGJhcnJpb3MgKyBwcm9wZXJ0eV90eXBlLHByb3BlcnRpZXNfbmV3KQ0Kc3VtbWFyeShtb2RlbF9uZXcpDQpgYGANCjxwPkNvbiBlbCBudWV2byBtb2RlbG8gcG9kZW1vcyB2ZXIgcXVlIHRvZGFzIGxhcyBjb3ZhcmlhYmxlcyBzb24gc2lnbmlmaWNhdGl2YXMuIEVsIFIyIG5vIHZhcmnDsyBtdWNoby48L3A+DQo8YnIvPjxoMz5jLiDCv1F1w6kgbW9kZWxvIGV4cGxpY2EgbWVqb3IgbGEgdmFyaWFiaWxpZGFkIGRlIGxvcyBkYXRvcywgZWwgcXVlIHV0aWxpemEgbGEgdmFyaWFibGUgbDMgbyBlbCBxdWUgdXRpbGl6YSBiYXJyaW8/IEVuIHN1IG9waW5pw7NuLCDCv1F1w6kgbW9kZWxvIGVzIG3DoXMgw7p0aWw/IMK/UG9ycXXDqT88L2gzPg0KPHA+RWwgbW9kZWxvIHF1ZSBwYXJlY2UgZXhwbGljYXIgbWVqb3IgbGEgdmFyaWFiaWxpZGFkIGVzIGVsIHF1ZSBjb250aWVuZSBhIGwzIHBvciB0ZW5lciB1biBSMiBBanVzdGFkbyBtYXlvcih1biBwb2NvKS4gVGFtYmllbiwgY29uIGVsIHByaW1lciBtb2RlbG8gdGVuZW1vcyBpbmZvcm1hY2lvbiBzb2JyZSBsb3MgYmFycmlvcyBkZSBmb3JtYSBpbmRpdmlkdWFsLCBxdWUgY29uIGVsIG1vZGVsbyBkZSBiYXJyaW9zIG5vLjwvcD4NCjxoMz5MYSBpbnRlcnByZXRhY2nDs24gZGUgbG9zIGNvZWZpY2llbnRlcyBkZSBsYXMgdmFyaWFibGVzIHN1cmZhY2VfY292ZXJlZCB5IHN1cmZhY2VfdG90YWwgcHVlZGUgc2VyIHVuIHBvY28gcHJvYmxlbcOhdGljYSB5YSBxdWUgc2UgZW5jdWVudHJhbiBjb3JyZWxhY2lvbmFkYXMuIEVudG9uY2VzLCBwb2RlbW9zIGNvbnN0cnVpciB1bmEgbnVldmEgdmFyaWFibGUgc3VyZmFjZV9wYXRpbyBwYXJhIGxhIGRpZmVyZW5jaWEgZW50cmUgYW1iYXMgc3VwZXJmaWNpZXM6DQpDb25zdHJ1aXIgdW5hIG51ZXZhIHZhcmlhYmxlIHN1cmZhY2VfcGF0aW8uIERhZG8gcXVlIGFsZ3Vub3MgcmVnaXN0cm9zIHB1ZWRlbiBjb250ZW5lciBsYSBjb250cmFkaWNjacOzbiBkZSBxdWUgc3VyZmFjZV90b3RhbDxzdXJmYWNlX2NvdmVyZWQsIGV4cGxpY2l0YXIgY8OzbW8gc2UgcHJvY2VkZSBwYXJhIGRpY2hvcyBjYXNvcy4NCkNhbGN1bGFyIG51ZXZhbWVudGUgZWwgbW9kZWxvIGxpbmVhbCBwYXJhIHRvZGFzIGxhcyBjb3ZhcmlhYmxlcyBwcmV2aWFzIChleGNlcHRvIHN1cmZhY2VfdG90YWwpLCBzdXJmYWNlX2NvdmVyZWQgeSBzdXJmYWNlX3BhdGlvIGUgaW50ZXJwcmV0YXIgbG9zIGNvZWZpY2llbnRlcyBkZSBlc3RhcyBkb3Mgw7psdGltYXMgdmFyaWFibGVzPC9oMz4NCmBgYHtyfQ0KcHJvcGVydGllc19uZXcgPC0gcHJvcGVydGllc19uZXcgJT4lIA0KICBtdXRhdGUoc3VyZmFjZV9wYXRpbyA9IHN1cmZhY2VfdG90YWwtc3VyZmFjZV9jb3ZlcmVkKQ0KDQojIFZlbW9zICBsYSBudWV2YSB2YXJpYWJsZSANCnN1bW1hcnkocHJvcGVydGllc19uZXckc3VyZmFjZV9wYXRpbykNCg0KbW9kZWxfbmV3IDwtIGxtKHByaWNlfnJvb21zICsgYmF0aHJvb21zICsgc3VyZmFjZV9wYXRpbyArIHN1cmZhY2VfY292ZXJlZCArIGJhcnJpb3MgKyBwcm9wZXJ0eV90eXBlLHByb3BlcnRpZXNfbmV3KQ0Kc3VtbWFyeShtb2RlbF9uZXcpDQpgYGANCjxwPnN1cmZhY2VfcGF0aW8gbm8gY29udGllbmUgY29udHJhZGljY2lvbmVzLiBTaSBsYXMgdHV2aWVyYSwgbG8gY29uc2lkZXJhcmlhIHVuIGVycm9yIHkgbGFzIGJvcnJhcsOtYS4gRWwgY29lZmljaWVudGUgZXN0aW1hZG8gcGFyYSBsYSB2YXJpYWJsZSBzdXJmYWNlX2NvdmVyZWQgc2UgaW50ZXJwcmV0YSBjb21vIGVsIGF1bWVudG8gZGVsIHByZWNpbyBwcm9tZWRpbyBkZSB1bmEgcHJvcGllZGFkIGN1YW5kbyBsYSBzdXBlcmZpY2llIGN1YmllcnRhIGF1bWVudGEgZW4gdW4gbWV0cm8gY3VhZHJhZG8uIEFuw6Fsb2dhbWVudGUgc2UgcHVlZGUgaW50ZXJwcmV0YXIgc3VyZmFjZV9wYXRpby4gRWwgYXVtZW50byBkZWwgcHJlY2lvIGVzIG1heW9yIGN1YW5kbyBhdW1lbnRhIGxhIHN1cGVyZmljaWUgY3ViaWVydGEgcXVlIGN1YW5kbyBhdW1lbnRhIGxhIHN1cGVyZmljaWUgZGVzY3ViaWVydGEuPC9wPg0KPGgyPkV2YWx1YWNpw7NuIGRlbCBtb2RlbG88L2gyPg0KPGgzPkFuYWxpemFyIGxvcyByZXNpZHVvcyBkZWwgbW9kZWxvIGVsYWJvcmFkbyBlbiAyLmQ8L2gzPg0KYGBge3J9DQpwbG90KG1vZGVsX25ldykNCmBgYA0KPHA+RWwgYW7DoWxpc2lzIGVzIGVsIG1pc21vIHF1ZSBlbiBlbCBjYXNvIGFudGVyaW9yLCBubyBzZSBwcmVzZW50YSBob21vZWRhc3RpY2lkYWQgZGUgbGEgdmFyaWFuemEgeSBsb3MgcmVzaWR1b3Mgbm8gc2lndWVuIHVuYSBkaXN0cmlidWNpw7NuIG5vcm1hbC4gRW4gZWwgw7psdGltbyBncsOhZmljb3NlIG9ic2VydmEgcXVlIGhheSBwdW50b3MgYWxlamFkb3MgcGVybyBlbCBsZXZlcmFnZSBlcyBtZW5vciBxdWUgZW4gbGEgZXZhbHVjacOzbiBkZWwgbW9kZWxvIGFudGVyaW9yLjwvcD4NCjxoMz5DYWxjdWxhciBlbCBtb2RlbG8NCkNvbXBhcmFyIGxhIHBlcmZvcm1hbmNlIGRlbCBtb2RlbG8gZGUgMi5kIGNvbiDDqXN0ZSwgdGFudG8gZW4gdMOpcm1pbm9zIGRlIGxhIHZhcmlhYmlsaWRhZCBleHBsaWNhZGEgY8OzbW8gZGUgc3UgcmVsYWNpw7NuIGNvbiBsb3Mgc3VwdWVzdG9zIGRlbCBtb2RlbG8gbGluZWFsLiBSZS1pbnRlcnByZXRhciBsb3MgcGFyw6FtZXRyb3MgZGVsIG1vZGVsby48L2gzPg0KYGBge3J9DQpwcm9wZXJ0aWVzX2xvZyA8LSBwcm9wZXJ0aWVzX25ldyAlPiUNCiAgbXV0YXRlKHByaWNlPWxvZyhwcmljZSkscm9vbXM9bG9nKHJvb21zKSxiYXRocm9vbXM9bG9nKGJhdGhyb29tcyksc3VyZmFjZV9jb3ZlcmVkPWxvZyhzdXJmYWNlX2NvdmVyZWQpKQ0KDQptb2RlbF9sb2cgPC0gbG0ocHJpY2V+cm9vbXMrYmF0aHJvb21zK3N1cmZhY2VfcGF0aW8rc3VyZmFjZV9jb3ZlcmVkK3Byb3BlcnR5X3R5cGUrYmFycmlvcyxkYXRhID0gcHJvcGVydGllc19sb2cpDQpzdW1tYXJ5KG1vZGVsX2xvZykNCmBgYA0KPHA+RXN0ZSBudWV2byBtb2RlbG8gbWVqb3JhIGxhIGV4cGxpY2FjacOzbiBkZSBsYSB2YXJpYWJpbGlkYWQgY29uIHVuIFIyIEFqdXN0YWRvPTAuODIwMiBmcmVudGUgYWwgYW50ZXJpb3IsIGF1bnF1ZSBzZSBwaWVyZGUgbGEgaW50ZXJwcmV0YWJpbGlkYWQuPC9wPg0KYGBge3J9DQpwbG90KG1vZGVsX2xvZykNCmBgYA0KPHA+RW4gZXN0ZSBjYXNvIGxvcyByZXNpZHVvcyBubyBwYXJlY2VuIHRlbmVyIHVuYSBlc3RydWN0dXJhIGFjZXJjYW5kb2NlIG1hcyBhIGxhIGhvbW9jZWRhc3RpY2lkYWQgZGUgbGEgdmFyaWFuemEuIExhIHJlY3RhIGVuIGVsIFFRIHBsb3Qgc2UgYXBlZ2EgbXVjaG8gbWFzIGxvcyBwdW50b3MgZGVsIGdyYWZpY28sIGluZGljYW5kbyBxdWUgbG9zIHJlc2lkdW9zIHNpZ3VlbiBsYSBkaXN0cmlidWNpw7NuIG5vcm1hbC48L3A+DQo8aDI+RGF0YWZyYW1lcyBhbmlkYWRvczwvaDI+DQo8aDM+QW5pZGFyIHBvciBsYSB2YXJpYWJsZSBwcm9wZXJ0eV90eXBlPC9oMz4NCmBgYHtyfQ0KcHJvcGVydGllc19uZXN0ZWQgPC0gcHJvcGVydGllcyAlPiUNCiAgICAgICAgICAgbXV0YXRlKHN1cmZhY2VfcGF0aW8gPSBzdXJmYWNlX3RvdGFsICAtIHN1cmZhY2VfY292ZXJlZCkgJT4lDQogICAgICAgICAgICBzZWxlY3QoLWMoc3VyZmFjZV90b3RhbCkpICU+JQ0KICAgICAgICAgICBncm91cF9ieShwcm9wZXJ0eV90eXBlKSAlPiUNCiAgICAgICAgICAgICBuZXN0KCkNCmhlYWQocHJvcGVydGllc19uZXN0ZWQpDQoNCmBgYA0KPGgzPkNvbnN0cnVpciBwYXJhIGNhZGEgdGlwbyBkZSBwcm9waWVkYWQgZWwgbW9kZWxvIGRlIDIuZCBlIGludGVycHJldGFyIGxvcyByZXN1bHRhZG9zIGVuIGNhZGEgY2Fzby4gUXXDqSBkaWZlcmVuY2lhcyBlbmN1ZW50cmFuIGVudHJlIGxvcyBkaXN0aW50b3MgbW9kZWxvcz88L2gzPg0KYGBge3J9DQoNCmRmX21vZGVsIDwtIGZ1bmN0aW9uKGRhdG9zKSB7DQogICAgICBkYXRvc190ZW1wIDwtIGRhdG9zICU+JSBkdW1teV9jb2xzKCkgJT4lIHNlbGVjdCgtYmFycmlvcykNCiAgICAgIG1vZGVsbyA8LSBsbSgicHJpY2Ugfi4gIiAsIGRhdGEgPSBkYXRvc190ZW1wKQ0KICAgICAgcmV0dXJuKG1vZGVsbykNCn0NCg0KbW9kZWxfbmVzdGVkIDwtIHByb3BlcnRpZXNfbmVzdGVkICU+JQ0KICAgICAgICAgICAgICAgICAgbXV0YXRlKG1vZGVsbyA9IG1hcChkYXRhLCBkZl9tb2RlbCkpICU+JQ0KICAgICAgICAgICAgICAgICAgbXV0YXRlKGRlc2NyaXBjaW9uID0gbWFwKG1vZGVsbywgZ2xhbmNlKSkgJT4lIA0KICAgICAgICAgICAgICAgICAgdW5uZXN0KGRlc2NyaXBjaW9uKQ0KICAgICAgICAgICAgICAgICAgIA0KbW9kZWxfbmVzdGVkDQpgYGA=