ANÁLISIS VARIABILIDAD CLIMÁTICA

Introducción

La variabilidad climática en una cuenca hidrográfica es un tema de gran importancia debido a su impacto en la gestión del agua, la agricultura, la biodiversidad y la seguridad alimentaria de las poblaciones que dependen de los recursos hídricos de esa región. Las cuencas hidrográficas son áreas geográficas naturales donde todas las aguas superficiales y subterráneas fluyen hacia un mismo punto de salida, ya sea un río principal, un lago o un océano. Esta característica hace que sean sistemas sensibles a los cambios climáticos.

La variabilidad climática se refiere a las fluctuaciones naturales en los patrones del clima a lo largo del tiempo y el espacio. Estas fluctuaciones pueden manifestarse en cambios en la temperatura, precipitación, humedad, vientos y otros parámetros climáticos. En una cuenca hidrográfica, la variabilidad climática puede influir en la cantidad, distribución y temporalidad de las precipitaciones, así como en la evaporación y la disponibilidad de agua superficial y subterránea.

Los cambios en el clima pueden ser causados por factores naturales, como variaciones en la actividad solar, oscilaciones climáticas naturales como El Niño y La Niña, y fenómenos atmosféricos como el cambio en la circulación atmosférica. Sin embargo, en las últimas décadas, la actividad humana, especialmente la emisión de gases de efecto invernadero, ha contribuido significativamente al calentamiento global y al cambio climático, exacerbando la variabilidad climática y generando impactos más pronunciados en las cuencas hidrográficas.

Informe Detallado

Dar click en la palabra INFORME para poder obtener mas información del informe.

Análisis de Resultados

A partir del año 1980 hasta el 2014 se han producido 10 eventos cálidos del Niño, empezando en abril/82 hasta junio/83, septiembre/86 hasta febrero/88, junio/91 hasta julio/92, octubre/94 hasta marzo/95, mayo/97 hasta mayo/98, junio/02 hasta febrero/03, julio/04 hasta abril/05, septiembre/06 hasta enero/07, jul abril/2010, noviembre/14 hasta diciembre/14, y siete eventos fríos de La Niña que ocurrieron en octubre/84 hasta junio/85, mayo/88 hasta mayo/89, agosto/95 hasta marzo/96, julio/98 hasta febrero/01, agosto/07 hasta junio/08, julio/10 hasta abril/11, agosto/11 hasta febrero/12.

Figura N°1: Precipitación Periodo 1979-2019 Estación Parque Bicentenario
Figura N°1: Precipitación Periodo 1979-2019 Estación Parque Bicentenario
Figura N°2: Temperatura Periodo 1979-2019 Estación Parque Bicentenario
Figura N°2: Temperatura Periodo 1979-2019 Estación Parque Bicentenario
Figura N°3: Índice Estandarizado de Sequía 1979-2019 Estación Parque Bicentenario
Figura N°3: Índice Estandarizado de Sequía 1979-2019 Estación Parque Bicentenario
Figura N°4: Índice Estandarizado de Sequía 1979-2019 Estación Canal 10TV
Figura N°4: Índice Estandarizado de Sequía 1979-2019 Estación Canal 10TV

MODELO ESTADÍSTICO “RANDOM FOREST” EN RSTUDIO

TEMPERATURA

1. Instalación y carga de librerías.

El primer paso es la instalación de las librerías utilizando el comando install.packages() esto es fundamental para el desarrollo de nuestro código. Ya que se necesitan algunos paquetes como caret, randomforest, lubridate, ggplot2. Sin estas librerías el codigo no va a correr.

A continuación las librerías que vamos a utilizar en el código.

install.packages(“lubridate”)

install.packages(“dplR”)

install.packages(“tidyr”)

install.packages(“ggplot2”)

install.packages(“caret”)

install.packages(“randomforest”)

install.packages(“zoo”)

Ahora tenemos que llamar a los paquetes instalados con el comando library(), y dentro de los parentesis escribir los paquetes que solicitamos anteriormente.

library(lubridate)

library(dplyr)

library(tidyr)

library(ggplot2)

library(caret)

library(randomForest)

library(zoo)

2. Analisis de los Datos.

Los datos obtenidos se descargaron de la página web Datos Abiertos, esta es una página anexada al INAHMI.

Vamos a realizar un análisis previo de nuestros datos para visualizar el comportamiento en serie de tiempo.

2.1. Carga de los Datos de Temperatura y Precipitación.

Ahora tenemos que hacer la carga de nuestros datos los cuales se encuentran en formato csv (texto delimitado por comas), es recomendado tener en este formato ya que R lo reconoce sin necesidad de agregar una libreria adicional.

setwd("~/ING AMBIENTAL/INFORMATICA23-24/Proyecto")
temp <- read.csv("Temp_Final.csv", sep = ",", dec = ".", header = T)

2.2. Creación de Serie de Tiempo para análisis de datos.

Una serie de tiempo es una secuencia de datos u observaciones medidos en determinados momentos, en intervalos iguales y ordenados cronológicamente.

El análisis de series de tiempo se refiere al proceso de analizar los datos disponibles para descubrir el patrón o la tendencia en los datos. Permite extraer y modelar las relaciones entre datos a lo largo del tiempo.

temp_ts <- ts(temp$Temperatura, frequency = 12, start = c(1975,1))
plot(temp_ts, main = "Serie de Tiempo de Temperatura 1975-2019", xlab = "Años", ylab = "Temperatura (°C)")

2.3. Componentes de la Serie de Tiempo.

Utilizaremos el comando decompose() el cual desagrega la serie de tiempo en los componentes: tendencia, estacional, cíclico y aleatorio.

temp_ts_descomp <- decompose(temp_ts) 
2.3.1. Tendencia

Es el patrón subyacente en los datos a lo largo del tiempo. No es necesariamente lineal.

plot(temp_ts_descomp$trend, main = "Tendencia de Temperatura", col = "blue", ylab = "Temperatura (°C)")

2.3.2. Estacionalidad

Cuando una serie esta influenciada por factores estacionales de periodo fijo como el día, mes, trimestre, año, etc.

plot(temp_ts_descomp$seasonal, main = "Estacionalidad de Temperatura", col = "red", ylab = "Temperatura (°C)")

2.3.3. Aleatoriedad

Parte inexplicable de los datos medidos.

plot(temp_ts_descomp$random, main = "Irregularidad de Temperatura", col = "green", ylab = "Temperatura (°C)")

3. Creación de variables previo al modelamiento.

En este apartado del código de R, creamos las variables de las fechas en las que se tomaron los datos según el INAHMI como podemos oberservar en nuestro código la primera fecha que se tomaron datos de temperatura están en “date_inicial” y la última fecha que se reportaron datos es “date_final”.

Ademas podemos notar que tenemos dos variables adicionales “fecha_final” la cual nos indica el mes siguiente a la última toma de datos reportados por el INAHMI y “fecha_pred” es la fecha de predicción que fijamos para un año.

fecha_final <- as.Date("2019-07-01")

fecha_pred <- as.Date("2020-07-01")

date_inicial <- as.Date("1975-01-01")

date_final <- as.Date("2019-06-01")

Fecha <- as.Date(seq(from = date_inicial, to = date_final, by="month"))

4. Preparación de Datos para el modelamiento.

4.1. Conversión de Datos a un data frame.

Los dataframes son una clase de objetos especial en R. Normalmente, cuando se realiza un estudio estadístico sobre los sujetos u objetos de una muestra, la información se organiza precisamente en un dataframe: una hoja de datos, en los que cada fila corresponde a un sujeto y cada columna a una variable. La estructura de un data.frame es muy similar a la de una matriz. La diferencia es que una matriz sólo admite valores numéricos, mientras que en un dataframe podemos incluir también datos alfanuméricos.

temp <- as.data.frame(temp)
head(temp, n = 60)
##         Fecha Temperatura
## 1  01/01/1975        13.5
## 2  01/02/1975        12.8
## 3  01/03/1975        13.3
## 4  01/04/1975        13.7
## 5  01/05/1975        13.5
## 6  01/06/1975        13.5
## 7  01/07/1975        12.3
## 8  01/08/1975        13.1
## 9  01/09/1975        13.1
## 10 01/10/1975        12.8
## 11 01/11/1975        12.6
## 12 01/12/1975        12.7
## 13 01/01/1976        12.9
## 14 01/02/1976        13.0
## 15 01/03/1976        13.5
## 16 01/04/1976        13.7
## 17 01/05/1976        13.8
## 18 01/06/1976        13.6
## 19 01/07/1976        14.2
## 20 01/08/1976        14.0
## 21 01/09/1976        14.8
## 22 01/10/1976        13.6
## 23 01/11/1976        13.8
## 24 01/12/1976        13.8
## 25 01/01/1977        14.4
## 26 01/02/1977        13.9
## 27 01/03/1977        15.0
## 28 01/04/1977        14.2
## 29 01/05/1977        14.2
## 30 01/06/1977        13.7
## 31 01/07/1977        14.8
## 32 01/08/1977        14.5
## 33 01/09/1977        15.0
## 34 01/10/1977        14.3
## 35 01/11/1977        14.5
## 36 01/12/1977        15.7
## 37 01/01/1978        14.4
## 38 01/02/1978        15.4
## 39 01/03/1978        14.4
## 40 01/04/1978        14.2
## 41 01/05/1978        14.6
## 42 01/06/1978        14.6
## 43 01/07/1978        14.4
## 44 01/08/1978        15.0
## 45 01/09/1978        14.4
## 46 01/10/1978        14.4
## 47 01/11/1978        15.2
## 48 01/12/1978        14.2
## 49 01/01/1979        14.4
## 50 01/02/1979        14.7
## 51 01/03/1979        14.2
## 52 01/04/1979        14.8
## 53 01/05/1979        14.5
## 54 01/06/1979        14.7
## 55 01/07/1979        14.9
## 56 01/08/1979        14.8
## 57 01/09/1979        14.2
## 58 01/10/1979        14.7
## 59 01/11/1979        14.5
## 60 01/12/1979        14.7

Posterior a eso empleamos el comando cbind() para combinar las dos columnas en un nuevo marco de datos. Combinamos la columna de temperaturas con una secuencia de fechas para formar un nuevo marco de datos.

temp <- as.data.frame(cbind(temp$Temperatura, Fecha))

Renombramos las columnas del marco de datos a ‘Temperatura’ y ‘Fecha’.

names(temp) <- c("Temperatura", "Fecha")

Después convertimos la columna de ‘Fecha’ a objetos de fecha en R empleando el comando as.Date(). Esto nos asegura que los datos de esta columna sean tratados como fechas.

temp$Fecha <- as.Date(temp$Fecha)

En resumen, hasta esta parte, el código toma un objeto de datos llamado ‘temp’, lo convierte en un marco de datos que solicita R, combina la columna de temperatura con una secuencia de fechas, renombramos las columnas y aseguramos que la columna de fechas este en el formato adecuados para su manipulación como fechas.

4.2. Creación del Data Frame para la muestra de los resultados según el modelo.

Creamos un nuevo data frame para los datos que vamos a predecir. Con este paso se crea una secuencia de fechas predichas desde la ‘fecha_final’ hasta ‘fecha_pred’ en intervalos mensuales.

fecha_predic <- as.Date(seq(from = fecha_final, to = (fecha_pred), by = "month"))

Inicializamos un vector ‘Temperatura’ con los valores ‘NA’ y combinamos este vector con el vector de fechas predichas para crear un nuevo marco de datos denominado ‘fecha_predic’. Y también convertimos la columna ‘fecha_predic’ a un formato fecha y renombramos las columnas a ‘Temperatura’ y ‘Fecha’.

Temperatura <- as.numeric(NA)
fecha_predic <- as.data.frame(cbind(Temperatura, fecha_predic))
fecha_predic$fecha_predic <- as.Date(fecha_predic$fecha_predic)
names(fecha_predic) <- c("Temperatura", "Fecha")

Combinamos los datos originales ‘temp’ con las fechas predichas ‘fecha_predic’ empleando el comando rbind(), el cual añade las filas al marco de datos ‘temp’.

temp <- rbind(temp, fecha_predic)

Duplicamos la columna fechas para no perder los datos en el proceso de modelamiento y también separamos esta columna en ‘Año’, ‘Mes’, ‘Dia’.

temp$Fecha_dup <- temp$Fecha

temp <- temp %>% separate(Fecha, c("Año", "Mes", "Dia"))

En el siguiente código convertimos cada una de las columnas ‘Año’, ‘Mes’, ‘Dia’ en carácter numérico.

temp$Año <- as.numeric(temp$Año)
temp$Mes <- as.numeric(temp$Mes)
temp$Dia <- as.numeric(temp$Dia)

5. Modelamiento

5.1. Creación del conjunto de datos de entrenamiento y testeo.

Establecemos la semilla (‘seed’) para que los resultados sean reproducibles en el futuro. Esto quiere decir que, si se ejecuta el código varias veces, se obtendrá los mismos resultados en cada ejecución, siempre y cuando se use la misma semilla.

Empleamos la función ‘createDataPartition’ del paquete ‘caret’ para crear un índice que indica que observaciones se incluirá en el conjunto de entrenamiento. Se filtran las observaciones y se elimina las filas con los valores faltantes en la columna ‘Temperatura’

set.seed(1996)
train <- createDataPartition(na.omit(subset(temp, temp$Fecha_dup < fecha_final))$Temperatura
                             , p = 0.7, list = F) 

El valor ‘p=0.7’ nos indica que se van a emplear el 70% de los datos como conjunto de entrenamiento. También especificamos que queremos un vector de índice en lugar de una lista con el subcomando ‘list = FALSE’.

Empleamos el índice ‘train’ para seleccionar las observaciones correspondiste al conjunto de prueba utilizado la notación del subconjunto (‘temp[-train,]’). Combinamos estas observaciones con las que tienen ‘Fecha_dup’ mayor o igual a ‘fecha_final’ empleando ‘rbind’.

test <- rbind(temp[-train,] , subset(temp, temp$Fecha_dup >= fecha_final))

En conclusión, en esta parte se crea un conjunto de entrenamiento y test. El conjunto de entrenamiento incluye datos hasta la fecha final, mientras que el conjunto de prueba incluye datos desde la fecha final hacia adelante. Cabe recalcar que en el conjunto de entrenamiento se selecciona el 70% de las observaciones.

5.2. Ejecución del algoritmo Random Forest.

Este algoritmo construye múltiples arboles de clasificación aleatorios dependiendo de los datos con los que se está trabajando. En los pasos anteriores se prepararon todos los conjuntos de datos adecuadamente para que se pueda realizar los procesos correspondientes. Además de definió el número de árboles que se van han construir a través del argumento “ntree”

mod_rf <- randomForest(Temperatura ~ Fecha_dup, data = temp[train,], 
                       type = "regression", ntree = 500)

Creamos las diferentes variables donde se van a almacenar los resultados de los procesos dependiendo de los índices que se establecieron como parámetros del modelamiento, con lo que tenemos una variable de resultado para la predicción y otra donde se almacenaran la combinación de los datos resultantes de la predicción.

pred_rf <- predict(mod_rf, test)

datos_rf <- cbind(pred_rf, test)

5.3. Errores para la comprobación del modelo.

Luego de esto vamos a determinar el porcentaje de error que se obtiene en la modelación realizada con lo cual podemos establecer si está dentro del rango aceptable. Esto nos garantiza que los datos que se han obtenido sean tratados como datos confiables para determinar predicciones futuras aplicando esta metodología y basado en el análisis de que se ha venido realizando en la toma y tratamiento de datos.

error_abs_rf <-RMSE(datos_rf$Temperatura, datos_rf$pred_rf, na.rm = T)

error_por_rf <- error_abs_rf / datos_rf[datos_rf$Fecha_dup == max(na.omit(datos_rf)$Fecha_dup), ]$Temperatura
error_por_rf*100
## [1] 3.628301
MAE(datos_rf$Temperatura, datos_rf$pred_rf, na.rm = T)
## [1] 0.4414555

6. Grafico del modelo resultante.

Finalmente se va a dibujar el modelo que se obtuvo como resultado del proceso realizado, en el cual se incluyen el comportamiento real líneas en negro y el modelo calculado líneas en rojo, así como el título de la estación que hemos tomado como referencia para la ejecución del modelamiento.

ggplot() + geom_line(data = datos_rf, aes(x = Fecha_dup, y = Temperatura), color = "black") +
  geom_line(data = datos_rf, aes(x = Fecha_dup, y = pred_rf), color = "red") + 
  ggtitle("Modelo de Predicción para Temperatura Estacion Iñaquito ")
## Warning: Removed 26 rows containing missing values (`geom_line()`).

Ademas adjuntamos la tabla de los resultados para nuestro modelo y los datos originales.

names(datos_rf) <- c("Temperatura Modelo", "Temperatura", "Año", "Mes", "Día", "Fecha")
head(datos_rf, n = 60)
##     Temperatura Modelo Temperatura  Año Mes Día      Fecha
## 1             13.43692        13.5 1975   1   1 1975-01-01
## 2             13.43692        12.8 1975   2   1 1975-02-01
## 3             13.43692        13.3 1975   3   1 1975-03-01
## 5             13.43692        13.5 1975   5   1 1975-05-01
## 20            14.28072        14.0 1976   8   1 1976-08-01
## 23            13.91197        13.8 1976  11   1 1976-11-01
## 32            14.49319        14.5 1977   8   1 1977-08-01
## 33            14.62774        15.0 1977   9   1 1977-09-01
## 35            15.10986        14.5 1977  11   1 1977-11-01
## 39            14.98644        14.4 1978   3   1 1978-03-01
## 40            14.74163        14.2 1978   4   1 1978-04-01
## 42            14.60976        14.6 1978   6   1 1978-06-01
## 49            14.39974        14.4 1979   1   1 1979-01-01
## 59            14.61820        14.5 1979  11   1 1979-11-01
## 62            15.13674        13.8 1980   2   1 1980-02-01
## 64            15.19141        14.7 1980   4   1 1980-04-01
## 65            15.20081        15.2 1980   5   1 1980-05-01
## 70            15.37153        14.8 1980  10   1 1980-10-01
## 71            15.31963        14.0 1980  11   1 1980-11-01
## 72            14.79745        14.3 1980  12   1 1980-12-01
## 73            14.58581        14.6 1981   1   1 1981-01-01
## 75            14.56820        14.5 1981   3   1 1981-03-01
## 78            14.43167        14.5 1981   6   1 1981-06-01
## 80            14.51902        14.8 1981   8   1 1981-08-01
## 83            14.85515        14.2 1981  11   1 1981-11-01
## 84            14.38721        14.8 1981  12   1 1981-12-01
## 89            14.60565        14.4 1982   5   1 1982-05-01
## 94            14.95426        14.0 1982  10   1 1982-10-01
## 103           14.91448        14.9 1983   7   1 1983-07-01
## 104           14.88402        15.3 1983   8   1 1983-08-01
## 108           14.46551        13.5 1983  12   1 1983-12-01
## 111           13.34742        14.1 1984   3   1 1984-03-01
## 114           13.87431        14.0 1984   6   1 1984-06-01
## 118           14.06019        13.9 1984  10   1 1984-10-01
## 119           14.32078        13.7 1984  11   1 1984-11-01
## 124           14.13371        14.1 1985   4   1 1985-04-01
## 128           13.62885        13.3 1985   8   1 1985-08-01
## 131           13.58681        13.2 1985  11   1 1985-11-01
## 138           14.88382        15.0 1986   6   1 1986-06-01
## 141           14.98001        15.3 1986   9   1 1986-09-01
## 142           14.86136        14.3 1986  10   1 1986-10-01
## 145           15.14127        15.1 1987   1   1 1987-01-01
## 153           15.06085        15.2 1987   9   1 1987-09-01
## 161           14.38180        14.6 1988   5   1 1988-05-01
## 163           14.28816        14.2 1988   7   1 1988-07-01
## 166           14.21512        14.0 1988  10   1 1988-10-01
## 168           13.91160        13.2 1988  12   1 1988-12-01
## 171           13.90957        13.0 1989   3   1 1989-03-01
## 172           14.13955        14.5 1989   4   1 1989-04-01
## 178           14.91661        14.4 1989  10   1 1989-10-01
## 183           14.63241        15.3 1990   3   1 1990-03-01
## 186           14.90800        15.5 1990   6   1 1990-06-01
## 189           15.36049        15.6 1990   9   1 1990-09-01
## 190           15.21517        14.0 1990  10   1 1990-10-01
## 192           15.15899        14.9 1990  12   1 1990-12-01
## 195           15.18462        14.6 1991   3   1 1991-03-01
## 196           15.11567        14.7 1991   4   1 1991-04-01
## 198           15.12156        15.6 1991   6   1 1991-06-01
## 199           15.08033        15.0 1991   7   1 1991-07-01
## 200           15.11168        15.0 1991   8   1 1991-08-01

PRECIPITACIÓN

1. Analisis de los Datos.

Los datos obtenidos se descargaron de la página web Datos Abiertos, esta es una página anexada al INAHMI.

Vamos a realizar un análisis previo de nuestros datos para visualizar el comportamiento en serie de tiempo.

1.1. Carga de los Datos de Precipitación.

Ahora tenemos que hacer la carga de nuestros datos los cuales se encuentran en formato csv (texto delimitado por comas), es recomendado tener en este formato ya que R lo reconoce sin necesidad de agregar una libreria adicional.

setwd("~/ING AMBIENTAL/INFORMATICA23-24/Proyecto")
preci <- read.csv("Preci_Final.csv", sep = ",", dec = ".", header = T)

1.2. Creación de Serie de Tiempo para análisis de datos.

Una serie de tiempo es una secuencia de datos u observaciones medidos en determinados momentos, en intervalos iguales y ordenados cronológicamente.

El análisis de series de tiempo se refiere al proceso de analizar los datos disponibles para descubrir el patrón o la tendencia en los datos. Permite extraer y modelar las relaciones entre datos a lo largo del tiempo.

preci_ts <- ts(preci$Parque.Bicentenario, frequency = 12, start = c(1975,1))
plot(preci_ts, main = "Serie de Tiempo de Precipitación 1975-2019", xlab = "Años", ylab = "Precipitación (mm)")

1.3. Componentes de la Serie de Tiempo.

Utilizaremos el comando decompose() el cual desagrega la serie de tiempo en los componentes: tendencia, estacional, cíclico y aleatorio.

preci_ts_descomp <- decompose(preci_ts) 
1.3.1. Tendencia

Es el patrón subyacente en los datos a lo largo del tiempo. No es necesariamente lineal.

plot(preci_ts_descomp$trend, main = "Tendencia Precipitación", col = "blue", ylab = "Precipitación (mm)")

1.3.2. Estacionalidad

Cuando una serie esta influenciada por factores estacionales de periodo fijo como el día, mes, trimestre, año, etc.

plot(preci_ts_descomp$seasonal, main = "Estacionalidad Precipitación", col = "red", ylab = "Precipitación (mm)")

1.3.3. Aleatoriedad

Parte inexplicable de los datos medidos.

plot(preci_ts_descomp$random, main = "Irregularidad Precipitación", col = "green", ylab = "Precipitación (mm)")

2. Preparación de Datos para el modelamiento.

2.1. Conversión de Datos a un data frame.

Los dataframes son una clase de objetos especial en R. Normalmente, cuando se realiza un estudio estadístico sobre los sujetos u objetos de una muestra, la información se organiza precisamente en un dataframe: una hoja de datos, en los que cada fila corresponde a un sujeto y cada columna a una variable. La estructura de un data.frame es muy similar a la de una matriz. La diferencia es que una matriz solo admite valores numéricos, mientras que en un dataframe podemos incluir también datos alfanuméricos.

preci <- as.data.frame(preci)
head(preci, n = 60)
##     Fecha Parque.Bicentenario
## 1  ene-75                63.5
## 2  feb-75               208.2
## 3  mar-75               206.1
## 4  abr-75               119.7
## 5  may-75               160.1
## 6  jun-75                50.1
## 7  jul-75               134.7
## 8  ago-75                46.4
## 9  sep-75                44.5
## 10 oct-75               157.8
## 11 nov-75                95.3
## 12 dic-75                51.3
## 13 ene-76                74.2
## 14 feb-76                84.6
## 15 mar-76               153.0
## 16 abr-76                92.8
## 17 may-76               122.3
## 18 jun-76                30.2
## 19 jul-76                 1.4
## 20 ago-76                 1.4
## 21 sep-76                41.2
## 22 oct-76                80.3
## 23 nov-76                90.2
## 24 dic-76               130.4
## 25 ene-77                70.6
## 26 feb-77                33.1
## 27 mar-77               142.6
## 28 abr-77                87.2
## 29 may-77                73.8
## 30 jun-77                50.5
## 31 jul-77                 9.5
## 32 ago-77                18.2
## 33 sep-77               119.6
## 34 oct-77               107.2
## 35 nov-77                19.4
## 36 dic-77               117.5
## 37 ene-78                58.8
## 38 feb-78                80.0
## 39 mar-78                81.6
## 40 abr-78               115.5
## 41 may-78                75.5
## 42 jun-78                13.5
## 43 jul-78                72.6
## 44 ago-78                 3.2
## 45 sep-78               118.8
## 46 oct-78                21.3
## 47 nov-78                75.5
## 48 dic-78                91.1
## 49 ene-79                51.8
## 50 feb-79                43.1
## 51 mar-79               119.0
## 52 abr-79               131.4
## 53 may-79               169.5
## 54 jun-79                74.8
## 55 jul-79                 9.3
## 56 ago-79                76.0
## 57 sep-79               154.8
## 58 oct-79                46.5
## 59 nov-79                51.5
## 60 dic-79                 3.5

Renombramos las columnas del marco de datos a ‘Fecha’ y ‘Precipitación’.

names(preci) <- c("Fecha", "Precipitacion")

Posterior a eso empleamos el comando cbind() para combinar las dos columnas en un nuevo marco de datos. Combinamos la columna de temperaturas con una secuencia de fechas para formar un nuevo marco de datos.

preci <- as.data.frame(cbind(Fecha, preci$Precipitacion))

Después convertimos la columna de ‘Fecha’ a objetos de fecha en R empleando el comando as.Date(). Esto nos asegura que los datos de esta columna sean tratados como fechas.

preci$Fecha <- as.Date(preci$Fecha)
names(preci) <- c("Fecha", "Precipitacion")

En resumen, hasta esta parte, el código toma un objeto de datos llamado ‘preci’, lo convierte en un marco de datos que solicita R, combina la columna de precipitación con una secuencia de fechas, renombramos las columnas y aseguramos que la columna de fechas este en el formato adecuados para su manipulación como fechas.

2.2. Creación del Data Frame para la muestra de los resultados según el modelo.

Creamos un nuevo data frame para los datos que vamos a predecir. Con este paso se crea una secuencia de fechas predichas desde la ‘fecha_final’ hasta ‘fecha_pred’ en intervalos mensuales.

fecha_predic_preci <- as.Date(seq(from = fecha_final, to = (fecha_pred), by = "month"))

Inicializamos un vector ‘Precipitación’ con los valores ‘NA’ y combinamos este vector con el vector de fechas predichas para crear un nuevo marco de datos denominado ‘fecha_predic_preci’. Y también convertimos la columna ‘fecha_predic_preci’ a un formato fecha y renombramos las columnas a ‘Fecha’ y ‘Precipitación’.

Precipitacion <- as.numeric(NA)
fecha_predic_preci <- as.data.frame(cbind(fecha_predic_preci, Precipitacion))
fecha_predic_preci$fecha_predic_preci <- as.Date(fecha_predic_preci$fecha_predic_preci)
names(fecha_predic_preci) <- c("Fecha", "Precipitacion")

Combinamos los datos originales ‘preci’ con las fechas predichas ‘fecha_predic_preci’ empleando el comando rbind(), el cual añade las filas al marco de datos ‘temp’.

preci <- rbind(preci, fecha_predic_preci)

Duplicamos la columna fechas para no perder los datos en el proceso de modelamiento y también separamos esta columna en ‘Año’, ‘Mes’, ‘Dia’.

preci$Fecha_dup_preci <- preci$Fecha

preci <- preci %>% separate(Fecha, c("Año", "Mes", "Dia"))

En el siguiente código convertimos cada una de las columnas ‘Año’, ‘Mes’, ‘Dia’ en carácter numérico.

preci$Año <- as.numeric(preci$Año)
preci$Mes <- as.numeric(preci$Mes)
preci$Dia <- as.numeric(preci$Dia)

3. Modelamiento

3.1. Creación del conjunto de datos de entrenamiento y testeo.

Establecemos la semilla (‘seed’) para que los resultados sean reproducibles en el futuro. Esto quiere decir que, si se ejecuta el código varias veces, se obtendrá los mismos resultados en cada ejecución, siempre y cuando se use la misma semilla.

Empleamos la función ‘createDataPartition’ del paquete ‘caret’ para crear un índice que indica que observaciones se incluirá en el conjunto de entrenamiento. Se filtran las observaciones y se elimina las filas con los valores faltantes en la columna ‘Temperatura’

set.seed(1997)
train_preci <- createDataPartition(na.omit(subset(preci, preci$Fecha_dup_preci < fecha_final))$Precipitacion
                             , p = 0.7, list = F) 

El valor ‘p=0.7’ nos indica que se van a emplear el 70% de los datos como conjunto de entrenamiento. También especificamos que queremos un vector de índice en lugar de una lista con el subcomando ‘list = FALSE’.

Empleamos el índice ‘train_preci’ para seleccionar las observaciones correspondiste al conjunto de prueba utilizado la notación del subconjunto (‘preci[-train_preci,]’). Combinamos estas observaciones con las que tienen ‘Fecha_dup’ mayor o igual a ‘fecha_final’ empleando ‘rbind’.

test_preci <- rbind(preci[-train_preci,] , subset(preci, preci$Fecha_dup_preci >= fecha_final))

En conclusión, en esta parte se crea un conjunto de entrenamiento y test. El conjunto de entrenamiento incluye datos hasta la fecha final, mientras que el conjunto de prueba incluye datos desde la fecha final hacia adelante. Cabe recalcar que en el conjunto de entrenamiento se selecciona el 70% de las observaciones.

3.2. Ejecución del algoritmo Random Forest.

Este algoritmo construye múltiples arboles de clasificación aleatorios dependiendo de los datos con los que se está trabajando. En los pasos anteriores se prepararon todos los conjuntos de datos adecuadamente para que se pueda realizar los procesos correspondientes. Además de definió el número de árboles que se van han construir a través del argumento “ntree”

mod_rf_preci <- randomForest(Precipitacion ~ Fecha_dup_preci, data = preci[train_preci,], 
                       type = "regression", ntree = 500)

Creamos las diferentes variables donde se van a almacenar los resultados de los procesos dependiendo de los índices que se establecieron como parámetros del modelamiento, con lo que tenemos una variable de resultado para la predicción y otra donde se almacenaran la combinación de los datos resultantes de la predicción.

pred_rf_preci <- predict(mod_rf_preci, test_preci)

datos_rf_preci <- cbind(pred_rf_preci, test_preci)

3.3. Errores para la comprobacion del modelo.

Luego de esto vamos a determinar el porcentaje de error que se obtiene en la modelación realizada con lo cual podemos establecer si está dentro del rango aceptable. Esto nos garantiza que los datos que se han obtenido sean tratados como datos confiables para determinar predicciones futuras aplicando esta metodología y basado en el análisis de que se ha venido realizando en la toma y tratamiento de datos.

error_abs_rf_preci <-RMSE(datos_rf_preci$Precipitacion, datos_rf_preci$pred_rf_preci, na.rm = T)

error_por_rf_preci <- error_abs_rf / datos_rf_preci[datos_rf_preci$Fecha_dup_preci == max(na.omit(datos_rf_preci)$Fecha_dup_preci), ]$Precipitacion
error_por_rf_preci*100
## [1] 1.578414
MAE(datos_rf_preci$Precipitacion, datos_rf_preci$pred_rf_preci, na.rm = T)
## [1] 52.16783

4. Grafico del modelo resultante

Finalmente se va a dibujar el modelo que se obtuvo como resultado del proceso realizado, en el cual se incluyen el comportamiento real líneas en negro y el modelo calculado líneas en ojo, así como el título de la estación que hemos tomado como referencia para la ejecución del modelamiento.

ggplot() + geom_line(data = datos_rf_preci, aes(x = Fecha_dup_preci, y = Precipitacion), color = "black") +
  geom_line(data = datos_rf_preci, aes(x = Fecha_dup_preci, y = pred_rf_preci), color = "red") + 
  ggtitle("Modelo de Predicción para Precipitación Estacion Iñaquito ")
## Warning: Removed 26 rows containing missing values (`geom_line()`).

Ademas adjuntamos la tabla de los resultados para nuestro modelo y los datos originales.

names(datos_rf_preci) <- c("Precipitación Modelo", "Año", "Mes", "Día", "Temperatura", "Fecha")
head(datos_rf_preci, n = 60)
##     Precipitación Modelo  Año Mes Día Temperatura      Fecha
## 3              149.81929 1975   3   1       206.1 1975-03-01
## 5              125.95345 1975   5   1       160.1 1975-05-01
## 6              109.70603 1975   6   1        50.1 1975-06-01
## 7               93.75725 1975   7   1       134.7 1975-07-01
## 9              108.77626 1975   9   1        44.5 1975-09-01
## 16             120.73962 1976   4   1        92.8 1976-04-01
## 19              35.68571 1976   7   1         1.4 1976-07-01
## 28              82.09567 1977   4   1        87.2 1977-04-01
## 31              53.28928 1977   7   1         9.5 1977-07-01
## 33              87.03775 1977   9   1       119.6 1977-09-01
## 35              97.15377 1977  11   1        19.4 1977-11-01
## 37              99.45419 1978   1   1        58.8 1978-01-01
## 43              27.87031 1978   7   1        72.6 1978-07-01
## 47              48.54095 1978  11   1        75.5 1978-11-01
## 48              50.58497 1978  12   1        91.1 1978-12-01
## 51              71.29668 1979   3   1       119.0 1979-03-01
## 56              66.50665 1979   8   1        76.0 1979-08-01
## 58              94.71189 1979  10   1        46.5 1979-10-01
## 63             155.47727 1980   3   1        70.8 1980-03-01
## 73             103.69579 1981   1   1         9.6 1981-01-01
## 76             114.69064 1981   4   1       200.8 1981-04-01
## 77             106.66433 1981   5   1        57.6 1981-05-01
## 78              53.12203 1981   6   1        25.6 1981-06-01
## 81              77.68581 1981   9   1        38.6 1981-09-01
## 85              92.44637 1982   1   1       140.9 1982-01-01
## 87             102.23483 1982   3   1       103.6 1982-03-01
## 92              47.93057 1982   8   1         2.7 1982-08-01
## 93             157.62051 1982   9   1        66.6 1982-09-01
## 97             171.94073 1983   1   1        95.7 1983-01-01
## 105             78.86136 1983   9   1        11.1 1983-09-01
## 109            179.81056 1984   1   1        72.5 1984-01-01
## 112             90.84725 1984   4   1       217.0 1984-04-01
## 115             60.04512 1984   7   1        10.2 1984-07-01
## 119             70.38415 1984  11   1        95.8 1984-11-01
## 121             50.14988 1985   1   1        57.9 1985-01-01
## 125             80.51916 1985   5   1       107.2 1985-05-01
## 127             58.93307 1985   7   1        30.1 1985-07-01
## 129             73.43896 1985   9   1       138.9 1985-09-01
## 130             75.52750 1985  10   1        62.2 1985-10-01
## 131            117.73697 1985  11   1       113.6 1985-11-01
## 135            100.06586 1986   3   1       153.3 1986-03-01
## 136             95.24826 1986   4   1       154.8 1986-04-01
## 138             74.16615 1986   6   1        14.0 1986-06-01
## 139             47.67612 1986   7   1       239.2 1986-07-01
## 142             52.28243 1986  10   1        86.8 1986-10-01
## 145             74.90899 1987   1   1        70.0 1987-01-01
## 147             77.57010 1987   3   1       116.2 1987-03-01
## 148             92.53818 1987   4   1       136.3 1987-04-01
## 155             41.60427 1987  11   1         8.0 1987-11-01
## 157             43.85722 1988   1   1        75.1 1988-01-01
## 160             85.88414 1988   4   1       297.8 1988-04-01
## 163             92.67992 1988   7   1        44.8 1988-07-01
## 170            118.38577 1989   2   1       113.0 1989-02-01
## 173            108.48726 1989   5   1        65.4 1989-05-01
## 175             73.45616 1989   7   1        20.1 1989-07-01
## 177            124.90910 1989   9   1       108.8 1989-09-01
## 181             70.64632 1990   1   1        46.6 1990-01-01
## 188             40.84400 1990   8   1        37.1 1990-08-01
## 189            140.12862 1990   9   1        27.3 1990-09-01
## 191            158.56849 1990  11   1        39.9 1990-11-01

APLICACIÓN Y REPOSITORIO DE DATOS

Aplicación

Aqui podemos digirirnos a nuestras aplicaciones desde cualquier ordenador.En este caso realizamos dos apps:

Temperatura APP

Precipitación app

Repositorio de Datos

Este es nuestro Repositorio, aqui pueden observar todos los datos que se utilizaron para el desarrollo de nuestro proyecto. Además compartimos el codigo fuente de la base de nuestra página subida a RPubs.

LS0tDQp0aXRsZTogIlVOSVZFUlNJREFEIENFTlRSQUwgREVMIEVDVUFET1IgQ2FycmVyYSBkZSBJbmdlbmllcsOtYSBBbWJpZW50YWwiIA0Kc3VidGl0bGU6ICJBbsOhbGlzaXMgZGUgbGEgVmFyaWFiaWxpZGFkIENsaW3DoXRpY2EgZW4gbGEgQ3VlbmNhIGhpZHJvZ3LDoWZpY2EgZGVsIFLDrW8gUnVtaXBhbWJhIC0gTW9kZWxvIGRlIFByZWRpY2Npw7NuIEVzdGFkw61zdGljbyBwYXJhIFRlbXBlcmF0dXJhIHkgUHJlY2lwaXRhY2nDs24gY29uIGVsIGFsZ29yaXRtbyBSYW5kb20gRm9yZXN0IGVuIGxlbmd1YWplIFIiDQphdXRob3I6ICJOYWltIEFndWlsYXIgLSBKaG9uYXRhbiBDcnV6IC0gSmF2aWVyIFF1aXNocGUgLSAgU3VzYW5hIEFyY2luaWVnYXMgT3J0ZWdhIg0KZGF0ZTogIjIwMjQvMDIvMDIiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIHRoZW1lOiB1bml0ZWQNCi0tLQ0KIVtdKFVDRS5qcGcpe3dpdGhkPTYwfQ0KIVtdKEZJR0VNUEEucG5nKXt3aXRoZD03MCV9DQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCg0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY2FyZXQpDQpsaWJyYXJ5KHJhbmRvbUZvcmVzdCkNCmxpYnJhcnkoem9vKQ0KDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KIyBBTsOBTElTSVMgVkFSSUFCSUxJREFEIENMSU3DgVRJQ0ENCg0KIyMgSW50cm9kdWNjacOzbg0KTGEgdmFyaWFiaWxpZGFkIGNsaW3DoXRpY2EgZW4gdW5hIGN1ZW5jYSBoaWRyb2dyw6FmaWNhIGVzIHVuIHRlbWEgZGUgZ3JhbiBpbXBvcnRhbmNpYSBkZWJpZG8gYSBzdSBpbXBhY3RvIGVuIGxhIGdlc3Rpw7NuIGRlbCBhZ3VhLCBsYSBhZ3JpY3VsdHVyYSwgbGEgYmlvZGl2ZXJzaWRhZCB5IGxhIHNlZ3VyaWRhZCBhbGltZW50YXJpYSBkZSBsYXMgcG9ibGFjaW9uZXMgcXVlIGRlcGVuZGVuIGRlIGxvcyByZWN1cnNvcyBow61kcmljb3MgZGUgZXNhIHJlZ2nDs24uIExhcyBjdWVuY2FzIGhpZHJvZ3LDoWZpY2FzIHNvbiDDoXJlYXMgZ2VvZ3LDoWZpY2FzIG5hdHVyYWxlcyBkb25kZSB0b2RhcyBsYXMgYWd1YXMgc3VwZXJmaWNpYWxlcyB5IHN1YnRlcnLDoW5lYXMgZmx1eWVuIGhhY2lhIHVuIG1pc21vIHB1bnRvIGRlIHNhbGlkYSwgeWEgc2VhIHVuIHLDrW8gcHJpbmNpcGFsLCB1biBsYWdvIG8gdW4gb2PDqWFuby4gRXN0YSBjYXJhY3RlcsOtc3RpY2EgaGFjZSBxdWUgc2VhbiBzaXN0ZW1hcyBzZW5zaWJsZXMgYSBsb3MgY2FtYmlvcyBjbGltw6F0aWNvcy4gDQoNCkxhIHZhcmlhYmlsaWRhZCBjbGltw6F0aWNhIHNlIHJlZmllcmUgYSBsYXMgZmx1Y3R1YWNpb25lcyBuYXR1cmFsZXMgZW4gbG9zIHBhdHJvbmVzIGRlbCBjbGltYSBhIGxvIGxhcmdvIGRlbCB0aWVtcG8geSBlbCBlc3BhY2lvLiBFc3RhcyBmbHVjdHVhY2lvbmVzIHB1ZWRlbiBtYW5pZmVzdGFyc2UgZW4gY2FtYmlvcyBlbiBsYSB0ZW1wZXJhdHVyYSwgcHJlY2lwaXRhY2nDs24sIGh1bWVkYWQsIHZpZW50b3MgeSBvdHJvcyBwYXLDoW1ldHJvcyBjbGltw6F0aWNvcy4gRW4gdW5hIGN1ZW5jYSBoaWRyb2dyw6FmaWNhLCBsYSB2YXJpYWJpbGlkYWQgY2xpbcOhdGljYSBwdWVkZSBpbmZsdWlyIGVuIGxhIGNhbnRpZGFkLCBkaXN0cmlidWNpw7NuIHkgdGVtcG9yYWxpZGFkIGRlIGxhcyBwcmVjaXBpdGFjaW9uZXMsIGFzw60gY29tbyBlbiBsYSBldmFwb3JhY2nDs24geSBsYSBkaXNwb25pYmlsaWRhZCBkZSBhZ3VhIHN1cGVyZmljaWFsIHkgc3VidGVycsOhbmVhLiANCg0KTG9zIGNhbWJpb3MgZW4gZWwgY2xpbWEgcHVlZGVuIHNlciBjYXVzYWRvcyBwb3IgZmFjdG9yZXMgbmF0dXJhbGVzLCBjb21vIHZhcmlhY2lvbmVzIGVuIGxhIGFjdGl2aWRhZCBzb2xhciwgb3NjaWxhY2lvbmVzIGNsaW3DoXRpY2FzIG5hdHVyYWxlcyBjb21vIEVsIE5pw7FvIHkgTGEgTmnDsWEsIHkgZmVuw7NtZW5vcyBhdG1vc2bDqXJpY29zIGNvbW8gZWwgY2FtYmlvIGVuIGxhIGNpcmN1bGFjacOzbiBhdG1vc2bDqXJpY2EuIFNpbiBlbWJhcmdvLCBlbiBsYXMgw7psdGltYXMgZMOpY2FkYXMsIGxhIGFjdGl2aWRhZCBodW1hbmEsIGVzcGVjaWFsbWVudGUgbGEgZW1pc2nDs24gZGUgZ2FzZXMgZGUgZWZlY3RvIGludmVybmFkZXJvLCBoYSBjb250cmlidWlkbyBzaWduaWZpY2F0aXZhbWVudGUgYWwgY2FsZW50YW1pZW50byBnbG9iYWwgeSBhbCBjYW1iaW8gY2xpbcOhdGljbywgZXhhY2VyYmFuZG8gbGEgdmFyaWFiaWxpZGFkIGNsaW3DoXRpY2EgeSBnZW5lcmFuZG8gaW1wYWN0b3MgbcOhcyBwcm9udW5jaWFkb3MgZW4gbGFzIGN1ZW5jYXMgaGlkcm9ncsOhZmljYXMuDQoNCiMjIEluZm9ybWUgRGV0YWxsYWRvDQoNCkRhciBjbGljayBlbiBsYSBwYWxhYnJhIFtJTkZPUk1FXShodHRwczovL3VjZWVkdS1teS5zaGFyZXBvaW50LmNvbS86YjovZy9wZXJzb25hbC9uYWFndWlsYXJhX3VjZV9lZHVfZWMvRVV0cTVvek44dk5PcTNSYWJXWjBjOEVCWU9fbnVTR0RjQndNb2JoTmZNSkE0QT9lPTBGTXd2VykgcGFyYSBwb2RlciBvYnRlbmVyIG1hcyBpbmZvcm1hY2nDs24gZGVsIGluZm9ybWUuDQoNCiMjIEFuw6FsaXNpcyBkZSBSZXN1bHRhZG9zDQoNCkEgcGFydGlyIGRlbCBhw7FvIDE5ODAgaGFzdGEgZWwgMjAxNCBzZSBoYW4gcHJvZHVjaWRvIDEwIGV2ZW50b3MgY8OhbGlkb3MgZGVsIE5pw7FvLCBlbXBlemFuZG8gZW4gYWJyaWwvODIgaGFzdGEganVuaW8vODMsIHNlcHRpZW1icmUvODYgaGFzdGEgZmVicmVyby84OCwganVuaW8vOTEgaGFzdGEganVsaW8vOTIsIG9jdHVicmUvOTQgaGFzdGEgbWFyem8vOTUsIG1heW8vOTcgaGFzdGEgbWF5by85OCwganVuaW8vMDIgaGFzdGEgZmVicmVyby8wMywganVsaW8vMDQgaGFzdGEgYWJyaWwvMDUsIHNlcHRpZW1icmUvMDYgaGFzdGEgZW5lcm8vMDcsIGp1bCBhYnJpbC8yMDEwLCBub3ZpZW1icmUvMTQgaGFzdGEgZGljaWVtYnJlLzE0LCB5IHNpZXRlIGV2ZW50b3MgZnLDrW9zIGRlIExhIE5pw7FhIHF1ZSBvY3Vycmllcm9uIGVuIG9jdHVicmUvODQgaGFzdGEganVuaW8vODUsIG1heW8vODggaGFzdGEgbWF5by84OSwgYWdvc3RvLzk1IGhhc3RhIG1hcnpvLzk2LCBqdWxpby85OCBoYXN0YSBmZWJyZXJvLzAxLCBhZ29zdG8vMDcgaGFzdGEganVuaW8vMDgsIGp1bGlvLzEwIGhhc3RhIGFicmlsLzExLCBhZ29zdG8vMTEgaGFzdGEgZmVicmVyby8xMi4gDQoNCiFbRmlndXJhIE7CsDE6IFByZWNpcGl0YWNpw7NuIFBlcmlvZG8gMTk3OS0yMDE5IEVzdGFjacOzbiBQYXJxdWUgQmljZW50ZW5hcmlvIF0oUHJlY2lwaXRhY2lvbl8xOTc5XzIwMTkucG5nKXt3aXRoZD0xMDAlfQ0KDQohW0ZpZ3VyYSBOwrAyOiBUZW1wZXJhdHVyYSBQZXJpb2RvIDE5NzktMjAxOSBFc3RhY2nDs24gUGFycXVlIEJpY2VudGVuYXJpbyBdKFRlbXBlcmF0dXJhXzE5NzktMjAxOS5wbmcpe3dpdGhkPTEwMCV9DQoNCg0KIVtGaWd1cmEgTsKwMzogw41uZGljZSBFc3RhbmRhcml6YWRvIGRlIFNlcXXDrWEgMTk3OS0yMDE5IEVzdGFjacOzbiBQYXJxdWUgQmljZW50ZW5hcmlvIF0oSUVTIEJpY2VudGVuYXJpby5qcGcpe3dpdGhkPTE1MCV9DQoNCiFbRmlndXJhIE7CsDQ6IMONbmRpY2UgRXN0YW5kYXJpemFkbyBkZSBTZXF1w61hIDE5NzktMjAxOSBFc3RhY2nDs24gQ2FuYWwgMTBUViBdKElFUyBDYW5hbCAxMC5qcGcpe3dpdGhkPTE1MCV9DQoNCiMgTU9ERUxPIEVTVEFEw41TVElDTyAiUkFORE9NIEZPUkVTVCIgRU4gUlNUVURJTw0KDQojIyBURU1QRVJBVFVSQQ0KDQojIyMgMS4gSW5zdGFsYWNpw7NuIHkgY2FyZ2EgZGUgbGlicmVyw61hcy4NCg0KRWwgcHJpbWVyIHBhc28gZXMgbGEgaW5zdGFsYWNpw7NuIGRlIGxhcyBsaWJyZXLDrWFzIHV0aWxpemFuZG8gZWwgY29tYW5kbyBpbnN0YWxsLnBhY2thZ2VzKCkgZXN0byBlcyBmdW5kYW1lbnRhbCBwYXJhIGVsIGRlc2Fycm9sbG8gZGUgbnVlc3RybyBjw7NkaWdvLiBZYSBxdWUgc2UgbmVjZXNpdGFuIGFsZ3Vub3MgcGFxdWV0ZXMgY29tbyBjYXJldCwgcmFuZG9tZm9yZXN0LCBsdWJyaWRhdGUsIGdncGxvdDIuIFNpbiBlc3RhcyBsaWJyZXLDrWFzIGVsIGNvZGlnbyBubyB2YSBhIGNvcnJlci4gDQoNCkEgY29udGludWFjacOzbiBsYXMgbGlicmVyw61hcyBxdWUgdmFtb3MgYSB1dGlsaXphciBlbiBlbCBjw7NkaWdvLg0KDQppbnN0YWxsLnBhY2thZ2VzKCJsdWJyaWRhdGUiKQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJkcGxSIikNCg0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCg0KaW5zdGFsbC5wYWNrYWdlcygiY2FyZXQiKQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21mb3Jlc3QiKQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJ6b28iKQ0KDQpBaG9yYSB0ZW5lbW9zIHF1ZSBsbGFtYXIgYSBsb3MgcGFxdWV0ZXMgaW5zdGFsYWRvcyBjb24gZWwgY29tYW5kbyBsaWJyYXJ5KCksIHkgZGVudHJvIGRlIGxvcyBwYXJlbnRlc2lzIGVzY3JpYmlyIGxvcyBwYXF1ZXRlcyBxdWUgc29saWNpdGFtb3MgYW50ZXJpb3JtZW50ZS4NCg0KbGlicmFyeShsdWJyaWRhdGUpDQoNCmxpYnJhcnkoZHBseXIpDQoNCmxpYnJhcnkodGlkeXIpDQoNCmxpYnJhcnkoZ2dwbG90MikNCg0KbGlicmFyeShjYXJldCkNCg0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQoNCmxpYnJhcnkoem9vKQ0KDQoNCiMjIyAyLiBBbmFsaXNpcyBkZSBsb3MgRGF0b3MuDQoNCkxvcyBkYXRvcyBvYnRlbmlkb3Mgc2UgZGVzY2FyZ2Fyb24gZGUgbGEgcMOhZ2luYSB3ZWIgW0RhdG9zIEFiaWVydG9zXShodHRwczovL3d3dy5kYXRvc2FiaWVydG9zLmdvYi5lYy9kYXRhc2V0LyksIGVzdGEgZXMgdW5hIHDDoWdpbmEgYW5leGFkYSBhbCBJTkFITUkuDQoNClZhbW9zIGEgcmVhbGl6YXIgdW4gYW7DoWxpc2lzIHByZXZpbyBkZSBudWVzdHJvcyBkYXRvcyBwYXJhIHZpc3VhbGl6YXIgZWwgY29tcG9ydGFtaWVudG8gZW4gc2VyaWUgZGUgdGllbXBvLiANCg0KIyMjIyAyLjEuIENhcmdhIGRlIGxvcyBEYXRvcyBkZSBUZW1wZXJhdHVyYSB5IFByZWNpcGl0YWNpw7NuLg0KDQpBaG9yYSB0ZW5lbW9zIHF1ZSBoYWNlciBsYSBjYXJnYSBkZSBudWVzdHJvcyBkYXRvcyBsb3MgY3VhbGVzIHNlIGVuY3VlbnRyYW4gZW4gZm9ybWF0byBjc3YgKHRleHRvIGRlbGltaXRhZG8gcG9yIGNvbWFzKSwgZXMgcmVjb21lbmRhZG8gdGVuZXIgZW4gZXN0ZSBmb3JtYXRvIHlhIHF1ZSBSIGxvIHJlY29ub2NlIHNpbiBuZWNlc2lkYWQgZGUgYWdyZWdhciB1bmEgbGlicmVyaWEgYWRpY2lvbmFsLg0KDQpgYGB7cn0NCnNldHdkKCJ+L0lORyBBTUJJRU5UQUwvSU5GT1JNQVRJQ0EyMy0yNC9Qcm95ZWN0byIpDQp0ZW1wIDwtIHJlYWQuY3N2KCJUZW1wX0ZpbmFsLmNzdiIsIHNlcCA9ICIsIiwgZGVjID0gIi4iLCBoZWFkZXIgPSBUKQ0KYGBgDQoNCiMjIyMgMi4yLiBDcmVhY2nDs24gZGUgU2VyaWUgZGUgVGllbXBvIHBhcmEgYW7DoWxpc2lzIGRlIGRhdG9zLg0KDQpVbmEgc2VyaWUgZGUgdGllbXBvIGVzIHVuYSBzZWN1ZW5jaWEgZGUgZGF0b3MgdSBvYnNlcnZhY2lvbmVzIG1lZGlkb3MgZW4gZGV0ZXJtaW5hZG9zIG1vbWVudG9zLCBlbiBpbnRlcnZhbG9zIGlndWFsZXMgeSBvcmRlbmFkb3MgY3Jvbm9sw7NnaWNhbWVudGUuDQoNCkVsIGFuw6FsaXNpcyBkZSBzZXJpZXMgZGUgdGllbXBvIHNlIHJlZmllcmUgYWwgcHJvY2VzbyBkZSBhbmFsaXphciBsb3MgZGF0b3MgZGlzcG9uaWJsZXMgcGFyYSBkZXNjdWJyaXIgZWwgcGF0csOzbiBvIGxhIHRlbmRlbmNpYSBlbiBsb3MgZGF0b3MuIFBlcm1pdGUgZXh0cmFlciB5IG1vZGVsYXIgbGFzIHJlbGFjaW9uZXMgZW50cmUgZGF0b3MgYSBsbyBsYXJnbyBkZWwgdGllbXBvLg0KDQpgYGB7cn0NCnRlbXBfdHMgPC0gdHModGVtcCRUZW1wZXJhdHVyYSwgZnJlcXVlbmN5ID0gMTIsIHN0YXJ0ID0gYygxOTc1LDEpKQ0KcGxvdCh0ZW1wX3RzLCBtYWluID0gIlNlcmllIGRlIFRpZW1wbyBkZSBUZW1wZXJhdHVyYSAxOTc1LTIwMTkiLCB4bGFiID0gIkHDsW9zIiwgeWxhYiA9ICJUZW1wZXJhdHVyYSAowrBDKSIpDQpgYGANCg0KIyMjIyAyLjMuIENvbXBvbmVudGVzIGRlIGxhIFNlcmllIGRlIFRpZW1wby4NCg0KVXRpbGl6YXJlbW9zIGVsIGNvbWFuZG8gZGVjb21wb3NlKCkgZWwgY3VhbCBkZXNhZ3JlZ2EgbGEgc2VyaWUgZGUgdGllbXBvIGVuIGxvcyBjb21wb25lbnRlczogdGVuZGVuY2lhLCBlc3RhY2lvbmFsLCBjw61jbGljbyB5IGFsZWF0b3Jpby4NCg0KYGBge3J9DQp0ZW1wX3RzX2Rlc2NvbXAgPC0gZGVjb21wb3NlKHRlbXBfdHMpIA0KYGBgDQoNCiMjIyMjIDIuMy4xLiBUZW5kZW5jaWENCg0KRXMgZWwgcGF0csOzbiBzdWJ5YWNlbnRlIGVuIGxvcyBkYXRvcyBhIGxvIGxhcmdvIGRlbCB0aWVtcG8uIE5vIGVzIG5lY2VzYXJpYW1lbnRlIGxpbmVhbC4NCg0KYGBge3J9DQpwbG90KHRlbXBfdHNfZGVzY29tcCR0cmVuZCwgbWFpbiA9ICJUZW5kZW5jaWEgZGUgVGVtcGVyYXR1cmEiLCBjb2wgPSAiYmx1ZSIsIHlsYWIgPSAiVGVtcGVyYXR1cmEgKMKwQykiKQ0KDQoNCmBgYA0KDQojIyMjIyAyLjMuMi4gRXN0YWNpb25hbGlkYWQNCg0KQ3VhbmRvIHVuYSBzZXJpZSBlc3RhIGluZmx1ZW5jaWFkYSBwb3IgZmFjdG9yZXMgZXN0YWNpb25hbGVzIGRlIHBlcmlvZG8gZmlqbyBjb21vIGVsIGTDrWEsIG1lcywgdHJpbWVzdHJlLCBhw7FvLCBldGMuDQoNCmBgYHtyfQ0KcGxvdCh0ZW1wX3RzX2Rlc2NvbXAkc2Vhc29uYWwsIG1haW4gPSAiRXN0YWNpb25hbGlkYWQgZGUgVGVtcGVyYXR1cmEiLCBjb2wgPSAicmVkIiwgeWxhYiA9ICJUZW1wZXJhdHVyYSAowrBDKSIpDQpgYGANCg0KDQojIyMjIyAyLjMuMy4gQWxlYXRvcmllZGFkDQoNClBhcnRlIGluZXhwbGljYWJsZSBkZSBsb3MgZGF0b3MgbWVkaWRvcy4NCg0KYGBge3J9DQpwbG90KHRlbXBfdHNfZGVzY29tcCRyYW5kb20sIG1haW4gPSAiSXJyZWd1bGFyaWRhZCBkZSBUZW1wZXJhdHVyYSIsIGNvbCA9ICJncmVlbiIsIHlsYWIgPSAiVGVtcGVyYXR1cmEgKMKwQykiKQ0KYGBgDQoNCg0KIyMjIDMuIENyZWFjacOzbiBkZSB2YXJpYWJsZXMgcHJldmlvIGFsIG1vZGVsYW1pZW50by4NCg0KRW4gZXN0ZSBhcGFydGFkbyBkZWwgY8OzZGlnbyBkZSBSLCBjcmVhbW9zIGxhcyB2YXJpYWJsZXMgZGUgbGFzIGZlY2hhcyBlbiBsYXMgcXVlIHNlIHRvbWFyb24gbG9zIGRhdG9zIHNlZ8O6biBlbCBJTkFITUkgY29tbyBwb2RlbW9zIG9iZXJzZXJ2YXIgZW4gbnVlc3RybyBjw7NkaWdvIGxhIHByaW1lcmEgZmVjaGEgcXVlIHNlIHRvbWFyb24gZGF0b3MgZGUgdGVtcGVyYXR1cmEgZXN0w6FuIGVuICJkYXRlX2luaWNpYWwiIHkgbGEgw7psdGltYSBmZWNoYSBxdWUgc2UgcmVwb3J0YXJvbiBkYXRvcyBlcyAiZGF0ZV9maW5hbCIuDQoNCkFkZW1hcyBwb2RlbW9zIG5vdGFyIHF1ZSB0ZW5lbW9zIGRvcyB2YXJpYWJsZXMgYWRpY2lvbmFsZXMgImZlY2hhX2ZpbmFsIiBsYSBjdWFsIG5vcyBpbmRpY2EgZWwgbWVzIHNpZ3VpZW50ZSBhIGxhIMO6bHRpbWEgdG9tYSBkZSBkYXRvcyByZXBvcnRhZG9zIHBvciBlbCBJTkFITUkgeSAiZmVjaGFfcHJlZCIgZXMgbGEgZmVjaGEgZGUgcHJlZGljY2nDs24gcXVlIGZpamFtb3MgcGFyYSB1biBhw7FvLg0KDQpgYGB7cn0NCg0KZmVjaGFfZmluYWwgPC0gYXMuRGF0ZSgiMjAxOS0wNy0wMSIpDQoNCmZlY2hhX3ByZWQgPC0gYXMuRGF0ZSgiMjAyMC0wNy0wMSIpDQoNCmRhdGVfaW5pY2lhbCA8LSBhcy5EYXRlKCIxOTc1LTAxLTAxIikNCg0KZGF0ZV9maW5hbCA8LSBhcy5EYXRlKCIyMDE5LTA2LTAxIikNCg0KRmVjaGEgPC0gYXMuRGF0ZShzZXEoZnJvbSA9IGRhdGVfaW5pY2lhbCwgdG8gPSBkYXRlX2ZpbmFsLCBieT0ibW9udGgiKSkNCg0KYGBgDQoNCg0KIyMjIDQuIFByZXBhcmFjacOzbiBkZSBEYXRvcyBwYXJhIGVsIG1vZGVsYW1pZW50by4NCg0KIyMjIyA0LjEuIENvbnZlcnNpw7NuIGRlIERhdG9zIGEgdW4gZGF0YSBmcmFtZS4gDQoNCkxvcyBkYXRhZnJhbWVzIHNvbiB1bmEgY2xhc2UgZGUgb2JqZXRvcyBlc3BlY2lhbCBlbiBSLiBOb3JtYWxtZW50ZSwgY3VhbmRvIHNlIHJlYWxpemEgdW4gZXN0dWRpbyBlc3RhZMOtc3RpY28gc29icmUgbG9zIHN1amV0b3MgdSBvYmpldG9zIGRlIHVuYSBtdWVzdHJhLCBsYSBpbmZvcm1hY2nDs24gc2Ugb3JnYW5pemEgcHJlY2lzYW1lbnRlIGVuIHVuIGRhdGFmcmFtZTogdW5hIGhvamEgZGUgZGF0b3MsIGVuIGxvcyBxdWUgY2FkYSBmaWxhIGNvcnJlc3BvbmRlIGEgdW4gc3VqZXRvIHkgY2FkYSBjb2x1bW5hIGEgdW5hIHZhcmlhYmxlLiBMYSBlc3RydWN0dXJhIGRlIHVuIGRhdGEuZnJhbWUgZXMgbXV5IHNpbWlsYXIgYSBsYSBkZSB1bmEgbWF0cml6LiBMYSBkaWZlcmVuY2lhIGVzIHF1ZSB1bmEgbWF0cml6IHPDs2xvIGFkbWl0ZSB2YWxvcmVzIG51bcOpcmljb3MsIG1pZW50cmFzIHF1ZSBlbiB1biBkYXRhZnJhbWUgcG9kZW1vcyBpbmNsdWlyIHRhbWJpw6luIGRhdG9zIGFsZmFudW3DqXJpY29zLg0KDQpgYGB7cn0NCnRlbXAgPC0gYXMuZGF0YS5mcmFtZSh0ZW1wKQ0KaGVhZCh0ZW1wLCBuID0gNjApDQpgYGANCg0KUG9zdGVyaW9yIGEgZXNvIGVtcGxlYW1vcyBlbCBjb21hbmRvIGNiaW5kKCkgcGFyYSBjb21iaW5hciBsYXMgZG9zIGNvbHVtbmFzIGVuIHVuIG51ZXZvIG1hcmNvIGRlIGRhdG9zLiBDb21iaW5hbW9zIGxhIGNvbHVtbmEgZGUgdGVtcGVyYXR1cmFzIGNvbiB1bmEgc2VjdWVuY2lhIGRlIGZlY2hhcyBwYXJhIGZvcm1hciB1biBudWV2byBtYXJjbyBkZSBkYXRvcy4gIA0KDQpgYGB7cn0NCnRlbXAgPC0gYXMuZGF0YS5mcmFtZShjYmluZCh0ZW1wJFRlbXBlcmF0dXJhLCBGZWNoYSkpDQpgYGANCg0KUmVub21icmFtb3MgbGFzIGNvbHVtbmFzIGRlbCBtYXJjbyBkZSBkYXRvcyBhIOKAmFRlbXBlcmF0dXJh4oCZIHkg4oCYRmVjaGHigJkuIA0KDQpgYGB7cn0NCm5hbWVzKHRlbXApIDwtIGMoIlRlbXBlcmF0dXJhIiwgIkZlY2hhIikNCmBgYA0KDQpEZXNwdcOpcyBjb252ZXJ0aW1vcyBsYSBjb2x1bW5hIGRlIOKAmEZlY2hh4oCZIGEgb2JqZXRvcyBkZSBmZWNoYSBlbiBSIGVtcGxlYW5kbyBlbCBjb21hbmRvIGFzLkRhdGUoKS4gRXN0byBub3MgYXNlZ3VyYSBxdWUgbG9zIGRhdG9zIGRlIGVzdGEgY29sdW1uYSBzZWFuIHRyYXRhZG9zIGNvbW8gZmVjaGFzLiAgDQoNCmBgYHtyfQ0KdGVtcCRGZWNoYSA8LSBhcy5EYXRlKHRlbXAkRmVjaGEpDQpgYGANCg0KRW4gcmVzdW1lbiwgaGFzdGEgZXN0YSBwYXJ0ZSwgZWwgY8OzZGlnbyB0b21hIHVuIG9iamV0byBkZSBkYXRvcyBsbGFtYWRvIOKAmHRlbXDigJksIGxvIGNvbnZpZXJ0ZSBlbiB1biBtYXJjbyBkZSBkYXRvcyBxdWUgc29saWNpdGEgUiwgY29tYmluYSBsYSBjb2x1bW5hIGRlIHRlbXBlcmF0dXJhIGNvbiB1bmEgc2VjdWVuY2lhIGRlIGZlY2hhcywgcmVub21icmFtb3MgbGFzIGNvbHVtbmFzIHkgYXNlZ3VyYW1vcyBxdWUgbGEgY29sdW1uYSBkZSBmZWNoYXMgZXN0ZSBlbiBlbCBmb3JtYXRvIGFkZWN1YWRvcyBwYXJhIHN1IG1hbmlwdWxhY2nDs24gY29tbyBmZWNoYXMuICAgDQoNCiMjIyMgNC4yLiBDcmVhY2nDs24gZGVsIERhdGEgRnJhbWUgcGFyYSBsYSBtdWVzdHJhIGRlIGxvcyByZXN1bHRhZG9zIHNlZ8O6biBlbCBtb2RlbG8uDQoNCkNyZWFtb3MgdW4gbnVldm8gZGF0YSBmcmFtZSBwYXJhIGxvcyBkYXRvcyBxdWUgdmFtb3MgYSBwcmVkZWNpci4gQ29uIGVzdGUgcGFzbyBzZSBjcmVhIHVuYSBzZWN1ZW5jaWEgZGUgZmVjaGFzIHByZWRpY2hhcyBkZXNkZSBsYSDigJhmZWNoYV9maW5hbOKAmSBoYXN0YSDigJhmZWNoYV9wcmVk4oCZIGVuIGludGVydmFsb3MgbWVuc3VhbGVzLiAgDQoNCmBgYHtyfQ0KZmVjaGFfcHJlZGljIDwtIGFzLkRhdGUoc2VxKGZyb20gPSBmZWNoYV9maW5hbCwgdG8gPSAoZmVjaGFfcHJlZCksIGJ5ID0gIm1vbnRoIikpDQpgYGANCg0KSW5pY2lhbGl6YW1vcyB1biB2ZWN0b3Ig4oCYVGVtcGVyYXR1cmHigJkgY29uIGxvcyB2YWxvcmVzIOKAmE5B4oCZIHkgY29tYmluYW1vcyBlc3RlIHZlY3RvciBjb24gZWwgdmVjdG9yIGRlIGZlY2hhcyBwcmVkaWNoYXMgcGFyYSBjcmVhciB1biBudWV2byBtYXJjbyBkZSBkYXRvcyBkZW5vbWluYWRvIOKAmGZlY2hhX3ByZWRpY+KAmS4gWSB0YW1iacOpbiBjb252ZXJ0aW1vcyBsYSBjb2x1bW5hIOKAmGZlY2hhX3ByZWRpY+KAmSBhIHVuIGZvcm1hdG8gZmVjaGEgeSByZW5vbWJyYW1vcyBsYXMgY29sdW1uYXMgYSDigJhUZW1wZXJhdHVyYeKAmSB5IOKAmEZlY2hh4oCZLiAgDQoNCmBgYHtyfQ0KVGVtcGVyYXR1cmEgPC0gYXMubnVtZXJpYyhOQSkNCmZlY2hhX3ByZWRpYyA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKFRlbXBlcmF0dXJhLCBmZWNoYV9wcmVkaWMpKQ0KZmVjaGFfcHJlZGljJGZlY2hhX3ByZWRpYyA8LSBhcy5EYXRlKGZlY2hhX3ByZWRpYyRmZWNoYV9wcmVkaWMpDQpuYW1lcyhmZWNoYV9wcmVkaWMpIDwtIGMoIlRlbXBlcmF0dXJhIiwgIkZlY2hhIikNCmBgYA0KDQpDb21iaW5hbW9zIGxvcyBkYXRvcyBvcmlnaW5hbGVzIOKAmHRlbXDigJkgY29uIGxhcyBmZWNoYXMgcHJlZGljaGFzIOKAmGZlY2hhX3ByZWRpY+KAmSBlbXBsZWFuZG8gZWwgY29tYW5kbyByYmluZCgpLCBlbCBjdWFsIGHDsWFkZSBsYXMgZmlsYXMgYWwgbWFyY28gZGUgZGF0b3Mg4oCYdGVtcOKAmS4NCmBgYHtyfQ0KdGVtcCA8LSByYmluZCh0ZW1wLCBmZWNoYV9wcmVkaWMpDQpgYGANCg0KRHVwbGljYW1vcyBsYSBjb2x1bW5hIGZlY2hhcyBwYXJhIG5vIHBlcmRlciBsb3MgZGF0b3MgZW4gZWwgcHJvY2VzbyBkZSBtb2RlbGFtaWVudG8geSB0YW1iacOpbiBzZXBhcmFtb3MgZXN0YSBjb2x1bW5hIGVuIOKAmEHDsW/igJksIOKAmE1lc+KAmSwg4oCYRGlh4oCZLiAgDQoNCmBgYHtyfQ0KdGVtcCRGZWNoYV9kdXAgPC0gdGVtcCRGZWNoYQ0KDQp0ZW1wIDwtIHRlbXAgJT4lIHNlcGFyYXRlKEZlY2hhLCBjKCJBw7FvIiwgIk1lcyIsICJEaWEiKSkNCmBgYA0KDQpFbiBlbCBzaWd1aWVudGUgY8OzZGlnbyBjb252ZXJ0aW1vcyBjYWRhIHVuYSBkZSBsYXMgY29sdW1uYXMg4oCYQcOxb+KAmSwg4oCYTWVz4oCZLCDigJhEaWHigJkgZW4gY2Fyw6FjdGVyIG51bcOpcmljby4gDQoNCmBgYHtyfQ0KdGVtcCRBw7FvIDwtIGFzLm51bWVyaWModGVtcCRBw7FvKQ0KdGVtcCRNZXMgPC0gYXMubnVtZXJpYyh0ZW1wJE1lcykNCnRlbXAkRGlhIDwtIGFzLm51bWVyaWModGVtcCREaWEpDQpgYGANCg0KIyMjIDUuIE1vZGVsYW1pZW50bw0KDQojIyMjIDUuMS4gQ3JlYWNpw7NuIGRlbCBjb25qdW50byBkZSBkYXRvcyBkZSBlbnRyZW5hbWllbnRvIHkgdGVzdGVvLg0KDQpFc3RhYmxlY2Vtb3MgbGEgc2VtaWxsYSAo4oCYc2VlZOKAmSkgcGFyYSBxdWUgbG9zIHJlc3VsdGFkb3Mgc2VhbiByZXByb2R1Y2libGVzIGVuIGVsIGZ1dHVyby4gRXN0byBxdWllcmUgZGVjaXIgcXVlLCBzaSBzZSBlamVjdXRhIGVsIGPDs2RpZ28gdmFyaWFzIHZlY2VzLCBzZSBvYnRlbmRyw6EgbG9zIG1pc21vcyByZXN1bHRhZG9zIGVuIGNhZGEgZWplY3VjacOzbiwgc2llbXByZSB5IGN1YW5kbyBzZSB1c2UgbGEgbWlzbWEgc2VtaWxsYS4gIA0KDQpFbXBsZWFtb3MgbGEgZnVuY2nDs24g4oCYY3JlYXRlRGF0YVBhcnRpdGlvbuKAmSBkZWwgcGFxdWV0ZSDigJhjYXJldOKAmSBwYXJhIGNyZWFyIHVuIMOtbmRpY2UgcXVlIGluZGljYSBxdWUgb2JzZXJ2YWNpb25lcyBzZSBpbmNsdWlyw6EgZW4gZWwgY29uanVudG8gZGUgZW50cmVuYW1pZW50by4gU2UgZmlsdHJhbiBsYXMgb2JzZXJ2YWNpb25lcyB5IHNlIGVsaW1pbmEgbGFzIGZpbGFzIGNvbiBsb3MgdmFsb3JlcyBmYWx0YW50ZXMgZW4gbGEgY29sdW1uYSDigJhUZW1wZXJhdHVyYeKAmQ0KDQpgYGB7cn0NCnNldC5zZWVkKDE5OTYpDQp0cmFpbiA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKG5hLm9taXQoc3Vic2V0KHRlbXAsIHRlbXAkRmVjaGFfZHVwIDwgZmVjaGFfZmluYWwpKSRUZW1wZXJhdHVyYQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHAgPSAwLjcsIGxpc3QgPSBGKSANCg0KDQpgYGANCg0KRWwgdmFsb3Ig4oCYcD0wLjfigJkgbm9zIGluZGljYSBxdWUgc2UgdmFuIGEgZW1wbGVhciBlbCA3MCUgZGUgbG9zIGRhdG9zIGNvbW8gY29uanVudG8gZGUgZW50cmVuYW1pZW50by4gVGFtYmnDqW4gZXNwZWNpZmljYW1vcyBxdWUgcXVlcmVtb3MgdW4gdmVjdG9yIGRlIMOtbmRpY2UgZW4gbHVnYXIgZGUgdW5hIGxpc3RhIGNvbiBlbCBzdWJjb21hbmRvIOKAmGxpc3QgPSBGQUxTReKAmS4gIA0KDQpFbXBsZWFtb3MgZWwgw61uZGljZSDigJh0cmFpbuKAmSBwYXJhIHNlbGVjY2lvbmFyIGxhcyBvYnNlcnZhY2lvbmVzIGNvcnJlc3BvbmRpc3RlIGFsIGNvbmp1bnRvIGRlIHBydWViYSB1dGlsaXphZG8gbGEgbm90YWNpw7NuIGRlbCBzdWJjb25qdW50byAo4oCYdGVtcFstdHJhaW4sXeKAmSkuIENvbWJpbmFtb3MgZXN0YXMgb2JzZXJ2YWNpb25lcyBjb24gbGFzIHF1ZSB0aWVuZW4g4oCYRmVjaGFfZHVw4oCZIG1heW9yIG8gaWd1YWwgYSDigJhmZWNoYV9maW5hbOKAmSBlbXBsZWFuZG8g4oCYcmJpbmTigJkuDQoNCmBgYHtyfQ0KdGVzdCA8LSByYmluZCh0ZW1wWy10cmFpbixdICwgc3Vic2V0KHRlbXAsIHRlbXAkRmVjaGFfZHVwID49IGZlY2hhX2ZpbmFsKSkNCg0KYGBgDQoNCkVuIGNvbmNsdXNpw7NuLCBlbiBlc3RhIHBhcnRlIHNlIGNyZWEgdW4gY29uanVudG8gZGUgZW50cmVuYW1pZW50byB5IHRlc3QuIEVsIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG8gaW5jbHV5ZSBkYXRvcyBoYXN0YSBsYSBmZWNoYSBmaW5hbCwgbWllbnRyYXMgcXVlIGVsIGNvbmp1bnRvIGRlIHBydWViYSBpbmNsdXllIGRhdG9zIGRlc2RlIGxhIGZlY2hhIGZpbmFsIGhhY2lhIGFkZWxhbnRlLiBDYWJlIHJlY2FsY2FyIHF1ZSBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvIHNlIHNlbGVjY2lvbmEgZWwgNzAlIGRlIGxhcyBvYnNlcnZhY2lvbmVzLiAgDQoNCiMjIyMgNS4yLiBFamVjdWNpw7NuIGRlbCBhbGdvcml0bW8gUmFuZG9tIEZvcmVzdC4NCg0KRXN0ZSBhbGdvcml0bW8gY29uc3RydXllIG3Dumx0aXBsZXMgYXJib2xlcyBkZSBjbGFzaWZpY2FjacOzbiBhbGVhdG9yaW9zIGRlcGVuZGllbmRvIGRlIGxvcyBkYXRvcyBjb24gbG9zIHF1ZSBzZSBlc3TDoSB0cmFiYWphbmRvLiBFbiBsb3MgcGFzb3MgYW50ZXJpb3JlcyBzZSBwcmVwYXJhcm9uIHRvZG9zIGxvcyBjb25qdW50b3MgZGUgZGF0b3MgYWRlY3VhZGFtZW50ZSBwYXJhIHF1ZSBzZSBwdWVkYSByZWFsaXphciBsb3MgcHJvY2Vzb3MgY29ycmVzcG9uZGllbnRlcy4gQWRlbcOhcyBkZSBkZWZpbmnDsyBlbCBuw7ptZXJvIGRlIMOhcmJvbGVzIHF1ZSBzZSB2YW4gaGFuIGNvbnN0cnVpciBhIHRyYXbDqXMgZGVsIGFyZ3VtZW50byDigJxudHJlZeKAnSANCg0KYGBge3J9DQptb2RfcmYgPC0gcmFuZG9tRm9yZXN0KFRlbXBlcmF0dXJhIH4gRmVjaGFfZHVwLCBkYXRhID0gdGVtcFt0cmFpbixdLCANCiAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZWdyZXNzaW9uIiwgbnRyZWUgPSA1MDApDQpgYGANCg0KQ3JlYW1vcyBsYXMgZGlmZXJlbnRlcyB2YXJpYWJsZXMgZG9uZGUgc2UgdmFuIGEgYWxtYWNlbmFyIGxvcyByZXN1bHRhZG9zIGRlIGxvcyBwcm9jZXNvcyBkZXBlbmRpZW5kbyBkZSBsb3Mgw61uZGljZXMgcXVlIHNlIGVzdGFibGVjaWVyb24gY29tbyBwYXLDoW1ldHJvcyBkZWwgbW9kZWxhbWllbnRvLCBjb24gbG8gcXVlIHRlbmVtb3MgdW5hIHZhcmlhYmxlIGRlIHJlc3VsdGFkbyBwYXJhIGxhIHByZWRpY2Npw7NuIHkgb3RyYSBkb25kZSBzZSBhbG1hY2VuYXJhbiBsYSBjb21iaW5hY2nDs24gZGUgbG9zIGRhdG9zIHJlc3VsdGFudGVzIGRlIGxhIHByZWRpY2Npw7NuLiAgDQoNCmBgYHtyfQ0KcHJlZF9yZiA8LSBwcmVkaWN0KG1vZF9yZiwgdGVzdCkNCg0KZGF0b3NfcmYgPC0gY2JpbmQocHJlZF9yZiwgdGVzdCkNCg0KYGBgDQoNCiMjIyMgNS4zLiBFcnJvcmVzIHBhcmEgbGEgY29tcHJvYmFjacOzbiBkZWwgbW9kZWxvLg0KDQpMdWVnbyBkZSBlc3RvIHZhbW9zIGEgZGV0ZXJtaW5hciBlbCBwb3JjZW50YWplIGRlIGVycm9yIHF1ZSBzZSBvYnRpZW5lIGVuIGxhIG1vZGVsYWNpw7NuIHJlYWxpemFkYSBjb24gbG8gY3VhbCBwb2RlbW9zIGVzdGFibGVjZXIgc2kgZXN0w6EgZGVudHJvIGRlbCByYW5nbyBhY2VwdGFibGUuIEVzdG8gbm9zIGdhcmFudGl6YSBxdWUgbG9zIGRhdG9zIHF1ZSBzZSBoYW4gb2J0ZW5pZG8gc2VhbiB0cmF0YWRvcyBjb21vIGRhdG9zIGNvbmZpYWJsZXMgcGFyYSBkZXRlcm1pbmFyIHByZWRpY2Npb25lcyBmdXR1cmFzIGFwbGljYW5kbyBlc3RhIG1ldG9kb2xvZ8OtYSB5IGJhc2FkbyBlbiBlbCBhbsOhbGlzaXMgZGUgcXVlIHNlIGhhIHZlbmlkbyByZWFsaXphbmRvIGVuIGxhIHRvbWEgeSB0cmF0YW1pZW50byBkZSBkYXRvcy4gDQoNCmBgYHtyfQ0KZXJyb3JfYWJzX3JmIDwtUk1TRShkYXRvc19yZiRUZW1wZXJhdHVyYSwgZGF0b3NfcmYkcHJlZF9yZiwgbmEucm0gPSBUKQ0KDQplcnJvcl9wb3JfcmYgPC0gZXJyb3JfYWJzX3JmIC8gZGF0b3NfcmZbZGF0b3NfcmYkRmVjaGFfZHVwID09IG1heChuYS5vbWl0KGRhdG9zX3JmKSRGZWNoYV9kdXApLCBdJFRlbXBlcmF0dXJhDQplcnJvcl9wb3JfcmYqMTAwDQoNCk1BRShkYXRvc19yZiRUZW1wZXJhdHVyYSwgZGF0b3NfcmYkcHJlZF9yZiwgbmEucm0gPSBUKQ0KYGBgDQoNCiMjIyA2LiBHcmFmaWNvIGRlbCBtb2RlbG8gcmVzdWx0YW50ZS4NCg0KRmluYWxtZW50ZSBzZSB2YSBhIGRpYnVqYXIgZWwgbW9kZWxvIHF1ZSBzZSBvYnR1dm8gY29tbyByZXN1bHRhZG8gZGVsIHByb2Nlc28gcmVhbGl6YWRvLCBlbiBlbCBjdWFsIHNlIGluY2x1eWVuIGVsIGNvbXBvcnRhbWllbnRvIHJlYWwgbMOtbmVhcyBlbiBuZWdybyB5IGVsIG1vZGVsbyBjYWxjdWxhZG8gbMOtbmVhcyBlbiByb2pvLCBhc8OtIGNvbW8gZWwgdMOtdHVsbyBkZSBsYSBlc3RhY2nDs24gcXVlIGhlbW9zIHRvbWFkbyBjb21vIHJlZmVyZW5jaWEgcGFyYSBsYSBlamVjdWNpw7NuIGRlbCBtb2RlbGFtaWVudG8uIA0KDQpgYGB7cn0NCmdncGxvdCgpICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRvc19yZiwgYWVzKHggPSBGZWNoYV9kdXAsIHkgPSBUZW1wZXJhdHVyYSksIGNvbG9yID0gImJsYWNrIikgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGRhdG9zX3JmLCBhZXMoeCA9IEZlY2hhX2R1cCwgeSA9IHByZWRfcmYpLCBjb2xvciA9ICJyZWQiKSArIA0KICBnZ3RpdGxlKCJNb2RlbG8gZGUgUHJlZGljY2nDs24gcGFyYSBUZW1wZXJhdHVyYSBFc3RhY2lvbiBJw7FhcXVpdG8gIikNCmBgYA0KDQpBZGVtYXMgYWRqdW50YW1vcyBsYSB0YWJsYSBkZSBsb3MgcmVzdWx0YWRvcyBwYXJhIG51ZXN0cm8gbW9kZWxvIHkgbG9zIGRhdG9zIG9yaWdpbmFsZXMuDQoNCmBgYHtyfQ0KbmFtZXMoZGF0b3NfcmYpIDwtIGMoIlRlbXBlcmF0dXJhIE1vZGVsbyIsICJUZW1wZXJhdHVyYSIsICJBw7FvIiwgIk1lcyIsICJEw61hIiwgIkZlY2hhIikNCmhlYWQoZGF0b3NfcmYsIG4gPSA2MCkNCmBgYA0KDQoNCiMjIFBSRUNJUElUQUNJw5NODQoNCiMjIyAxLiBBbmFsaXNpcyBkZSBsb3MgRGF0b3MuDQoNCkxvcyBkYXRvcyBvYnRlbmlkb3Mgc2UgZGVzY2FyZ2Fyb24gZGUgbGEgcMOhZ2luYSB3ZWIgW0RhdG9zIEFiaWVydG9zXShodHRwczovL3d3dy5kYXRvc2FiaWVydG9zLmdvYi5lYy9kYXRhc2V0LyksIGVzdGEgZXMgdW5hIHDDoWdpbmEgYW5leGFkYSBhbCBJTkFITUkuDQoNClZhbW9zIGEgcmVhbGl6YXIgdW4gYW7DoWxpc2lzIHByZXZpbyBkZSBudWVzdHJvcyBkYXRvcyBwYXJhIHZpc3VhbGl6YXIgZWwgY29tcG9ydGFtaWVudG8gZW4gc2VyaWUgZGUgdGllbXBvLiANCg0KIyMjIyAxLjEuIENhcmdhIGRlIGxvcyBEYXRvcyBkZSBQcmVjaXBpdGFjacOzbi4NCg0KQWhvcmEgdGVuZW1vcyBxdWUgaGFjZXIgbGEgY2FyZ2EgZGUgbnVlc3Ryb3MgZGF0b3MgbG9zIGN1YWxlcyBzZSBlbmN1ZW50cmFuIGVuIGZvcm1hdG8gY3N2ICh0ZXh0byBkZWxpbWl0YWRvIHBvciBjb21hcyksIGVzIHJlY29tZW5kYWRvIHRlbmVyIGVuIGVzdGUgZm9ybWF0byB5YSBxdWUgUiBsbyByZWNvbm9jZSBzaW4gbmVjZXNpZGFkIGRlIGFncmVnYXIgdW5hIGxpYnJlcmlhIGFkaWNpb25hbC4NCg0KYGBge3J9DQpzZXR3ZCgifi9JTkcgQU1CSUVOVEFML0lORk9STUFUSUNBMjMtMjQvUHJveWVjdG8iKQ0KcHJlY2kgPC0gcmVhZC5jc3YoIlByZWNpX0ZpbmFsLmNzdiIsIHNlcCA9ICIsIiwgZGVjID0gIi4iLCBoZWFkZXIgPSBUKQ0KYGBgDQoNCiMjIyMgMS4yLiBDcmVhY2nDs24gZGUgU2VyaWUgZGUgVGllbXBvIHBhcmEgYW7DoWxpc2lzIGRlIGRhdG9zLg0KDQpVbmEgc2VyaWUgZGUgdGllbXBvIGVzIHVuYSBzZWN1ZW5jaWEgZGUgZGF0b3MgdSBvYnNlcnZhY2lvbmVzIG1lZGlkb3MgZW4gZGV0ZXJtaW5hZG9zIG1vbWVudG9zLCBlbiBpbnRlcnZhbG9zIGlndWFsZXMgeSBvcmRlbmFkb3MgY3Jvbm9sw7NnaWNhbWVudGUuDQoNCkVsIGFuw6FsaXNpcyBkZSBzZXJpZXMgZGUgdGllbXBvIHNlIHJlZmllcmUgYWwgcHJvY2VzbyBkZSBhbmFsaXphciBsb3MgZGF0b3MgZGlzcG9uaWJsZXMgcGFyYSBkZXNjdWJyaXIgZWwgcGF0csOzbiBvIGxhIHRlbmRlbmNpYSBlbiBsb3MgZGF0b3MuIFBlcm1pdGUgZXh0cmFlciB5IG1vZGVsYXIgbGFzIHJlbGFjaW9uZXMgZW50cmUgZGF0b3MgYSBsbyBsYXJnbyBkZWwgdGllbXBvLg0KDQpgYGB7cn0NCnByZWNpX3RzIDwtIHRzKHByZWNpJFBhcnF1ZS5CaWNlbnRlbmFyaW8sIGZyZXF1ZW5jeSA9IDEyLCBzdGFydCA9IGMoMTk3NSwxKSkNCnBsb3QocHJlY2lfdHMsIG1haW4gPSAiU2VyaWUgZGUgVGllbXBvIGRlIFByZWNpcGl0YWNpw7NuIDE5NzUtMjAxOSIsIHhsYWIgPSAiQcOxb3MiLCB5bGFiID0gIlByZWNpcGl0YWNpw7NuIChtbSkiKQ0KYGBgDQoNCiMjIyMgMS4zLiBDb21wb25lbnRlcyBkZSBsYSBTZXJpZSBkZSBUaWVtcG8uDQoNClV0aWxpemFyZW1vcyBlbCBjb21hbmRvIGRlY29tcG9zZSgpIGVsIGN1YWwgZGVzYWdyZWdhIGxhIHNlcmllIGRlIHRpZW1wbyBlbiBsb3MgY29tcG9uZW50ZXM6IHRlbmRlbmNpYSwgZXN0YWNpb25hbCwgY8OtY2xpY28geSBhbGVhdG9yaW8uDQoNCmBgYHtyfQ0KcHJlY2lfdHNfZGVzY29tcCA8LSBkZWNvbXBvc2UocHJlY2lfdHMpIA0KYGBgDQoNCiMjIyMjIDEuMy4xLiBUZW5kZW5jaWENCg0KRXMgZWwgcGF0csOzbiBzdWJ5YWNlbnRlIGVuIGxvcyBkYXRvcyBhIGxvIGxhcmdvIGRlbCB0aWVtcG8uIE5vIGVzIG5lY2VzYXJpYW1lbnRlIGxpbmVhbC4NCg0KYGBge3J9DQpwbG90KHByZWNpX3RzX2Rlc2NvbXAkdHJlbmQsIG1haW4gPSAiVGVuZGVuY2lhIFByZWNpcGl0YWNpw7NuIiwgY29sID0gImJsdWUiLCB5bGFiID0gIlByZWNpcGl0YWNpw7NuIChtbSkiKQ0KYGBgDQoNCiMjIyMjIDEuMy4yLiBFc3RhY2lvbmFsaWRhZA0KDQpDdWFuZG8gdW5hIHNlcmllIGVzdGEgaW5mbHVlbmNpYWRhIHBvciBmYWN0b3JlcyBlc3RhY2lvbmFsZXMgZGUgcGVyaW9kbyBmaWpvIGNvbW8gZWwgZMOtYSwgbWVzLCB0cmltZXN0cmUsIGHDsW8sIGV0Yy4NCg0KYGBge3J9DQpwbG90KHByZWNpX3RzX2Rlc2NvbXAkc2Vhc29uYWwsIG1haW4gPSAiRXN0YWNpb25hbGlkYWQgUHJlY2lwaXRhY2nDs24iLCBjb2wgPSAicmVkIiwgeWxhYiA9ICJQcmVjaXBpdGFjacOzbiAobW0pIikNCmBgYA0KDQoNCiMjIyMjIDEuMy4zLiBBbGVhdG9yaWVkYWQNCg0KUGFydGUgaW5leHBsaWNhYmxlIGRlIGxvcyBkYXRvcyBtZWRpZG9zLg0KDQpgYGB7cn0NCnBsb3QocHJlY2lfdHNfZGVzY29tcCRyYW5kb20sIG1haW4gPSAiSXJyZWd1bGFyaWRhZCBQcmVjaXBpdGFjacOzbiIsIGNvbCA9ICJncmVlbiIsIHlsYWIgPSAiUHJlY2lwaXRhY2nDs24gKG1tKSIpDQpgYGANCg0KIyMjIDIuIFByZXBhcmFjacOzbiBkZSBEYXRvcyBwYXJhIGVsIG1vZGVsYW1pZW50by4NCg0KIyMjIyAyLjEuIENvbnZlcnNpw7NuIGRlIERhdG9zIGEgdW4gZGF0YSBmcmFtZS4gDQoNCkxvcyBkYXRhZnJhbWVzIHNvbiB1bmEgY2xhc2UgZGUgb2JqZXRvcyBlc3BlY2lhbCBlbiBSLiBOb3JtYWxtZW50ZSwgY3VhbmRvIHNlIHJlYWxpemEgdW4gZXN0dWRpbyBlc3RhZMOtc3RpY28gc29icmUgbG9zIHN1amV0b3MgdSBvYmpldG9zIGRlIHVuYSBtdWVzdHJhLCBsYSBpbmZvcm1hY2nDs24gc2Ugb3JnYW5pemEgcHJlY2lzYW1lbnRlIGVuIHVuIGRhdGFmcmFtZTogdW5hIGhvamEgZGUgZGF0b3MsIGVuIGxvcyBxdWUgY2FkYSBmaWxhIGNvcnJlc3BvbmRlIGEgdW4gc3VqZXRvIHkgY2FkYSBjb2x1bW5hIGEgdW5hIHZhcmlhYmxlLiBMYSBlc3RydWN0dXJhIGRlIHVuIGRhdGEuZnJhbWUgZXMgbXV5IHNpbWlsYXIgYSBsYSBkZSB1bmEgbWF0cml6LiBMYSBkaWZlcmVuY2lhIGVzIHF1ZSB1bmEgbWF0cml6IHNvbG8gYWRtaXRlIHZhbG9yZXMgbnVtw6lyaWNvcywgbWllbnRyYXMgcXVlIGVuIHVuIGRhdGFmcmFtZSBwb2RlbW9zIGluY2x1aXIgdGFtYmnDqW4gZGF0b3MgYWxmYW51bcOpcmljb3MuDQoNCmBgYHtyfQ0KcHJlY2kgPC0gYXMuZGF0YS5mcmFtZShwcmVjaSkNCmhlYWQocHJlY2ksIG4gPSA2MCkNCmBgYA0KDQpSZW5vbWJyYW1vcyBsYXMgY29sdW1uYXMgZGVsIG1hcmNvIGRlIGRhdG9zIGEg4oCYRmVjaGHigJkgeSDigJhQcmVjaXBpdGFjacOzbuKAmS4gDQoNCmBgYHtyfQ0KbmFtZXMocHJlY2kpIDwtIGMoIkZlY2hhIiwgIlByZWNpcGl0YWNpb24iKQ0KYGBgDQoNClBvc3RlcmlvciBhIGVzbyBlbXBsZWFtb3MgZWwgY29tYW5kbyBjYmluZCgpIHBhcmEgY29tYmluYXIgbGFzIGRvcyBjb2x1bW5hcyBlbiB1biBudWV2byBtYXJjbyBkZSBkYXRvcy4gQ29tYmluYW1vcyBsYSBjb2x1bW5hIGRlIHRlbXBlcmF0dXJhcyBjb24gdW5hIHNlY3VlbmNpYSBkZSBmZWNoYXMgcGFyYSBmb3JtYXIgdW4gbnVldm8gbWFyY28gZGUgZGF0b3MuICANCg0KYGBge3J9DQpwcmVjaSA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKEZlY2hhLCBwcmVjaSRQcmVjaXBpdGFjaW9uKSkNCmBgYA0KDQoNCkRlc3B1w6lzIGNvbnZlcnRpbW9zIGxhIGNvbHVtbmEgZGUg4oCYRmVjaGHigJkgYSBvYmpldG9zIGRlIGZlY2hhIGVuIFIgZW1wbGVhbmRvIGVsIGNvbWFuZG8gYXMuRGF0ZSgpLiBFc3RvIG5vcyBhc2VndXJhIHF1ZSBsb3MgZGF0b3MgZGUgZXN0YSBjb2x1bW5hIHNlYW4gdHJhdGFkb3MgY29tbyBmZWNoYXMuICANCg0KYGBge3J9DQpwcmVjaSRGZWNoYSA8LSBhcy5EYXRlKHByZWNpJEZlY2hhKQ0KbmFtZXMocHJlY2kpIDwtIGMoIkZlY2hhIiwgIlByZWNpcGl0YWNpb24iKQ0KYGBgDQoNCkVuIHJlc3VtZW4sIGhhc3RhIGVzdGEgcGFydGUsIGVsIGPDs2RpZ28gdG9tYSB1biBvYmpldG8gZGUgZGF0b3MgbGxhbWFkbyDigJhwcmVjaeKAmSwgbG8gY29udmllcnRlIGVuIHVuIG1hcmNvIGRlIGRhdG9zIHF1ZSBzb2xpY2l0YSBSLCBjb21iaW5hIGxhIGNvbHVtbmEgZGUgcHJlY2lwaXRhY2nDs24gY29uIHVuYSBzZWN1ZW5jaWEgZGUgZmVjaGFzLCByZW5vbWJyYW1vcyBsYXMgY29sdW1uYXMgeSBhc2VndXJhbW9zIHF1ZSBsYSBjb2x1bW5hIGRlIGZlY2hhcyBlc3RlIGVuIGVsIGZvcm1hdG8gYWRlY3VhZG9zIHBhcmEgc3UgbWFuaXB1bGFjacOzbiBjb21vIGZlY2hhcy4gICANCg0KIyMjIyAyLjIuIENyZWFjacOzbiBkZWwgRGF0YSBGcmFtZSBwYXJhIGxhIG11ZXN0cmEgZGUgbG9zIHJlc3VsdGFkb3Mgc2Vnw7puIGVsIG1vZGVsby4NCg0KQ3JlYW1vcyB1biBudWV2byBkYXRhIGZyYW1lIHBhcmEgbG9zIGRhdG9zIHF1ZSB2YW1vcyBhIHByZWRlY2lyLiBDb24gZXN0ZSBwYXNvIHNlIGNyZWEgdW5hIHNlY3VlbmNpYSBkZSBmZWNoYXMgcHJlZGljaGFzIGRlc2RlIGxhIOKAmGZlY2hhX2ZpbmFs4oCZIGhhc3RhIOKAmGZlY2hhX3ByZWTigJkgZW4gaW50ZXJ2YWxvcyBtZW5zdWFsZXMuICANCg0KYGBge3J9DQpmZWNoYV9wcmVkaWNfcHJlY2kgPC0gYXMuRGF0ZShzZXEoZnJvbSA9IGZlY2hhX2ZpbmFsLCB0byA9IChmZWNoYV9wcmVkKSwgYnkgPSAibW9udGgiKSkNCmBgYA0KDQpJbmljaWFsaXphbW9zIHVuIHZlY3RvciDigJhQcmVjaXBpdGFjacOzbuKAmSBjb24gbG9zIHZhbG9yZXMg4oCYTkHigJkgeSBjb21iaW5hbW9zIGVzdGUgdmVjdG9yIGNvbiBlbCB2ZWN0b3IgZGUgZmVjaGFzIHByZWRpY2hhcyBwYXJhIGNyZWFyIHVuIG51ZXZvIG1hcmNvIGRlIGRhdG9zIGRlbm9taW5hZG8g4oCYZmVjaGFfcHJlZGljX3ByZWNp4oCZLiBZIHRhbWJpw6luIGNvbnZlcnRpbW9zIGxhIGNvbHVtbmEg4oCYZmVjaGFfcHJlZGljX3ByZWNp4oCZIGEgdW4gZm9ybWF0byBmZWNoYSB5IHJlbm9tYnJhbW9zIGxhcyBjb2x1bW5hcyBhIOKAmEZlY2hh4oCZIHkg4oCYUHJlY2lwaXRhY2nDs27igJkuICANCg0KYGBge3J9DQpQcmVjaXBpdGFjaW9uIDwtIGFzLm51bWVyaWMoTkEpDQpmZWNoYV9wcmVkaWNfcHJlY2kgPC0gYXMuZGF0YS5mcmFtZShjYmluZChmZWNoYV9wcmVkaWNfcHJlY2ksIFByZWNpcGl0YWNpb24pKQ0KZmVjaGFfcHJlZGljX3ByZWNpJGZlY2hhX3ByZWRpY19wcmVjaSA8LSBhcy5EYXRlKGZlY2hhX3ByZWRpY19wcmVjaSRmZWNoYV9wcmVkaWNfcHJlY2kpDQpuYW1lcyhmZWNoYV9wcmVkaWNfcHJlY2kpIDwtIGMoIkZlY2hhIiwgIlByZWNpcGl0YWNpb24iKQ0KYGBgDQoNCkNvbWJpbmFtb3MgbG9zIGRhdG9zIG9yaWdpbmFsZXMg4oCYcHJlY2nigJkgY29uIGxhcyBmZWNoYXMgcHJlZGljaGFzIOKAmGZlY2hhX3ByZWRpY19wcmVjaeKAmSBlbXBsZWFuZG8gZWwgY29tYW5kbyByYmluZCgpLCBlbCBjdWFsIGHDsWFkZSBsYXMgZmlsYXMgYWwgbWFyY28gZGUgZGF0b3Mg4oCYdGVtcOKAmS4NCmBgYHtyfQ0KcHJlY2kgPC0gcmJpbmQocHJlY2ksIGZlY2hhX3ByZWRpY19wcmVjaSkNCmBgYA0KDQpEdXBsaWNhbW9zIGxhIGNvbHVtbmEgZmVjaGFzIHBhcmEgbm8gcGVyZGVyIGxvcyBkYXRvcyBlbiBlbCBwcm9jZXNvIGRlIG1vZGVsYW1pZW50byB5IHRhbWJpw6luIHNlcGFyYW1vcyBlc3RhIGNvbHVtbmEgZW4g4oCYQcOxb+KAmSwg4oCYTWVz4oCZLCDigJhEaWHigJkuICANCg0KYGBge3J9DQpwcmVjaSRGZWNoYV9kdXBfcHJlY2kgPC0gcHJlY2kkRmVjaGENCg0KcHJlY2kgPC0gcHJlY2kgJT4lIHNlcGFyYXRlKEZlY2hhLCBjKCJBw7FvIiwgIk1lcyIsICJEaWEiKSkNCmBgYA0KDQpFbiBlbCBzaWd1aWVudGUgY8OzZGlnbyBjb252ZXJ0aW1vcyBjYWRhIHVuYSBkZSBsYXMgY29sdW1uYXMg4oCYQcOxb+KAmSwg4oCYTWVz4oCZLCDigJhEaWHigJkgZW4gY2Fyw6FjdGVyIG51bcOpcmljby4gDQoNCmBgYHtyfQ0KcHJlY2kkQcOxbyA8LSBhcy5udW1lcmljKHByZWNpJEHDsW8pDQpwcmVjaSRNZXMgPC0gYXMubnVtZXJpYyhwcmVjaSRNZXMpDQpwcmVjaSREaWEgPC0gYXMubnVtZXJpYyhwcmVjaSREaWEpDQoNCmBgYA0KDQojIyMgMy4gTW9kZWxhbWllbnRvDQoNCiMjIyMgMy4xLiBDcmVhY2nDs24gZGVsIGNvbmp1bnRvIGRlIGRhdG9zIGRlIGVudHJlbmFtaWVudG8geSB0ZXN0ZW8uDQoNCkVzdGFibGVjZW1vcyBsYSBzZW1pbGxhICjigJhzZWVk4oCZKSBwYXJhIHF1ZSBsb3MgcmVzdWx0YWRvcyBzZWFuIHJlcHJvZHVjaWJsZXMgZW4gZWwgZnV0dXJvLiBFc3RvIHF1aWVyZSBkZWNpciBxdWUsIHNpIHNlIGVqZWN1dGEgZWwgY8OzZGlnbyB2YXJpYXMgdmVjZXMsIHNlIG9idGVuZHLDoSBsb3MgbWlzbW9zIHJlc3VsdGFkb3MgZW4gY2FkYSBlamVjdWNpw7NuLCBzaWVtcHJlIHkgY3VhbmRvIHNlIHVzZSBsYSBtaXNtYSBzZW1pbGxhLiAgDQoNCkVtcGxlYW1vcyBsYSBmdW5jacOzbiDigJhjcmVhdGVEYXRhUGFydGl0aW9u4oCZIGRlbCBwYXF1ZXRlIOKAmGNhcmV04oCZIHBhcmEgY3JlYXIgdW4gw61uZGljZSBxdWUgaW5kaWNhIHF1ZSBvYnNlcnZhY2lvbmVzIHNlIGluY2x1aXLDoSBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvLiBTZSBmaWx0cmFuIGxhcyBvYnNlcnZhY2lvbmVzIHkgc2UgZWxpbWluYSBsYXMgZmlsYXMgY29uIGxvcyB2YWxvcmVzIGZhbHRhbnRlcyBlbiBsYSBjb2x1bW5hIOKAmFRlbXBlcmF0dXJh4oCZDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTk5NykNCnRyYWluX3ByZWNpIDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24obmEub21pdChzdWJzZXQocHJlY2ksIHByZWNpJEZlY2hhX2R1cF9wcmVjaSA8IGZlY2hhX2ZpbmFsKSkkUHJlY2lwaXRhY2lvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHAgPSAwLjcsIGxpc3QgPSBGKSANCmBgYA0KDQpFbCB2YWxvciDigJhwPTAuN+KAmSBub3MgaW5kaWNhIHF1ZSBzZSB2YW4gYSBlbXBsZWFyIGVsIDcwJSBkZSBsb3MgZGF0b3MgY29tbyBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvLiBUYW1iacOpbiBlc3BlY2lmaWNhbW9zIHF1ZSBxdWVyZW1vcyB1biB2ZWN0b3IgZGUgw61uZGljZSBlbiBsdWdhciBkZSB1bmEgbGlzdGEgY29uIGVsIHN1YmNvbWFuZG8g4oCYbGlzdCA9IEZBTFNF4oCZLiAgDQoNCkVtcGxlYW1vcyBlbCDDrW5kaWNlIOKAmHRyYWluX3ByZWNp4oCZIHBhcmEgc2VsZWNjaW9uYXIgbGFzIG9ic2VydmFjaW9uZXMgY29ycmVzcG9uZGlzdGUgYWwgY29uanVudG8gZGUgcHJ1ZWJhIHV0aWxpemFkbyBsYSBub3RhY2nDs24gZGVsIHN1YmNvbmp1bnRvICjigJhwcmVjaVstdHJhaW5fcHJlY2ksXeKAmSkuIENvbWJpbmFtb3MgZXN0YXMgb2JzZXJ2YWNpb25lcyBjb24gbGFzIHF1ZSB0aWVuZW4g4oCYRmVjaGFfZHVw4oCZIG1heW9yIG8gaWd1YWwgYSDigJhmZWNoYV9maW5hbOKAmSBlbXBsZWFuZG8g4oCYcmJpbmTigJkuDQoNCmBgYHtyfQ0KdGVzdF9wcmVjaSA8LSByYmluZChwcmVjaVstdHJhaW5fcHJlY2ksXSAsIHN1YnNldChwcmVjaSwgcHJlY2kkRmVjaGFfZHVwX3ByZWNpID49IGZlY2hhX2ZpbmFsKSkNCg0KYGBgDQoNCkVuIGNvbmNsdXNpw7NuLCBlbiBlc3RhIHBhcnRlIHNlIGNyZWEgdW4gY29uanVudG8gZGUgZW50cmVuYW1pZW50byB5IHRlc3QuIEVsIGNvbmp1bnRvIGRlIGVudHJlbmFtaWVudG8gaW5jbHV5ZSBkYXRvcyBoYXN0YSBsYSBmZWNoYSBmaW5hbCwgbWllbnRyYXMgcXVlIGVsIGNvbmp1bnRvIGRlIHBydWViYSBpbmNsdXllIGRhdG9zIGRlc2RlIGxhIGZlY2hhIGZpbmFsIGhhY2lhIGFkZWxhbnRlLiBDYWJlIHJlY2FsY2FyIHF1ZSBlbiBlbCBjb25qdW50byBkZSBlbnRyZW5hbWllbnRvIHNlIHNlbGVjY2lvbmEgZWwgNzAlIGRlIGxhcyBvYnNlcnZhY2lvbmVzLiAgDQoNCiMjIyMgMy4yLiBFamVjdWNpw7NuIGRlbCBhbGdvcml0bW8gUmFuZG9tIEZvcmVzdC4NCg0KRXN0ZSBhbGdvcml0bW8gY29uc3RydXllIG3Dumx0aXBsZXMgYXJib2xlcyBkZSBjbGFzaWZpY2FjacOzbiBhbGVhdG9yaW9zIGRlcGVuZGllbmRvIGRlIGxvcyBkYXRvcyBjb24gbG9zIHF1ZSBzZSBlc3TDoSB0cmFiYWphbmRvLiBFbiBsb3MgcGFzb3MgYW50ZXJpb3JlcyBzZSBwcmVwYXJhcm9uIHRvZG9zIGxvcyBjb25qdW50b3MgZGUgZGF0b3MgYWRlY3VhZGFtZW50ZSBwYXJhIHF1ZSBzZSBwdWVkYSByZWFsaXphciBsb3MgcHJvY2Vzb3MgY29ycmVzcG9uZGllbnRlcy4gQWRlbcOhcyBkZSBkZWZpbmnDsyBlbCBuw7ptZXJvIGRlIMOhcmJvbGVzIHF1ZSBzZSB2YW4gaGFuIGNvbnN0cnVpciBhIHRyYXbDqXMgZGVsIGFyZ3VtZW50byDigJxudHJlZeKAnSANCg0KYGBge3J9DQptb2RfcmZfcHJlY2kgPC0gcmFuZG9tRm9yZXN0KFByZWNpcGl0YWNpb24gfiBGZWNoYV9kdXBfcHJlY2ksIGRhdGEgPSBwcmVjaVt0cmFpbl9wcmVjaSxdLCANCiAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJyZWdyZXNzaW9uIiwgbnRyZWUgPSA1MDApDQpgYGANCg0KQ3JlYW1vcyBsYXMgZGlmZXJlbnRlcyB2YXJpYWJsZXMgZG9uZGUgc2UgdmFuIGEgYWxtYWNlbmFyIGxvcyByZXN1bHRhZG9zIGRlIGxvcyBwcm9jZXNvcyBkZXBlbmRpZW5kbyBkZSBsb3Mgw61uZGljZXMgcXVlIHNlIGVzdGFibGVjaWVyb24gY29tbyBwYXLDoW1ldHJvcyBkZWwgbW9kZWxhbWllbnRvLCBjb24gbG8gcXVlIHRlbmVtb3MgdW5hIHZhcmlhYmxlIGRlIHJlc3VsdGFkbyBwYXJhIGxhIHByZWRpY2Npw7NuIHkgb3RyYSBkb25kZSBzZSBhbG1hY2VuYXJhbiBsYSBjb21iaW5hY2nDs24gZGUgbG9zIGRhdG9zIHJlc3VsdGFudGVzIGRlIGxhIHByZWRpY2Npw7NuLiAgDQoNCmBgYHtyfQ0KcHJlZF9yZl9wcmVjaSA8LSBwcmVkaWN0KG1vZF9yZl9wcmVjaSwgdGVzdF9wcmVjaSkNCg0KZGF0b3NfcmZfcHJlY2kgPC0gY2JpbmQocHJlZF9yZl9wcmVjaSwgdGVzdF9wcmVjaSkNCg0KYGBgDQoNCiMjIyMgMy4zLiBFcnJvcmVzIHBhcmEgbGEgY29tcHJvYmFjaW9uIGRlbCBtb2RlbG8uDQoNCkx1ZWdvIGRlIGVzdG8gdmFtb3MgYSBkZXRlcm1pbmFyIGVsIHBvcmNlbnRhamUgZGUgZXJyb3IgcXVlIHNlIG9idGllbmUgZW4gbGEgbW9kZWxhY2nDs24gcmVhbGl6YWRhIGNvbiBsbyBjdWFsIHBvZGVtb3MgZXN0YWJsZWNlciBzaSBlc3TDoSBkZW50cm8gZGVsIHJhbmdvIGFjZXB0YWJsZS4gRXN0byBub3MgZ2FyYW50aXphIHF1ZSBsb3MgZGF0b3MgcXVlIHNlIGhhbiBvYnRlbmlkbyBzZWFuIHRyYXRhZG9zIGNvbW8gZGF0b3MgY29uZmlhYmxlcyBwYXJhIGRldGVybWluYXIgcHJlZGljY2lvbmVzIGZ1dHVyYXMgYXBsaWNhbmRvIGVzdGEgbWV0b2RvbG9nw61hIHkgYmFzYWRvIGVuIGVsIGFuw6FsaXNpcyBkZSBxdWUgc2UgaGEgdmVuaWRvIHJlYWxpemFuZG8gZW4gbGEgdG9tYSB5IHRyYXRhbWllbnRvIGRlIGRhdG9zLiANCg0KYGBge3J9DQplcnJvcl9hYnNfcmZfcHJlY2kgPC1STVNFKGRhdG9zX3JmX3ByZWNpJFByZWNpcGl0YWNpb24sIGRhdG9zX3JmX3ByZWNpJHByZWRfcmZfcHJlY2ksIG5hLnJtID0gVCkNCg0KZXJyb3JfcG9yX3JmX3ByZWNpIDwtIGVycm9yX2Fic19yZiAvIGRhdG9zX3JmX3ByZWNpW2RhdG9zX3JmX3ByZWNpJEZlY2hhX2R1cF9wcmVjaSA9PSBtYXgobmEub21pdChkYXRvc19yZl9wcmVjaSkkRmVjaGFfZHVwX3ByZWNpKSwgXSRQcmVjaXBpdGFjaW9uDQplcnJvcl9wb3JfcmZfcHJlY2kqMTAwDQoNCk1BRShkYXRvc19yZl9wcmVjaSRQcmVjaXBpdGFjaW9uLCBkYXRvc19yZl9wcmVjaSRwcmVkX3JmX3ByZWNpLCBuYS5ybSA9IFQpDQpgYGANCg0KIyMjIDQuIEdyYWZpY28gZGVsIG1vZGVsbyByZXN1bHRhbnRlDQoNCkZpbmFsbWVudGUgc2UgdmEgYSBkaWJ1amFyIGVsIG1vZGVsbyBxdWUgc2Ugb2J0dXZvIGNvbW8gcmVzdWx0YWRvIGRlbCBwcm9jZXNvIHJlYWxpemFkbywgZW4gZWwgY3VhbCBzZSBpbmNsdXllbiBlbCBjb21wb3J0YW1pZW50byByZWFsIGzDrW5lYXMgZW4gbmVncm8geSBlbCBtb2RlbG8gY2FsY3VsYWRvIGzDrW5lYXMgZW4gb2pvLCBhc8OtIGNvbW8gZWwgdMOtdHVsbyBkZSBsYSBlc3RhY2nDs24gcXVlIGhlbW9zIHRvbWFkbyBjb21vIHJlZmVyZW5jaWEgcGFyYSBsYSBlamVjdWNpw7NuIGRlbCBtb2RlbGFtaWVudG8uIA0KDQpgYGB7cn0NCmdncGxvdCgpICsgZ2VvbV9saW5lKGRhdGEgPSBkYXRvc19yZl9wcmVjaSwgYWVzKHggPSBGZWNoYV9kdXBfcHJlY2ksIHkgPSBQcmVjaXBpdGFjaW9uKSwgY29sb3IgPSAiYmxhY2siKSArDQogIGdlb21fbGluZShkYXRhID0gZGF0b3NfcmZfcHJlY2ksIGFlcyh4ID0gRmVjaGFfZHVwX3ByZWNpLCB5ID0gcHJlZF9yZl9wcmVjaSksIGNvbG9yID0gInJlZCIpICsgDQogIGdndGl0bGUoIk1vZGVsbyBkZSBQcmVkaWNjacOzbiBwYXJhIFByZWNpcGl0YWNpw7NuIEVzdGFjaW9uIEnDsWFxdWl0byAiKQ0KYGBgDQoNCkFkZW1hcyBhZGp1bnRhbW9zIGxhIHRhYmxhIGRlIGxvcyByZXN1bHRhZG9zIHBhcmEgbnVlc3RybyBtb2RlbG8geSBsb3MgZGF0b3Mgb3JpZ2luYWxlcy4NCg0KYGBge3J9DQpuYW1lcyhkYXRvc19yZl9wcmVjaSkgPC0gYygiUHJlY2lwaXRhY2nDs24gTW9kZWxvIiwgIkHDsW8iLCAiTWVzIiwgIkTDrWEiLCAiVGVtcGVyYXR1cmEiLCAiRmVjaGEiKQ0KaGVhZChkYXRvc19yZl9wcmVjaSwgbiA9IDYwKQ0KYGBgDQoNCiMgQVBMSUNBQ0nDk04gWSBSRVBPU0lUT1JJTyBERSBEQVRPUw0KDQojIyBBcGxpY2FjacOzbg0KDQpBcXVpIHBvZGVtb3MgZGlnaXJpcm5vcyBhIG51ZXN0cmFzIGFwbGljYWNpb25lcyBkZXNkZSBjdWFscXVpZXIgb3JkZW5hZG9yLkVuIGVzdGUgY2FzbyByZWFsaXphbW9zIGRvcyBhcHBzOg0KDQoqVGVtcGVyYXR1cmEqIFtBUFBdKGh0dHBzOi8vNnlpNjR5LW5haW0tYWd1aWxhci5zaGlueWFwcHMuaW8vQXBwX0luZm9yX0NsaW1hLykNCg0KKlByZWNpcGl0YWNpw7NuKiBbYXBwXShodHRwczovLzZ5aTY0eS1uYWltLWFndWlsYXIuc2hpbnlhcHBzLmlvL2FwcF9wcmVjaS8pDQoNCiMjIFJlcG9zaXRvcmlvIGRlIERhdG9zDQoNCkVzdGUgZXMgbnVlc3RybyBbUmVwb3NpdG9yaW9dKGh0dHBzOi8vZ2l0aHViLmNvbS9uaW5vZ2FlbDE5OTYvSU5GT1JfQ0xJTUFfMjAyNCksIGFxdWkgcHVlZGVuIG9ic2VydmFyIHRvZG9zIGxvcyBkYXRvcyBxdWUgc2UgdXRpbGl6YXJvbiBwYXJhIGVsIGRlc2Fycm9sbG8gZGUgbnVlc3RybyBwcm95ZWN0by4NCkFkZW3DoXMgY29tcGFydGltb3MgZWwgY29kaWdvIGZ1ZW50ZSBkZSBsYSBiYXNlIGRlIG51ZXN0cmEgcMOhZ2luYSBzdWJpZGEgYSBSUHVicy4gDQoNCg0KDQo=