Objetivo: En este documento se presentan los primeros pasos en R, desde la instalación del programa y creación de proyectos, hasta la lectura y primeros pasos de análisis de bases de datos.

Además, de la creación de objetos, la creación de vectores, la asignación de variables, la creación de secuencias de números, la redondear números, entre otros.

1 Instalación de R y RStudio

La instalación de R y RStudio se puede hacer desde la página web posit.com. Para esto:

  1. Descargue e instale R desde el link https://cran.rstudio.com/, aquí encontrara el paso a paso:

    Para Windows

    1. Haz clic en el enlace «Download R for Windows».
    2. En la siguiente página, haz clic en «base».
    3. Descarga el instalador haciendo clic en el enlace que dice **Download R X.X.X for Windows** (donde `X.X.X` es el número de la versión más reciente).
    4. Una vez descargado, ejecuta el archivo de instalación.
    5. Sigue las instrucciones del asistente de instalación, aceptando los términos de la licencia y seleccionando las opciones predeterminadas.

    Para Mac

    1. Haz clic en el enlace «Download R for macOS»

    2. Descarga el instalador haciendo clic en el enlace que dice **R-X.X.X.pkg** (donde `X.X.X` es el número de la versión más reciente).

    3. Una vez descargado, abre el archivo `.pkg`.

    4. Sigue las instrucciones del asistente de instalación, aceptando los términos de la licencia y seleccionando las opciones predeterminadas.

  1. Abre tu navegador web y visita la página oficial de RStudio: https://posit.co/download/rstudio-desktop/

    Para Windows

    1. Haz clic en «Products» y selecciona «RStudio».

    2. En la página de RStudio, haz clic en «Download RStudio».

    3. Selecciona «RStudio Desktop» y haz clic en «Download RStudio for Windows».

    4. Descarga el instalador y ejecuta el archivo descargado.

    5. Sigue las instrucciones del asistente de instalación, aceptando los términos de la licencia y seleccionando las opciones predeterminadas.

    Para Mac

    1. Haz clic en «Products» y selecciona «RStudio».
    2. En la página de RStudio, haz clic en «Download RStudio».
    3. Selecciona «RStudio Desktop» y haz clic en «Download RStudio for macOS».
    4. Descarga el instalador y abre el archivo descargado.
    5. Arrastra el icono de RStudio a la carpeta «Aplicaciones» para completar la instalación.

2 Creación cuenta de GitHub

  1. Registrarse en GitHub:

    1. Abre tu navegador web y visita la página de registro de GitHub: https://github.com/join

    2. Haz clic en «Sing in».

    3. Sigue las instrucciones para crear tu cuenta con tu dirección de correo electrónico académico

    4. Verifica tu dirección de correo electrónico.

  2. Solicitar la cuenta de GitHub para Estudiantes:

    1. Abre tu navegador web y visita la página de GitHub Education: https://education.github.com/discount_requests/new

    2. Haz clic en «Get benefits» bajo la sección «Student».

    3. Completa el formulario con tu información personal y académica. Es posible que necesites proporcionar una prueba de tu estatus de estudiante, como tu carnet o el certificado de matricula.

  3. Envía tu solicitud y espera la aprobación de GitHub Education. Esto puede tardar algunos días.

2.1 Configurar GitHub Copilot en RStudio

  1. Activar GitHub Copilot:

    1. Una vez que tengas tu cuenta de GitHub para estudiantes aprobada, ve a https://github.com/features/copilot

    2. 2. Haz clic en «Sign up for GitHub Copilot» y sigue las instrucciones para activar Copilot en tu cuenta.

  2. Usar GitHub Copilot con RStudio

    1. Abre RStudio.

    2. Activa GitHub Copilot en «Tools» y «Global options»

GitHub Copilot es una herramienta de inteligencia artificial desarrollada por GitHub y OpenAI que ayuda a los desarrolladores a escribir código más rápido y de manera más eficiente al sugerir líneas de código o funciones completas basadas en el contexto del código en el que están trabajando. Aunque GitHub Copilot se integra mejor con Visual Studio Code (VS Code), se puede usar en combinación con RStudio para mejorar la productividad en el desarrollo de código en R. Aquí te explico cómo puede ser útil:

2.2 Beneficios de Usar GitHub Copilot con RStudio

  1. Generación de Código Automática:

    • GitHub Copilot puede sugerir fragmentos de código, funciones y estructuras completas basadas en comentarios y el código que estás escribiendo.

    • Esto puede ahorrar tiempo y reducir errores al escribir código repetitivo o estándar.

  2. Mejora de la Productividad:

    • Al proporcionar sugerencias en tiempo real, Copilot puede ayudar a acelerar el proceso de desarrollo, permitiéndote concentrarte en la lógica y el análisis en lugar de la sintaxis.
  3. Aprendizaje y Educación:

    • Copilot puede ser una herramienta educativa útil para aprender nuevas funciones y mejores prácticas de programación en R al sugerir soluciones a problemas comunes.

    • Puede actuar como un tutor que sugiere mejoras y alternativas al código que escribes.

  4. Reducción de Errores:

    • Al proporcionar fragmentos de código pre-probados y sugerencias basadas en patrones comunes, Copilot puede ayudar a reducir la cantidad de errores y bugs en el código.
  5. Exploración de Nuevas Funcionalidades:

    • Copilot puede sugerir funciones y bibliotecas que quizás no conocías, expandiendo tus capacidades y conocimiento del lenguaje R.

3 Proyecto en R

Un proyecto en R es una estructura de trabajo que ayuda a organizar y gestionar tu código, datos, resultados y otros archivos relacionados con un análisis o desarrollo en R. Utilizar proyectos en RStudio, facilita la gestión de múltiples archivos y la configuración del entorno de trabajo, asegurando que todo lo necesario para el análisis esté en un solo lugar.

3.1 Características de un Proyecto en R

  1. Directorio de Trabajo:

    • Un proyecto en R tiene su propio directorio de trabajo, lo que ayuda a mantener todos los archivos organizados y relacionados con el proyecto en un solo lugar.
  2. Configuración del Entorno:

    • RStudio guarda la configuración específica del proyecto, como los archivos abiertos, el historial de comandos y la configuración del entorno.
  3. Facilidad de Compartir:

    • Los proyectos son fáciles de compartir con otros, ya que todos los archivos relacionados se encuentran en el mismo directorio. Esto es especialmente útil para la colaboración.

3.2 Crear un Proyecto en RStudio

Aquí tienes una guía paso a paso para crear un proyecto en RStudio:

  1. Abrir RStudio:

    • Inicia RStudio en tu computadora.
  2. Crear un Nuevo Proyecto:

    • Ve al menú superior y selecciona File > New Project....

    Seleccionar Tipo de Proyecto:

    • Aparecerá una ventana con varias opciones. Selecciona New Directory si deseas crear un proyecto desde cero, Existing Directory si deseas convertir una carpeta existente en un proyecto, o Version Control si deseas crear un proyecto a partir de un repositorio de Git.

    Para este ejemplo, seleccionaremos New Directory.

  3. Elegir un Tipo de Directorio:

    • Selecciona New Project para crear un proyecto vacío. También puedes elegir R Package si estás desarrollando un paquete de R o Shiny Web Application para aplicaciones web interactivas.
  4. Configurar el Proyecto:

    • Escribe un nombre para tu proyecto y selecciona la ubicación donde deseas crear el directorio del proyecto. RStudio creará una nueva carpeta con este nombre en la ubicación seleccionada.
  5. Crear el Proyecto:

    • Haz clic en Create Project. RStudio creará el nuevo proyecto y abrirá una nueva sesión con este proyecto activo.

3.3 Estructura de un Proyecto en R

Una vez creado el proyecto, RStudio configurará automáticamente una estructura básica de carpetas y archivos. Aquí hay un ejemplo de cómo podría verse:

mi_proyecto/

├── mi_proyecto.Rproj # Archivo de proyecto de RStudio

├── data/ # Carpeta para datos

├── scripts/ # Carpeta para scripts de R

├── output/ # Carpeta para resultados y gráficos

├── README.md # Archivo de documentación del proyecto

└── .git/ # Carpeta de Git si el proyecto está versionado

3.4 Trabajar con un Proyecto en R

  1. Abrir el Proyecto:

    • Abre RStudio y selecciona File > Open Project... para abrir un proyecto existente.
  2. Organizar Archivos:

    • Coloca tus scripts de R en la carpeta scripts/,
      tus datos en la carpeta data/
      y tus resultados en la carpeta output/.

4 R sin proyecto - ¿Cómo definir la ruta?

Cuando trabajas en R y no estás utilizando un proyecto de RStudio, definir rutas a archivos puede ser un poco más desafiante porque no tienes la estructura organizada de un proyecto que facilite la gestión de directorios. Aquí te explico cómo manejar rutas en R en este contexto y algunas prácticas recomendadas.

4.1 Definir Rutas en R sin un Proyecto

  1. Ruta Absoluta:

    • Una ruta absoluta especifica la ubicación completa del archivo en el sistema de archivos, desde la raíz hasta el archivo.

    • Ejemplo en Windows:

      file_path <- "C:/Users/Usuario/Documents/datos/mi_archivo.csv" data <- read.csv(file_path)
    • Ejemplo en Mac/Linux:

      file_path <- "/Users/Usuario/Documents/datos/mi_archivo.csv" data <- read.csv(file_path)
  2. Ruta Relativa:

    • Una ruta relativa especifica la ubicación del archivo en relación con el directorio de trabajo actual de R. El directorio de trabajo es el directorio en el que R está operando en ese momento.

    • Para ver o cambiar el directorio de trabajo, puedes usar getwd() y setwd() respectivamente.

    • Ejemplo para obtener el directorio de trabajo actual:

      getwd()
    • Ejemplo para establecer un nuevo directorio de trabajo:

      setwd("C:/Users/Usuario/Documents/datos") file_path <- "mi_archivo.csv" data <- read.csv(file_path)
    • Ejemplo para una ruta relativa desde el directorio de trabajo:

      file_path <- "datos/mi_archivo.csv" data <- read.csv(file_path)
  3. Uso del Paquete here:

    • Aunque here se utiliza comúnmente dentro de un proyecto de RStudio, puedes usarlo fuera de un proyecto para construir rutas relativas de manera más robusta.

    • Primero, instala el paquete si no lo tienes:

      install.packages("here")
    • Luego, utiliza here() para construir rutas relativas:

      library(here) file_path <- here("datos", "mi_archivo.csv") data <- read.csv(file_path)

4.2 Cómo Definir Rutas de Manera Eficiente

  1. Conoce tu Directorio de Trabajo:

    • Siempre verifica tu directorio de trabajo actual utilizando getwd() para asegurarte de que las rutas relativas sean correctas.
  2. Utiliza Rutas Relativas Siempre que Sea Posible:

    • Las rutas relativas hacen que el código sea más flexible y portátiles, especialmente si el script se mueve entre diferentes directorios o máquinas.
  3. Mantén una Estructura de Directorios Clara:

    • Organiza tus archivos y datos en carpetas claramente estructuradas para facilitar la referencia y evitar errores en las rutas.
  4. Documenta las Rutas:

    • Asegúrate de documentar en el script qué directorio de trabajo se asume o cómo se espera que se estructuren las carpetas para evitar confusiones.

5 Script en R

Un script en R es un archivo de texto que contiene una serie de comandos y código escrito en el lenguaje de programación R. Estos comandos se ejecutan secuencialmente para realizar tareas específicas, como análisis de datos, visualización de datos, manipulación de datos y más. Los scripts en R tienen generalmente la extensión .R.

5.1 Características de un Script en R

  1. Archivo de Texto:

    • Un script es simplemente un archivo de texto plano que puede ser editado con cualquier editor de texto, aunque normalmente se edita y ejecuta dentro de un entorno de desarrollo integrado (IDE) como RStudio.
  2. Secuencia de Comandos:

    • Contiene una secuencia de comandos que R ejecuta en el orden en que aparecen. Esto permite automatizar procesos y realizar análisis repetibles.
  3. Reutilización:

    • Los scripts pueden guardarse y reutilizarse, lo que es especialmente útil para repetir análisis o compartir metodologías con otros.
  4. Documentación:

    • Los scripts suelen incluir comentarios (líneas que comienzan con #) que describen lo que hace cada parte del código, facilitando la comprensión y el mantenimiento del script.

5.2 Crear y Ejecutar un Script en RStudio

Aquí hay una guía paso a paso para crear y ejecutar un script en RStudio:

  1. Abrir RStudio:

    • Inicia RStudio en tu computadora.
  2. Crear un Nuevo Script:

    • Ve al menú superior y selecciona File > New File > R Script. Esto abrirá una nueva ventana de script en RStudio.

  3. Escribir Código en el Script:

    • Escribe los comandos que deseas ejecutar en el script.
  4. Guardar el Script:

    • Guarda el script seleccionando File > Save o presionando Ctrl+S (Cmd+S en Mac). Elige un nombre y una ubicación para el archivo, asegurándote de que tiene la extensión .R.
  5. Ejecutar el Script:

    • Puedes ejecutar el script completo seleccionando Code > Run Region > Run All o presionando Ctrl+Shift+Enter (Cmd+Shift+Enter en Mac).

    • También puedes ejecutar partes del script seleccionando las líneas de código que deseas ejecutar y presionando Ctrl+Enter (Cmd+Enter en Mac).

5.3 Documentación y Comentarios

Los comentarios en los scripts de R son importantes para la claridad y la colaboración. Los comentarios comienzan con el símbolo # y se utilizan para explicar el propósito de diferentes partes del código.

6 R Markdown

Es una extensión de Markdown que permite la integración de código R directamente en documentos que combinan texto, código y resultados de análisis. R Markdown es una herramienta muy poderosa para crear informes reproducibles y documentos que incluyen análisis estadísticos, gráficos, tablas y más.

6.1 Características de R Markdown

  1. Combina Texto y Código:

    • Puedes escribir texto en formato Markdown y agregar bloques de código R que se ejecutarán y mostrarán los resultados en el documento.
  2. Reproducibilidad:

    • Permite generar documentos reproducibles donde el código y los resultados se actualizan automáticamente cuando cambias el código o los datos.
  3. Salida en Múltiples Formatos:

    • Puedes renderizar documentos en HTML, PDF, Word, y otros formatos.
  4. Interactividad:

    • Permite crear documentos interactivos, como aplicaciones Shiny y presentaciones con R Markdown.

6.2 Estructura de un Documento R Markdown

Un documento R Markdown típico tiene tres partes:

  1. YAML Header:

    • Contiene metadatos sobre el documento, como el título, el autor, la fecha y el formato de salida.

      ---

      title: "Análisis de Datos"

      author: "Tu Nombre"

      date: "2024-07-30"

      output: html_document

      ---

  2. Chunks de Código:

    • Se utilizan para insertar y ejecutar código R. Los chunks de código están delimitados por ```{r} al principio y ``` al final.

      ```{r}

      # Cargar datos

      data <- read.csv("data/mi_archivo.csv")

      summary(data)

      ```

Texto en Markdown:

  • Utiliza la sintaxis de Markdown para formatear texto, encabezados, listas, enlaces y otros elementos.

6.3 Crear y Ejecutar un Documento R Markdown en RStudio

  1. Abrir RStudio:

    • Inicia RStudio en tu computadora.
  2. Crear un Nuevo Documento R Markdown:

    • Ve al menú superior y selecciona File > New File > R Markdown....
  3. Configurar el Documento:

    • Aparecerá una ventana para configurar el nuevo documento. Introduce el título, el autor y selecciona el formato de salida (HTML, PDF, Word). Haz clic en OK.

  4. Escribir el Contenido:

    • RStudio abrirá un nuevo archivo R Markdown con una estructura básica. Puedes editar este archivo para agregar tu propio texto, chunks de código y otros elementos.
  5. Renderizar el Documento:

    • Para generar el documento final, haz clic en el botón Knit en la barra de herramientas de RStudio. Esto ejecutará el código en los chunks y generará un documento en el formato seleccionado (HTML, PDF, Word).

7 Paquetes en R

  • Un paquete es una colección de funciones, datos y documentación que extiende las capacidades del lenguaje R.

  • Los paquetes permiten a los usuarios realizar tareas específicas, como análisis estadísticos, gráficos, manipulación de datos, y mucho más.

  • Los paquetes son una forma de compartir y reutilizar código y recursos en R.

7.1 Qué es un Paquete

  1. Funciones:

    • Contiene funciones que realizan tareas específicas. Por ejemplo, el paquete ggplot2 incluye funciones para crear gráficos avanzados.
  2. Datos:

    • Incluye conjuntos de datos que pueden ser utilizados para practicar o demostrar funcionalidades del paquete.
  3. Documentación:

    • Proporciona documentación y ejemplos sobre cómo usar las funciones del paquete.
  4. Vignettes:

    • Documentos adicionales que proporcionan tutoriales y ejemplos más detallados sobre el uso del paquete.

7.2 Instalación de un Paquete

Para instalar un paquete en R, utilizas la función install.packages(). Esta función descarga e instala el paquete desde CRAN (Comprehensive R Archive Network) o desde otro repositorio especificado.

Ejemplo de Instalación:

# Instalar el paquete ggplot2 install.packages("ggplot2")

Puedes instalar varios paquetes a la vez si separas sus nombres con comas:

# Instalar múltiples paquetes install.packages(c("dplyr", "tidyr", "readr"))

7.3 Carga de un Paquete

Una vez que un paquete está instalado, debes cargarlo en tu sesión de R para usar sus funciones y datos. Esto se hace con la función library().

Ejemplo de Carga:

# Cargar el paquete ggplot2 library(ggplot2)

7.4 Verificar Paquetes Instalados

Para ver una lista de los paquetes que tienes instalados en tu sistema, puedes usar la función installed.packages():

# Mostrar los paquetes instalados installed.packages()

7.5 Actualización de Paquetes

Para actualizar un paquete a la última versión disponible, usa la función update.packages():

`# Actualizar todos los paquetes instalados update.packages()

# Actualizar un paquete específico update.packages(“ggplot2”)`

7.6 Eliminación de Paquetes

Para eliminar un paquete que ya no necesitas, utiliza la función remove.packages():

# Eliminar el paquete ggplot2 remove.packages("ggplot2")

8 Comandos en R

En este documento se presentan varios comandos útiles en R y se explican sus usos con ejemplos prácticos.

8.1 Contribuidores al Desarrollo de R

El comando `contributors()` muestra una lista de personas que han contribuido al desarrollo de R.

# Mostrar los contribuidores al desarrollo de R
contributors()

8.2 Citar un Paquete Específico

El comando citation() proporciona información sobre cómo citar R y sus paquetes en publicaciones. Para citar un paquete específico, puedes usar citation("nombre_del_paquete").

# Citar R
citation()
## 
## To cite R in publications use:
## 
##   R Core Team (2022). R: A language and environment for statistical
##   computing. R Foundation for Statistical Computing, Vienna, Austria.
##   URL https://www.R-project.org/.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {R: A Language and Environment for Statistical Computing},
##     author = {{R Core Team}},
##     organization = {R Foundation for Statistical Computing},
##     address = {Vienna, Austria},
##     year = {2022},
##     url = {https://www.R-project.org/},
##   }
## 
## We have invested a lot of time and effort in creating R, please cite it
## when using it for data analysis. See also 'citation("pkgname")' for
## citing R packages.
#Citar el paquete dplyr
citation("dplyr")
## 
## To cite package 'dplyr' in publications use:
## 
##   Wickham H, François R, Henry L, Müller K, Vaughan D (2023). _dplyr: A
##   Grammar of Data Manipulation_. R package version 1.1.4,
##   <https://CRAN.R-project.org/package=dplyr>.
## 
## A BibTeX entry for LaTeX users is
## 
##   @Manual{,
##     title = {dplyr: A Grammar of Data Manipulation},
##     author = {Hadley Wickham and Romain François and Lionel Henry and Kirill Müller and Davis Vaughan},
##     year = {2023},
##     note = {R package version 1.1.4},
##     url = {https://CRAN.R-project.org/package=dplyr},
##   }

8.3 Información de la Sesión de R

El comando sessionInfo() muestra información sobre la sesión actual de R, incluyendo versiones de paquetes, sistema operativo, y más.

# Mostrar información de la sesión de R
sessionInfo()
## R version 4.2.2 (2022-10-31 ucrt)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19045)
## 
## Matrix products: default
## 
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8  LC_CTYPE=Spanish_Colombia.utf8   
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C                     
## [5] LC_TIME=Spanish_Colombia.utf8    
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## loaded via a namespace (and not attached):
##  [1] digest_0.6.31     R6_2.5.1          lifecycle_1.0.4   jsonlite_1.8.8   
##  [5] evaluate_0.23     cachem_1.0.8      rlang_1.1.3       cli_3.6.2        
##  [9] rstudioapi_0.16.0 jquerylib_0.1.4   bslib_0.7.0       rmarkdown_2.26   
## [13] tools_4.2.2       xfun_0.43         yaml_2.3.8        fastmap_1.1.1    
## [17] compiler_4.2.2    htmltools_0.5.8.1 knitr_1.46        sass_0.4.9

8.4 Fecha y Hora del Sistema

Los comandos Sys.Date() y Sys.time() se utilizan para obtener la fecha y hora actual del sistema, respectivamente.

# Mostrar la fecha actual del sistema
Sys.Date()
## [1] "2024-08-12"
# Mostrar la hora actual del sistema
Sys.time()
## [1] "2024-08-12 23:33:06 -05"

8.5 Ayuda

help() y ? proporcionan ayuda sobre paquetes, funciones y objetos que estén instalados

# Ayuda sobre la función lm
  help(lm)
## starting httpd help server ... done
# Otra forma
  ?lm

Si el paquete o función no esta instalado se debe usar ??

8.6 Objetos del entorno de trabajo

ls lista todos los objetos del entorno de trabajo

ls()
## character(0)

8.7 Eliminar objetos del entorno de trabajo

rmelimina uno o más los objetos del entorno de trabajo

rm(base)

8.8 Coerción de Tipos y Clases de Variables

El comando as.numeric() puede usarse para convertir valores a tipo numérico. Aquí, lo usamos para convertir el valor de Sys.time() a un número. También se puede usar class() para verificar la clase de una variable.

# Convertir la hora actual a tipo numérico
as.numeric(Sys.time())
## [1] 1723523588
# Verificar la clase de la variable Sys.time()
class(Sys.time())
## [1] "POSIXct" "POSIXt"

8.9 Bases de Datos de Ejemplo Disponibles en R

El comando data() lista las bases de datos de ejemplo disponibles en R.

# Listar las bases de datos de ejemplo disponibles en R
data()

8.10 Cargar una Base de Datos de Ejemplo

Para cargar una base de datos de ejemplo, se puede usar el comando data(nombre_de_la_base_de_datos).

# Cargar una de las bases de datos de ejemplo de R
data(presidents)

presidents
##      Qtr1 Qtr2 Qtr3 Qtr4
## 1945   NA   87   82   75
## 1946   63   50   43   32
## 1947   35   60   54   55
## 1948   36   39   NA   NA
## 1949   69   57   57   51
## 1950   45   37   46   39
## 1951   36   24   32   23
## 1952   25   32   NA   32
## 1953   59   74   75   60
## 1954   71   61   71   57
## 1955   71   68   79   73
## 1956   76   71   67   75
## 1957   79   62   63   57
## 1958   60   49   48   52
## 1959   57   62   61   66
## 1960   71   62   61   57
## 1961   72   83   71   78
## 1962   79   71   62   74
## 1963   76   64   62   57
## 1964   80   73   69   69
## 1965   71   64   69   62
## 1966   63   46   56   44
## 1967   44   52   38   46
## 1968   36   49   35   44
## 1969   59   65   65   56
## 1970   66   53   61   52
## 1971   51   48   54   49
## 1972   49   61   NA   NA
## 1973   68   44   40   27
## 1974   28   25   24   24

9 Mis primeros pasos en el análisis bases de datos en R

9.1 Cargar la base de datos

Para cargar bases de datos, puedes usar diferentes comandos dependiendo del formato del archivo. A continuación, se presentan algunos ejemplos de cómo cargar datos desde archivos CSV y archivos de texto, así como desde archivos Excel.

9.1.1 Archivos CSV

Si tienes un archivo CSV, puedes usar la función `read.csv()` para cargar los datos. Aquí hay un ejemplo de cómo hacerlo:

`# Cargar un archivo CSV con valores separados por punto y coma df <- read.csv(“bd.csv”, header = TRUE, sep = “;”, dec = “.”)

# Cargar un archivo CSV con valores separados por tabulación df <- read.csv(“bd.txt”, header = TRUE, sep = “, dec =”.”)` |

  • header = TRUE: Indica que la primera fila del archivo contiene los nombres de las columnas.

  • sep = ";": Especifica el delimitador de los valores en el archivo. En este caso, el delimitador es el punto y coma.

  • sep = "\t": Especifica que el delimitador de los valores en el archivo es una tabulación (tab).

    dec = ".": Define el carácter utilizado para los decimales. Aquí, el punto es el separador decimal.

9.1.2 Archivos Excel

Para cargar datos desde un archivo Excel, puedes usar el paquete readxl y su función read_xlsx(). Primero, asegúrate de tener el paquete instalado y cargado:

`# Instalar el paquete readxl si aún no está instalado install.packages(“readxl”)

# Cargar el paquete readxl library(readxl)

# Cargar datos desde un archivo Excel df <- readxl::read_xlsx(“U:\Mi unidad\Analytics\df.xlsx”)`

  • readxl::read_xlsx(): Esta función lee archivos Excel con extensión .xlsx.

  • Ruta del archivo: Especifica la ruta completa al archivo Excel. Asegúrate de que la ruta sea correcta y que tengas permisos de lectura para el archivo.

9.1.3 Desde un proyecto

En la pantalla Files busque la base de datos, clic sobre ella e Import dataset

Adicionalmente, desde el menú File > Import Dataset >puede seleccionar diferentes métodos para cargar datos dependiendo de su formato

9.1.4 Desde el portapapeles

Para leer datos directamente desde el portapapeles del sistema operativo y cargarlos en un dataframe en R, se usa el siguiente código:

  1. Se tiene esta base de datos en Excel

  2. Se seleccionan las celdas y se copian ctrl + c

  3. Se corre el siguiente codigo en R

    datos <- read.table(file = "clipboard", header = TRUE, sep = "\t")
  • read.table(): es una función en R que se utiliza para leer datos en forma de tabla desde un archivo o una conexión. Por defecto, esta función lee archivos de texto donde los valores están separados por espacios o tabulaciones.

  • file = "clipboard": especifica la fuente de los datos que se van a leer. En este caso, "clipboard" indica que los datos deben ser leídos desde el portapapeles del sistema operativo.

    Usar "clipboard" es útil cuando has copiado datos tabulares desde otra aplicación (como Excel o un editor de texto) y quieres pegarlos directamente en R sin tener que guardarlos en un archivo primero.

  • header = TRUE: indica si el archivo de datos tiene una fila de encabezado (nombres de columnas) en la primera fila. Cuando se establece en TRUE, read.table() trata la primera fila del archivo como los nombres de las columnas del dataframe resultante.

  • sep = "\t": especifica el delimitador que se utiliza para separar los valores en los datos. En este caso, "\t" indica que el delimitador es una tabulación (tab), que es común en archivos tabulados.

    Esto significa que read.table() espera que los datos en el portapapeles estén separados por tabulaciones.

datos <- read.table(file = "clipboard", header = TRUE, sep = "\t")
## Warning in read.table(file = "clipboard", header = TRUE, sep = "\t"):
## incomplete final line found by readTableHeader on 'clipboard'
datos
## [1] X..objeto.X.no.encontrado
## <0 rows> (or 0-length row.names)

9.2 Describir la base de datos

Para esta sección se usara la base de datos de la prueba de realizada llamada df

9.2.1 Cargue de la base de datos

library(readxl)
## Warning: package 'readxl' was built under R version 4.2.3
df <- read_excel("Bases de datos/df.xlsx")
View(df)

9.2.2 Dimensión de la Base de Datos

Para obtener la dimensión de un dataframe, es decir, el número de filas y columnas, utiliza la función `dim()`:

# Dimensión de la base de datos (filas x columnas)
dim(df)
## [1] 200   4

dim(df): Devuelve un vector con el número de filas y el número de columnas del dataframe df.

Para el caso de la base de datos analizada, se tienen 200 filas y 4 columnas

9.2.3 Número de Columnas y Filas por separado

Para obtener el número de columnas y filas, puedes usar los siguientes comandos:

  • length(df): Devuelve el número de columnas en el dataframe df.

  • nrow(df): Devuelve el número de filas en el dataframe df.

# Número de columnas
length(df)
## [1] 4
# Número de filas
nrow(df)
## [1] 200

El primer resultado indica que la base de datos df cuenta con 4 columnas, el segundo indica el número de filas (200 registros)

9.2.4 Número de Elementos en una Variable

Para contar los elementos en una variable específica dentro del dataframe se debe agregar el nombre de la variable después de el nombre de la base de datos y el signo $

# Número de elementos en la variable 'ubicacion'
length(df$ubicacion)
## [1] 200
# Número de elementos en la variable 'distancia'
length(df$edad)
## [1] 200

Las variables analizadas cuentan con 200 registros cada una

9.2.5 Nombres de las Variables

Para obtener los nombres de las variables (columnas) en el dataframe:

# Nombres de las variables
names(df)
## [1] "candidatos" "ubicacion"  "edad"       "distancia"

names(df): Devuelve un vector con los nombres de las columnas del dataframe df.

Para el ejemplo, sabemos que la base contaba con 4 columnas o variables, y sus nombres son: candidatos, ubicación, edad y distancia.

9.2.6 Primeros y Últimos Registros de la Tabla

Para ver los primeros y últimos registros del dataframe:

  • head(df): Muestra las primeras 6 filas del dataframe.

  • head(df, 10): Muestra las primeras 10 filas.

  • tail(df): Muestra las últimas 6 filas.

  • tail(df, 4): Muestra las últimas 4 filas.

# Primeros registros de la tabla
head(df)
## # A tibble: 6 × 4
##   candidatos ubicacion      edad distancia
##   <chr>      <chr>         <dbl>     <dbl>
## 1 Putin      Hogwarts         51     25.1 
## 2 Putin      Macondo         104      8.62
## 3 Putin      Wakanda          70      4.15
## 4 Uribe      Ostania          58     15.0 
## 5 Uribe      Ciudad gotica    20     14.0 
## 6 Putin      Ostania          29      6.78
head(df, 10)  # Primeros 10 registros
## # A tibble: 10 × 4
##    candidatos ubicacion      edad distancia
##    <chr>      <chr>         <dbl>     <dbl>
##  1 Putin      Hogwarts         51     25.1 
##  2 Putin      Macondo         104      8.62
##  3 Putin      Wakanda          70      4.15
##  4 Uribe      Ostania          58     15.0 
##  5 Uribe      Ciudad gotica    20     14.0 
##  6 Putin      Ostania          29      6.78
##  7 Putin      Ciudad gotica    39     NA   
##  8 Uribe      Ostania          36      9.94
##  9 Putin      Ciudad gotica    62      7.96
## 10 Milei      Wakanda          26     13.6
# Últimos registros de la tabla
tail(df)
## # A tibble: 6 × 4
##   candidatos ubicacion  edad distancia
##   <chr>      <chr>     <dbl>     <dbl>
## 1 Uribe      Hogwarts     26      9.30
## 2 Putin      Hogwarts     40      9.56
## 3 Uribe      Macondo      66      9.11
## 4 Putin      Mordor       20     NA   
## 5 Uribe      Hogwarts     64      7.60
## 6 Milei      Macondo      58      8.42
tail(df, 4)   # Últimos 4 registros
## # A tibble: 4 × 4
##   candidatos ubicacion  edad distancia
##   <chr>      <chr>     <dbl>     <dbl>
## 1 Uribe      Macondo      66      9.11
## 2 Putin      Mordor       20     NA   
## 3 Uribe      Hogwarts     64      7.60
## 4 Milei      Macondo      58      8.42

9.2.7 Elementos Aleatorios de la Tabla

Para obtener una muestra aleatoria de registros del dataframe, utiliza la función some() del paquete car:

# Instalar librería car
#install.packages("car")

# Cargar la librería car
library(car)
## Warning: package 'car' was built under R version 4.2.3
## Loading required package: carData
## Warning: package 'carData' was built under R version 4.2.3
#DOS FORMAS DE EJECUTAR LA FUNCIÓN SOME

#A. Obtener una muestra aleatoria
some(df)
## # A tibble: 10 × 4
##    candidatos ubicacion      edad distancia
##    <chr>      <chr>         <dbl>     <dbl>
##  1 Uribe      Ciudad gotica    49     10.5 
##  2 Putin      Wakanda          14     16.0 
##  3 Putin      Ostania          72      7.59
##  4 Putin      Ciudad gotica     7     11.0 
##  5 Putin      Ciudad gotica    37     NA   
##  6 Putin      Ciudad gotica    84     18.8 
##  7 Putin      Wakanda          25     11.7 
##  8 Putin      Ciudad gotica    24      9.57
##  9 Uribe      Ostania          75      6.83
## 10 Uribe      Ostania          44      8.80
#B. Usar la función con el prefijo del paquete para evitar conflictos
car::some(df)
## # A tibble: 10 × 4
##    candidatos ubicacion      edad distancia
##    <chr>      <chr>         <dbl>     <dbl>
##  1 Putin      Wakanda          70      4.15
##  2 Putin      Ostania          29      6.78
##  3 Milei      Macondo          96     12.6 
##  4 Putin      Ciudad gotica    16      7.75
##  5 Milei      Wakanda          24      9.16
##  6 Uribe      Macondo          81      5.41
##  7 Uribe      Wakanda          47     14.9 
##  8 Putin      Macondo          62      4.68
##  9 Putin      Hogwarts         40      9.56
## 10 Milei      Macondo          58      8.42

9.2.8 Información sobre la Base de Datos

Para obtener una visión general de la estructura de los datos y el porcentaje de datos perdidos:

  • Abstract(df): Proporciona un resumen con el porcentaje de datos perdidos y otras estadísticas descriptivas.

  • str(df): Muestra la estructura interna del dataframe df, incluyendo el tipo de cada columna y una muestra de los datos.

# Información general sobre la base de datos
str(df)
## tibble [200 × 4] (S3: tbl_df/tbl/data.frame)
##  $ candidatos: chr [1:200] "Putin" "Putin" "Putin" "Uribe" ...
##  $ ubicacion : chr [1:200] "Hogwarts" "Macondo" "Wakanda" "Ostania" ...
##  $ edad      : num [1:200] 51 104 70 58 20 29 39 36 62 26 ...
##  $ distancia : num [1:200] 25.14 8.62 4.15 14.96 13.96 ...

La base de datos df contiene 200 columnas x 4 variables

Las variables son: candidatos, ubicación, edad y distancia.

Para el caso de candidatos, es una variable del tipo carácter y sus primeros registros toman los valores “Putin”,“Putin”,“Putin”,“Uribe”

Para el caso de la variable distancia, es una variable de tipo númerica, y cuenta con decimales.

# Instalar y cargar librería DescTools
#install.packages("DescTools")
library(DescTools)
## Warning: package 'DescTools' was built under R version 4.2.3
## 
## Attaching package: 'DescTools'
## The following object is masked from 'package:car':
## 
##     Recode
# Información general sobre la base de datos

Abstract(df)
## ------------------------------------------------------------------------------ 
## df
## 
## data frame:  200 obs. of  4 variables
##      193 complete cases (96.5%)
## 
##   Nr  ColName     Class      NAs       Levels
##   1   candidatos  character  .               
##   2   ubicacion   character  .               
##   3   edad        numeric    .               
##   4   distancia   numeric    7 (3.5%)

Las variables son: candidatos, ubicación, edad y distancia.

Para el caso de la variable distancia, es una variable de tipo númerica, y cuenta con decimales, se cuentan con 7 registros en NA (es decir, sin información) lo que corresponde al 3.5% de los 200 registros de la base de datos.

9.2.9 Clase de un objeto y de sus variables

Para verificar la clase de un dataframe y sus variables:

  • class(df): Devuelve la clase del objeto df (debería ser "data.frame").

  • class(df$candidatos), class(df$ubicacion), etc.: Devuelven la clase de las columnas específicas.

# Clase del dataframe 
class(df)
## [1] "tbl_df"     "tbl"        "data.frame"
# Clase de las  variables
class(df$candidatos)
## [1] "character"
class(df$ubicacion)
## [1] "character"
class(df$distancia)
## [1] "numeric"
class(df$edad)
## [1] "numeric"

9.2.10 Valores únicos de las variables

Para obtener los valores únicos en una variable discreta, es decir, los valores o categorías que toma:

unique(df$ubicacion): Devuelve los valores únicos en la columna ubicacion.

# Valores únicos en las variables
unique(df$ubicacion)
## [1] "Hogwarts"      "Macondo"       "Wakanda"       "Ostania"      
## [5] "Ciudad gotica" "Mordor"
unique(df$candidatos)
## [1] "Putin" "Uribe" "Milei"
##unique(df$distancia)
unique(df$edad)
##  [1]  51 104  70  58  20  29  39  36  62  26 107  89  83  12  49  41  66  57  56
## [20]  96  82  27   2   4  34  60   3  75  91  74  47  10  24  37  55  16  53   1
## [39]  32  69  46  42  22  23  15  63  76  61  73  50  80  44  14   8  45  30  33
## [58]  59  38   6  72  71   7  97 102  67   5  40  93  64   9  81  84  21  43  28
## [77]  90  78  25  88 100 127  65 117

9.2.11 Resumen de estadísticas y frecuencias

Para obtener un resumen de estadísticas descriptivas y frecuencias:

  • summary(df): Proporciona un resumen estadístico básico de cada columna del dataframe.

  • Desc(df): Ofrece un resumen detallado y gráficos para cada variable en el dataframe.

  • dfSummary(df): Resumen detallado con estadísticas y gráficos usando el paquete summarytools.

# Resumen de estadísticas descriptivas
summary(df)
##   candidatos         ubicacion              edad          distancia     
##  Length:200         Length:200         Min.   :  1.00   Min.   : 3.167  
##  Class :character   Class :character   1st Qu.: 24.00   1st Qu.: 7.887  
##  Mode  :character   Mode  :character   Median : 42.50   Median : 9.567  
##                                        Mean   : 45.37   Mean   : 9.949  
##                                        3rd Qu.: 63.00   3rd Qu.:11.603  
##                                        Max.   :127.00   Max.   :25.138  
##                                                         NA's   :7

Describe las variables númericas de la base de datos, por ejemplo, la edad mínima de los encuestados es 1 año mientras que la máxima es de 127 años, con una mediana de 42.5 años y una media de 45.37 años.

Para el caso de las variables candidatos y ubicación, como estas son caracteres solo muestra que tienen 200 registros.

¿Qué pasa si se convierten las variables caracter a factor?

#Convertir una variable a factor
df$ubicacion <- as.factor(df$ubicacion) 
df$candidatos <- as.factor(df$candidatos) 

# Resumen de estadísticas descriptivas
summary(df)
##  candidatos         ubicacion       edad          distancia     
##  Milei:50   Ciudad gotica:37   Min.   :  1.00   Min.   : 3.167  
##  Putin:80   Hogwarts     :31   1st Qu.: 24.00   1st Qu.: 7.887  
##  Uribe:70   Macondo      :39   Median : 42.50   Median : 9.567  
##             Mordor       :26   Mean   : 45.37   Mean   : 9.949  
##             Ostania      :39   3rd Qu.: 63.00   3rd Qu.:11.603  
##             Wakanda      :28   Max.   :127.00   Max.   :25.138  
##                                                 NA's   :7

Al convertir a factor, es posible analizar las variables candidatos y ubicación. Ahora el comando summary muestra las frecuencias de cada una de las alternativas que toma cada variable.

# Resumen de frecuencias y gráficos
library(DescTools)
Desc(df)
## ------------------------------------------------------------------------------ 
## Describe df (tbl_df, tbl, data.frame):
## 
## data frame:  200 obs. of  4 variables
##      193 complete cases (96.5%)
## 
##   Nr  ColName     Class    NAs       Levels                             
##   1   candidatos  factor   .         (3): 1-Milei, 2-Putin, 3-Uribe     
##   2   ubicacion   factor   .         (6): 1-Ciudad gotica, 2-Hogwarts,  
##                                      3-Macondo, 4-Mordor, 5-Ostania, ...
##   3   edad        numeric  .                                            
##   4   distancia   numeric  7 (3.5%)                                     
## 
## 
## ------------------------------------------------------------------------------ 
## 1 - candidatos (factor)
## 
##   length      n    NAs unique levels  dupes
##      200    200      0      3      3      y
##          100.0%   0.0%                     
## 
##    level  freq   perc  cumfreq  cumperc
## 1  Putin    80  40.0%       80    40.0%
## 2  Uribe    70  35.0%      150    75.0%
## 3  Milei    50  25.0%      200   100.0%

## ------------------------------------------------------------------------------ 
## 2 - ubicacion (factor)
## 
##   length      n    NAs unique levels  dupes
##      200    200      0      6      6      y
##          100.0%   0.0%                     
## 
##            level  freq   perc  cumfreq  cumperc
## 1        Macondo    39  19.5%       39    19.5%
## 2        Ostania    39  19.5%       78    39.0%
## 3  Ciudad gotica    37  18.5%      115    57.5%
## 4       Hogwarts    31  15.5%      146    73.0%
## 5        Wakanda    28  14.0%      174    87.0%
## 6         Mordor    26  13.0%      200   100.0%

## ------------------------------------------------------------------------------ 
## 3 - edad (numeric)
## 
##   length       n    NAs  unique     0s   mean  meanCI'
##      200     200      0      84      0  45.37   41.64
##           100.0%   0.0%           0.0%          49.10
##                                                      
##      .05     .10    .25  median    .75    .90     .95
##     5.00   11.80  24.00   42.50  63.00  83.00   93.15
##                                                      
##    range      sd  vcoef     mad    IQR   skew    kurt
##   126.00   26.78   0.59   28.91  39.00   0.43   -0.35
##                                                      
## lowest : 1.0 (3), 2.0 (2), 3.0 (2), 4.0 (2), 5.0 (2)
## highest: 102.0 (2), 104.0, 107.0, 117.0, 127.0
## 
## ' 95%-CI (classic)

## ------------------------------------------------------------------------------ 
## 4 - distancia (numeric)
## 
##      length         n       NAs    unique         0s       mean     meanCI'
##         200       193         7       = n          0   9.949046   9.494072
##                 96.5%      3.5%                 0.0%             10.404021
##                                                                           
##         .05       .10       .25    median        .75        .90        .95
##    5.428575  6.481631  7.887011  9.566809  11.602988  14.310540  15.666749
##                                                                           
##       range        sd     vcoef       mad        IQR       skew       kurt
##   21.970785  3.204581  0.322099  2.687759   3.715977   1.000008   2.337304
##                                                                           
## lowest : 3.166745, 3.97088, 3.987963, 4.146889, 4.422825
## highest: 16.822945, 18.169112, 18.775825, 20.68755, 25.13753
## 
## ' 95%-CI (classic)

Desc(df): Ofrece un resumen detallado y gráficos para cada variable en el dataframe.

# Otros paquetes de frecuencias
#install.packages("summarytools")
library(summarytools)
## Warning: package 'summarytools' was built under R version 4.2.3
dfSummary(df, 
          varnumbers = FALSE, 
          valid.col = FALSE, 
          graph.magnif = 0.76)
## Data Frame Summary  
## df  
## Dimensions: 200 x 4  
## Duplicates: 0  
## 
## --------------------------------------------------------------------------------------------
## Variable     Stats / Values            Freqs (% of Valid)    Graph                 Missing  
## ------------ ------------------------- --------------------- --------------------- ---------
## candidatos   1. Milei                  50 (25.0%)            IIIII                 0        
## [factor]     2. Putin                  80 (40.0%)            IIIIIIII              (0.0%)   
##              3. Uribe                  70 (35.0%)            IIIIIII                        
## 
## ubicacion    1. Ciudad gotica          37 (18.5%)            III                   0        
## [factor]     2. Hogwarts               31 (15.5%)            III                   (0.0%)   
##              3. Macondo                39 (19.5%)            III                            
##              4. Mordor                 26 (13.0%)            II                             
##              5. Ostania                39 (19.5%)            III                            
##              6. Wakanda                28 (14.0%)            II                             
## 
## edad         Mean (sd) : 45.4 (26.8)   84 distinct values      .   :               0        
## [numeric]    min < med < max:                                  : : : :             (0.0%)   
##              1 < 42.5 < 127                                  : : : : : .                    
##              IQR (CV) : 39 (0.6)                             : : : : : : :                  
##                                                              : : : : : : : : . .            
## 
## distancia    Mean (sd) : 9.9 (3.2)     193 distinct values       :                 7        
## [numeric]    min < med < max:                                    : :               (3.5%)   
##              3.2 < 9.6 < 25.1                                  . : :                        
##              IQR (CV) : 3.7 (0.3)                              : : : . .                    
##                                                              . : : : : :                    
## --------------------------------------------------------------------------------------------

dfSummary(df): Resumen detallado con estadísticas y gráficos usando el paquete summarytools.

Para generar y visualizar el resumen anterior de summarytools en formato HTML:

htmltools::html_print(html_summary): Imprime el resumen en formato HTML

# Generar resumen en HTML
resumen <- dfSummary(df, 
                    varnumbers = FALSE, 
                    valid.col = FALSE, 
                    graph.magnif = 0.76)
html_summary <- print(resumen, method = "render")
htmltools::html_print(html_summary)

gt_plt_summary(df, title = "Resumen de la base de datos"): Crea una tabla resumen con un título utilizando el paquete gtExtras.

# Instalar y cargar gtExtras
#install.packages("gtExtras")
library(gtExtras)
## Loading required package: gt
## Warning: package 'gt' was built under R version 4.2.3
# Generar una tabla resumen
gt_plt_summary(df, title = "Resumen de la base de datos")
## Warning in geom_point(data = NULL, aes(x = rng_vals[1], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 200 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_point(data = NULL, aes(x = rng_vals[2], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 200 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_point(data = NULL, aes(x = rng_vals[1], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 193 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
## Warning in geom_point(data = NULL, aes(x = rng_vals[2], y = 1), color = "transparent", : All aesthetics have length 1, but the data has 193 rows.
## ℹ Please consider using `annotate()` or provide this layer with data containing
##   a single row.
Resumen de la base de datos
200 rows x 4 cols
Column Plot Overview Missing Mean Median SD
candidatos Putin, Uribe and Milei
3 categories 0.0%
ubicacion Macondo, Ostania, Ciudad gotica, Hogwarts, Wakanda and Mordor
6 categories 0.0%
edad 1127 0.0% 45.4 42.5 26.8
distancia 325 3.5% 9.9 9.6 3.2

9.3 Creación de variables

x <- 5: Crea una variable x y le asigna el valor 5.

x <- 5

rm(x): Elimina la variable x del entorno de trabajo.

rm(x)

x = 5: Asigna el valor 5 a x. Es una forma alternativa de asignación, aunque en R es preferible usar <-

x = 5

9.3.1 ¡Ojo! Diferencia entre operadores de Asignación y Comparación

# ¿Es lo mismo?
x <  -5
## [1] FALSE

Este código no tiene un significado lógico claro debido al espacio entre < y -5. En R, x < -5 compararía si x es menor que -5. El espacio genera confusión, por lo que no se debería usar así.

9.3.2 Sensibilidad a Mayúsculas y Minúsculas

# Qué pasa aquí
x <- 100
#X
#R distingue entre mayúsculas y minúsculas
#! objeto 'X' no encontrado
x
## [1] 100

R es sensible a las mayúsculas y minúsculas. x y X se consideran variables diferentes.

9.4 Diferentes Formas de Crear un Vector

weight <- c(10,20,30,40)
assign ("weight1", c(10,20,30,40)) #Otra forma de crear un vector
weight = c(10,20,30,40)
  • weight <- c(10,20,30,40): Crea un vector llamado weight.

  • assign("weight1", c(10,20,30,40)): Otra forma de crear un vector, usando la función assign.

  • weight = c(10,20,30,40): Otra forma de asignar valores a un vector, similar al uso de <-.

9.5 Incremento de Variables

# ¿Qué pasa aquí?
n <- 1        #1
n <- n + 1    #1 + 1 
n             #2
## [1] 2
  • n <- 1: Asigna 1 a n.

  • n <- n + 1: Incrementa n en 1, por lo que n ahora es 2.

  • n: Muestra el valor actual de n, que es 2.

9.6 Redondeo de Números

floor(5.7) ### Aproxima hacia abajo
## [1] 5
ceiling(5.7)  ###Aproxima hacia arriba
## [1] 6
round(5.777, digits = 2)
## [1] 5.78
round(5.772, digits = 2)
## [1] 5.77
round(5.772154513610, digits = 5)
## [1] 5.77215
  • floor(5.7): Aproxima 5.7 hacia abajo (devuelve 5).

  • ceiling(5.7): Aproxima 5.7 hacia arriba (devuelve 6).

  • round(5.777, digits = 2): Redondea a 2 decimales (devuelve 5.78).

  • round(5.772154513610, digits = 5): Redondea a 5 decimales (devuelve 5.77215).

9.7 Creación de Secuencias

# Cómo crear secuencias de números
seq(from = 1, to = 9, by = 2) ###Secuencia desde 1 hasta 9 cada 2###
## [1] 1 3 5 7 9
#¿Cómo lo asignaría al vector llamado "Ho"?
Ho = seq(from = 1, to = 9, by = 2)
Ho <- seq(from = 1, to = 9, by = 2)
  • seq(from = 1, to = 9, by = 2): Genera una secuencia desde 1 hasta 9, en incrementos de 2.

  • Ho <- seq(from = 1, to = 9, by = 2): Asigna la secuencia generada al vector Ho.

9.7.1 Otras Formas de Generar Secuencias

10:18 
## [1] 10 11 12 13 14 15 16 17 18
18:10
## [1] 18 17 16 15 14 13 12 11 10
-0.5:8.5
##  [1] -0.5  0.5  1.5  2.5  3.5  4.5  5.5  6.5  7.5  8.5
seq(0, 1.5, 0.2)
## [1] 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4
seq(1.5, 0, -0.2)
## [1] 1.5 1.3 1.1 0.9 0.7 0.5 0.3 0.1
  • 10:18: Genera una secuencia del 10 al 18.

  • 18:10: Genera una secuencia inversa del 18 al 10.

  • -0.5:8.5: Genera una secuencia de -0.5 a 8.5.

  • seq(0, 1.5, 0.2): Genera una secuencia de 0 a 1.5 en incrementos de 0.2.

  • seq(1.5, 0, -0.2): Genera una secuencia de 1.5 a 0 en decrementos de 0.2.

9.8 Secuencias con la Función sequence

sequence(5)  ###Números de 1 a 5###
## [1] 1 2 3 4 5
sequence(5:1) ###Números de 1 a 5, luego de 1 a 4, luego de 1 a 3, de 1 a 2, y finalmente 1###
##  [1] 1 2 3 4 5 1 2 3 4 1 2 3 1 2 1
sequence(c(5,2,4)) ###Secuencia de 1 a 5, de 1 a 2, de 1 a 4###
##  [1] 1 2 3 4 5 1 2 1 2 3 4
  • sequence(5): Genera una secuencia de 1 a 5.

  • sequence(5:1): Genera secuencias desde 1 hasta 5, luego 1 hasta 4, y así sucesivamente.

  • sequence(c(5,2,4)): Genera secuencias de 1 a 5, de 1 a 2, y de 1 a 4.

9.9 Repeticiones

rep(9,5)
## [1] 9 9 9 9 9
rep(1:4, 2) ### Repetir 1,2,3,4 dos veces ###
## [1] 1 2 3 4 1 2 3 4
rep(1:4, each = 2) ###Repite 1 dos veces, 2 dos veces,...###
## [1] 1 1 2 2 3 3 4 4
rep(1:4, each = 2, times = 3)
##  [1] 1 1 2 2 3 3 4 4 1 1 2 2 3 3 4 4 1 1 2 2 3 3 4 4
rep(1:4, 1:4) ### Repite 1 una vez, 2 dos veces...###
##  [1] 1 2 2 3 3 3 4 4 4 4
rep(1:4, c(4,1,5,2)) ###Repite 1 cuatro veces, 2 una vez...###
##  [1] 1 1 1 1 2 3 3 3 3 3 4 4
  • rep(9,5): Repite el 9 cinco veces.

  • rep(1:4, 2): Repite la secuencia 1,2,3,4 dos veces.

  • rep(1:4, each = 2): Repite cada número dos veces.

  • rep(1:4, 1:4): Repite el 1 una vez, el 2 dos veces, y así sucesivamente.

9.10 Vectores y Acceso a Elementos

a <- c(1, 2, 5, 3, 6, -2, 4)
b <- c("one", "two", "three")
c <- c(TRUE, TRUE, TRUE, FALSE, TRUE, FALSE)

typeof(a); typeof(b); typeof(c) ###Tipo de los elementos que componen cada vector###
## [1] "double"
## [1] "character"
## [1] "logical"
q <- 10:16; q
## [1] 10 11 12 13 14 15 16
q[3]   ###Elemento 3 del vector q###
## [1] 12
q[c(2,4,7)]  ###Elementos 2,4,7 del vector q###
## [1] 11 13 16
y <- c(10, 11, 12, 13, 14, 15, 16)
y[2:5]   ###Elementos 2 al 5 del vector y###
## [1] 11 12 13 14
y[-3]    ###Elimina el tercer elemento del vector y###
## [1] 10 11 13 14 15 16
r <- 4.3
r[2]
## [1] NA
z <- r[-1]
length(z)
## [1] 0
  • typeof(a), typeof(b), typeof(c): Muestra el tipo de los elementos en los vectores a, b, y c.

  • q[3]: Accede al tercer elemento del vector q.

  • y[-3]: Elimina el tercer elemento del vector y.

9.11 Matrices

y <- matrix(1:20, nrow = 5); y  ###Matriz elementos 1 a 20 con 5 filas,se llena por columnas###
##      [,1] [,2] [,3] [,4]
## [1,]    1    6   11   16
## [2,]    2    7   12   17
## [3,]    3    8   13   18
## [4,]    4    9   14   19
## [5,]    5   10   15   20
y <- matrix(1:20, nrow = 5, byrow = TRUE); y  ###Matriz elementos 1 a 20 con 5 filas,se llena por filas###
##      [,1] [,2] [,3] [,4]
## [1,]    1    2    3    4
## [2,]    5    6    7    8
## [3,]    9   10   11   12
## [4,]   13   14   15   16
## [5,]   17   18   19   20
cells <- c(1, 26, 24, 68)
rnames <- c("R1", "R2"); cnames <- c("C1", "C2") ###Vector nombres filas, vector nombres columnas###
mymatrix <- matrix(cells, nrow = 2, ncol = 2, byrow = TRUE,  ###Crea una matriz con los elementos del vector cells por filas, con 2 filas, 2 columnas, y asigna nombres###
                   dimnames = list(rnames, cnames))
mymatrix
##    C1 C2
## R1  1 26
## R2 24 68
mymatrix <- matrix(cells, nrow = 2, ncol = 2, byrow = FALSE, ###Crea una matriz con los elementos del vector cells por columnas, con 2 filas, 2 columnas, y asigna nombres###
                   dimnames = list(rnames, cnames))
mymatrix
##    C1 C2
## R1  1 24
## R2 26 68
  • y <- matrix(1:20, nrow = 5): Crea una matriz con elementos de 1 a 20 en 5 filas, llenando por columnas.

  • mymatrix: Crea una matriz de 2x2 usando el vector cells y asigna nombres a las filas y columnas.

9.12 Lectura de Datos desde la Consola

y <- scan() ###Read data into a vector or list from the console or file. Pide introducir los datos###
  • scan(): Permite leer datos desde la consola o un archivo directamente a un vector o lista.

9.13 Creación de Secuencias y Acceso a Elementos

(x <- seq(1, 20, by = 2))
##  [1]  1  3  5  7  9 11 13 15 17 19
(y <- rep(3, 4))  ###Repetir 4 veces el número 3###
## [1] 3 3 3 3
(z <- c(y, x))   ###Unión de dos vectores###
##  [1]  3  3  3  3  1  3  5  7  9 11 13 15 17 19
(x <- 100:110)  ###Crea un vector desde 100 hasta 110###
##  [1] 100 101 102 103 104 105 106 107 108 109 110
i <- c(1, 3, 2) ###Vector###
x[i]            ###Trae las posiciones creadas en el vector  i del vector x###
## [1] 100 102 101
j <- c(-1, -2, -3)
x[j]            ###Elimina las posiciones creadas en el vector j del vector x###
## [1] 103 104 105 106 107 108 109 110
  • seq(1, 20, by = 2): Genera una secuencia desde 1 hasta 20, en incrementos de 2.

  • rep(3, 4): Repite el número 3 cuatro veces.

  • x[i]: Accede a las posiciones indicadas por i en el vector x.

  • x[j]: Elimina las posiciones indicadas por j en el vector x.

LS0tDQp0aXRsZTogIlByaW1lcm9zIHBhc29zIGVuIFIiDQphdXRob3I6ICJKdWxpZXRoIFpvcnJvIE1lbG8iDQpkYXRlOiAiMjAyNC0wNy0zMCINCm91dHB1dDogDQojaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duL2h0bWwtZG9jdW1lbnQuaHRtbCAjVHV0b3JpYWwgYsOhc2ljbw0KICBodG1sX2RvY3VtZW50Og0KICAgIHRvYzogdHJ1ZSAgICAgICAgICAgICAgICAgI1RhYmxhIGRlIGNvbnRlbmlkbw0KICAgIHRvY19kZXB0aDogMiAgICAgICAgICAgICAgI05pdmVsIGRlIHByb2Z1bmRpZGFkIGRlIGxhIHRhYmxhIGRlIGNvbnRlbmlkbw0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgICAgI051bWVyYXIgc2VjY2lvbmVzDQogICAgDQogICAgdG9jX2Zsb2F0OiAgICAgICAgICAgICAgICAjVGFibGEgZGUgY29udGVuaWRvIGZsb3RhbnRlDQogICAgICBjb2xsYXBzZWQ6IHRydWUgICAgICAgICAgI1RhYmxhIGRlIGNvbnRlbmlkbyBjb2xhcHNhZGENCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlICAgICAgI0Rlc3BsYXphbWllbnRvIHN1YXZlICANCiAgICAgIHRvY190aXRsZTogIkNvbnRlbmlkbyIgICAgI1TDrXR1bG8gZGUgbGEgdGFibGEgZGUgY29udGVuaWRvICAgIA0KICAgIA0KDQogICAgdGhlbWU6IGpvdXJuYWwgICAgICAgICAgICAjVGVtYSBkZSBsYSBwcmVzZW50YWNpw7NuICAjaHR0cHM6Ly9ib290c3dhdGNoLmNvbS8zL2pvdXJuYWwvDQogICAgaGlnaGxpZ2h0OiBrYXRlICAgICAgICAgICAjUmVzYWx0YWRvIGRlIGxhIHNpbnRheGlzICAgICBodHRwczovL2VsYXN0aWMtbG92ZWxhY2UtMTU1ODQ4Lm5ldGxpZnkuYXBwL2hpZ2hsaWdodGVycy5odG1sDQogICAgY29kZV9mb2xkaW5nOiBzaG93ICAgICAgICAjUGxlZ2FkbyBkZSBjw7NkaWdvDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZSAgICAgICAjRGVzY2FyZ2EgZGUgY8OzZGlnbw0KLS0tDQoNCipPYmpldGl2bzoqIEVuIGVzdGUgZG9jdW1lbnRvIHNlIHByZXNlbnRhbiBsb3MgcHJpbWVyb3MgcGFzb3MgZW4gUiwgZGVzZGUgbGEgaW5zdGFsYWNpw7NuIGRlbCBwcm9ncmFtYSB5IGNyZWFjacOzbiBkZSBwcm95ZWN0b3MsIGhhc3RhIGxhIGxlY3R1cmEgeSBwcmltZXJvcyBwYXNvcyBkZSBhbsOhbGlzaXMgZGUgYmFzZXMgZGUgZGF0b3MuDQoNCkFkZW3DoXMsIGRlIGxhIGNyZWFjacOzbiBkZSBvYmpldG9zLCBsYSBjcmVhY2nDs24gZGUgdmVjdG9yZXMsIGxhIGFzaWduYWNpw7NuIGRlIHZhcmlhYmxlcywgbGEgY3JlYWNpw7NuIGRlIHNlY3VlbmNpYXMgZGUgbsO6bWVyb3MsIGxhIHJlZG9uZGVhciBuw7ptZXJvcywgZW50cmUgb3Ryb3MuDQoNCiMgSW5zdGFsYWNpw7NuIGRlIFIgeSBSU3R1ZGlvDQoNCkxhIGluc3RhbGFjacOzbiBkZSBSIHkgUlN0dWRpbyBzZSBwdWVkZSBoYWNlciBkZXNkZSBsYSBww6FnaW5hIHdlYiBbcG9zaXQuY29tXShodHRwczovL3Bvc2l0LmNvL2Rvd25sb2FkL3JzdHVkaW8tZGVza3RvcC8pLiBQYXJhIGVzdG86DQoNCiFbXShpbWFnZXMvY2xpcGJvYXJkLTM5MDY3NjMwMjgucG5nKXt3aWR0aD0iNjI2In0NCg0KMS4gICoqRGVzY2FyZ3VlIGUgaW5zdGFsZSBSIGRlc2RlIGVsIGxpbmsgPGh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbS8+LCBhcXXDrSBlbmNvbnRyYXJhIGVsIHBhc28gYSBwYXNvOioqDQoNCiAgICAqKipQYXJhIFdpbmRvd3MqKioNCg0KICAgIGkpICBIYXogY2xpYyBlbiBlbCBlbmxhY2UgwqsqRG93bmxvYWQgUiBmb3IgV2luZG93c8K7Ki4NCiAgICBpaSkgRW4gbGEgc2lndWllbnRlIHDDoWdpbmEsIGhheiBjbGljIGVuIMKrKmJhc2UqwrsuDQogICAgaWlpKSBEZXNjYXJnYSBlbCBpbnN0YWxhZG9yIGhhY2llbmRvIGNsaWMgZW4gZWwgZW5sYWNlIHF1ZSBkaWNlIFwqXCpEb3dubG9hZCBSIFguWC5YIGZvciBXaW5kb3dzXCpcKiAoZG9uZGUgXGBYLlguWFxgIGVzIGVsIG7Dum1lcm8gZGUgbGEgdmVyc2nDs24gbcOhcyByZWNpZW50ZSkuDQogICAgaXYpIFVuYSB2ZXogZGVzY2FyZ2FkbywgZWplY3V0YSBlbCBhcmNoaXZvIGRlIGluc3RhbGFjacOzbi4NCiAgICB2KSAgU2lndWUgbGFzIGluc3RydWNjaW9uZXMgZGVsIGFzaXN0ZW50ZSBkZSBpbnN0YWxhY2nDs24sIGFjZXB0YW5kbyBsb3MgdMOpcm1pbm9zIGRlIGxhIGxpY2VuY2lhIHkgc2VsZWNjaW9uYW5kbyBsYXMgb3BjaW9uZXMgcHJlZGV0ZXJtaW5hZGFzLg0KDQogICAgIVtdKGltYWdlcy9jaHJvbWVfUnhORTZ0TmF2Ui0wMS5naWYpDQoNCiAgICAqKipQYXJhIE1hYyoqKg0KDQogICAgaSkgIEhheiBjbGljIGVuIGVsIGVubGFjZSDCqypEb3dubG9hZCBSIGZvciBtYWNPUyrCuw0KDQogICAgaWkpIERlc2NhcmdhIGVsIGluc3RhbGFkb3IgaGFjaWVuZG8gY2xpYyBlbiBlbCBlbmxhY2UgcXVlIGRpY2UgXCpcKlItWC5YLlgucGtnXCpcKiAoZG9uZGUgXGBYLlguWFxgIGVzIGVsIG7Dum1lcm8gZGUgbGEgdmVyc2nDs24gbcOhcyByZWNpZW50ZSkuDQoNCiAgICBpaWkpIFVuYSB2ZXogZGVzY2FyZ2FkbywgYWJyZSBlbCBhcmNoaXZvIFxgLnBrZ1xgLg0KDQogICAgaXYpIFNpZ3VlIGxhcyBpbnN0cnVjY2lvbmVzIGRlbCBhc2lzdGVudGUgZGUgaW5zdGFsYWNpw7NuLCBhY2VwdGFuZG8gbG9zIHTDqXJtaW5vcyBkZSBsYSBsaWNlbmNpYSB5IHNlbGVjY2lvbmFuZG8gbGFzIG9wY2lvbmVzIHByZWRldGVybWluYWRhcy4NCg0KPCEtLSAtLT4NCg0KPCEtLSAtLT4NCg0KMi4gICoqQWJyZSB0dSBuYXZlZ2Fkb3Igd2ViIHkgdmlzaXRhIGxhIHDDoWdpbmEgb2ZpY2lhbCBkZSBSU3R1ZGlvOiA8aHR0cHM6Ly9wb3NpdC5jby9kb3dubG9hZC9yc3R1ZGlvLWRlc2t0b3AvPioqDQoNCiAgICAqKipQYXJhIFdpbmRvd3MqKioNCg0KICAgIGkuICBIYXogY2xpYyBlbiDCqypQcm9kdWN0cyrCuyB5IHNlbGVjY2lvbmEgwqsqUlN0dWRpbyrCuy4NCg0KICAgIGlpLiBFbiBsYSBww6FnaW5hIGRlIFJTdHVkaW8sIGhheiBjbGljIGVuIMKrKkRvd25sb2FkIFJTdHVkaW8qwrsuDQoNCiAgICBpaWkuIFNlbGVjY2lvbmEgwqsqUlN0dWRpbyBEZXNrdG9wKsK7IHkgaGF6IGNsaWMgZW4gwqsqRG93bmxvYWQgUlN0dWRpbyBmb3IgV2luZG93cyrCuy4NCg0KICAgIGl2LiBEZXNjYXJnYSBlbCBpbnN0YWxhZG9yIHkgZWplY3V0YSBlbCBhcmNoaXZvIGRlc2NhcmdhZG8uDQoNCiAgICB2LiAgU2lndWUgbGFzIGluc3RydWNjaW9uZXMgZGVsIGFzaXN0ZW50ZSBkZSBpbnN0YWxhY2nDs24sIGFjZXB0YW5kbyBsb3MgdMOpcm1pbm9zIGRlIGxhIGxpY2VuY2lhIHkgc2VsZWNjaW9uYW5kbyBsYXMgb3BjaW9uZXMgcHJlZGV0ZXJtaW5hZGFzLg0KDQogICAgKioqUGFyYSBNYWMqKioNCg0KICAgIGkuICBIYXogY2xpYyBlbiDCqypQcm9kdWN0cyrCuyB5IHNlbGVjY2lvbmEgwqsqUlN0dWRpbyrCuy4NCiAgICBpaS4gRW4gbGEgcMOhZ2luYSBkZSBSU3R1ZGlvLCBoYXogY2xpYyBlbiDCqypEb3dubG9hZCBSU3R1ZGlvKsK7Lg0KICAgIGlpaS4gU2VsZWNjaW9uYSDCqypSU3R1ZGlvIERlc2t0b3AqwrsgeSBoYXogY2xpYyBlbiDCqypEb3dubG9hZCBSU3R1ZGlvIGZvciBtYWNPUyrCuy4NCiAgICBpdi4gRGVzY2FyZ2EgZWwgaW5zdGFsYWRvciB5IGFicmUgZWwgYXJjaGl2byBkZXNjYXJnYWRvLg0KICAgIHYuICBBcnJhc3RyYSBlbCBpY29ubyBkZSBSU3R1ZGlvIGEgbGEgY2FycGV0YSDCqypBcGxpY2FjaW9uZXMqwrsgcGFyYSBjb21wbGV0YXIgbGEgaW5zdGFsYWNpw7NuLg0KDQohW10oaW1hZ2VzL2Nocm9tZV9sN3doQWF2Vmc2LmdpZil7d2lkdGg9IjYxNSJ9DQoNCiMgQ3JlYWNpw7NuIGN1ZW50YSBkZSBHaXRIdWINCg0KMS4gICoqUmVnaXN0cmFyc2UgZW4gR2l0SHViOioqDQoNCiAgICBpLiAgQWJyZSB0dSBuYXZlZ2Fkb3Igd2ViIHkgdmlzaXRhIGxhIHDDoWdpbmEgZGUgcmVnaXN0cm8gZGUgR2l0SHViOiA8aHR0cHM6Ly9naXRodWIuY29tL2pvaW4+DQoNCiAgICBpaS4gSGF6IGNsaWMgZW4gwqsqU2luZyBpbirCuy4NCg0KICAgICAgICAhW10oaW1hZ2VzL2NsaXBib2FyZC05MDU5NDc0MTMucG5nKXt3aWR0aD0iNTEyIn0NCg0KICAgIGlpaS4gU2lndWUgbGFzIGluc3RydWNjaW9uZXMgcGFyYSBjcmVhciB0dSBjdWVudGEgY29uIHR1IGRpcmVjY2nDs24gZGUgY29ycmVvIGVsZWN0csOzbmljbyBhY2Fkw6ltaWNvDQoNCiAgICBpdi4gVmVyaWZpY2EgdHUgZGlyZWNjacOzbiBkZSBjb3JyZW8gZWxlY3Ryw7NuaWNvLg0KDQoyLiAgKipTb2xpY2l0YXIgbGEgY3VlbnRhIGRlIEdpdEh1YiBwYXJhIEVzdHVkaWFudGVzOioqDQoNCiAgICBpLiAgQWJyZSB0dSBuYXZlZ2Fkb3Igd2ViIHkgdmlzaXRhIGxhIHDDoWdpbmEgZGUgR2l0SHViIEVkdWNhdGlvbjogPGh0dHBzOi8vZWR1Y2F0aW9uLmdpdGh1Yi5jb20vZGlzY291bnRfcmVxdWVzdHMvbmV3Pg0KDQogICAgaWkuIEhheiBjbGljIGVuIMKrKkdldCBiZW5lZml0c8K7KiBiYWpvIGxhIHNlY2Npw7NuIMKrKlN0dWRlbnQqwrsuDQoNCiAgICBpaWkuIENvbXBsZXRhIGVsIGZvcm11bGFyaW8gY29uIHR1IGluZm9ybWFjacOzbiBwZXJzb25hbCB5IGFjYWTDqW1pY2EuIEVzIHBvc2libGUgcXVlIG5lY2VzaXRlcyBwcm9wb3JjaW9uYXIgdW5hIHBydWViYSBkZSB0dSBlc3RhdHVzIGRlIGVzdHVkaWFudGUsIGNvbW8gdHUgY2FybmV0IG8gZWwgY2VydGlmaWNhZG8gZGUgbWF0cmljdWxhLg0KDQozLiAgKipFbnbDrWEgdHUgc29saWNpdHVkIHkgZXNwZXJhIGxhIGFwcm9iYWNpw7NuIGRlIEdpdEh1YiBFZHVjYXRpb24uIEVzdG8gcHVlZGUgdGFyZGFyIGFsZ3Vub3MgZMOtYXMuKioNCg0KIyMgQ29uZmlndXJhciBHaXRIdWIgQ29waWxvdCBlbiBSU3R1ZGlvDQoNCjEuICAqKkFjdGl2YXIgR2l0SHViIENvcGlsb3Q6KioNCg0KICAgIGkuICBVbmEgdmV6IHF1ZSB0ZW5nYXMgdHUgY3VlbnRhIGRlIEdpdEh1YiBwYXJhIGVzdHVkaWFudGVzIGFwcm9iYWRhLCB2ZSBhIFtodHRwczovL2dpdGh1Yi5jb20vZmVhdHVyZXMvY29waWxvdF0oaHR0cHM6Ly9naXRodWIuY29tL2ZlYXR1cmVzL2NvcGlsb3QlNUQpDQoNCiAgICBpaS4gMlwuIEhheiBjbGljIGVuIMKrKlNpZ24gdXAgZm9yIEdpdEh1YiBDb3BpbG90KsK7IHkgc2lndWUgbGFzIGluc3RydWNjaW9uZXMgcGFyYSBhY3RpdmFyIENvcGlsb3QgZW4gdHUgY3VlbnRhLg0KDQoyLiAgKipVc2FyIEdpdEh1YiBDb3BpbG90IGNvbiBSU3R1ZGlvKioNCg0KICAgIGkuICBBYnJlIFJTdHVkaW8uDQoNCiAgICBpaS4gQWN0aXZhIEdpdEh1YiBDb3BpbG90IGVuIMKrKlRvb2xzKsK7IHkgwqsqR2xvYmFsIG9wdGlvbnMqwrsNCg0KICAgICAgICAhW10oaW1hZ2VzL3JzdHVkaW9fTEJ1bVhadmUyRC5naWYpDQoNCkdpdEh1YiBDb3BpbG90IGVzIHVuYSBoZXJyYW1pZW50YSBkZSBpbnRlbGlnZW5jaWEgYXJ0aWZpY2lhbCBkZXNhcnJvbGxhZGEgcG9yIEdpdEh1YiB5IE9wZW5BSSBxdWUgYXl1ZGEgYSBsb3MgZGVzYXJyb2xsYWRvcmVzIGEgZXNjcmliaXIgY8OzZGlnbyBtw6FzIHLDoXBpZG8geSBkZSBtYW5lcmEgbcOhcyBlZmljaWVudGUgYWwgc3VnZXJpciBsw61uZWFzIGRlIGPDs2RpZ28gbyBmdW5jaW9uZXMgY29tcGxldGFzIGJhc2FkYXMgZW4gZWwgY29udGV4dG8gZGVsIGPDs2RpZ28gZW4gZWwgcXVlIGVzdMOhbiB0cmFiYWphbmRvLiBBdW5xdWUgR2l0SHViIENvcGlsb3Qgc2UgaW50ZWdyYSBtZWpvciBjb24gVmlzdWFsIFN0dWRpbyBDb2RlIChWUyBDb2RlKSwgc2UgcHVlZGUgdXNhciBlbiBjb21iaW5hY2nDs24gY29uIFJTdHVkaW8gcGFyYSBtZWpvcmFyIGxhIHByb2R1Y3RpdmlkYWQgZW4gZWwgZGVzYXJyb2xsbyBkZSBjw7NkaWdvIGVuIFIuIEFxdcOtIHRlIGV4cGxpY28gY8OzbW8gcHVlZGUgc2VyIMO6dGlsOg0KDQojIyBCZW5lZmljaW9zIGRlIFVzYXIgR2l0SHViIENvcGlsb3QgY29uIFJTdHVkaW8NCg0KMS4gICoqR2VuZXJhY2nDs24gZGUgQ8OzZGlnbyBBdXRvbcOhdGljYToqKg0KDQogICAgLSAgIEdpdEh1YiBDb3BpbG90IHB1ZWRlIHN1Z2VyaXIgZnJhZ21lbnRvcyBkZSBjw7NkaWdvLCBmdW5jaW9uZXMgeSBlc3RydWN0dXJhcyBjb21wbGV0YXMgYmFzYWRhcyBlbiBjb21lbnRhcmlvcyB5IGVsIGPDs2RpZ28gcXVlIGVzdMOhcyBlc2NyaWJpZW5kby4NCg0KICAgIC0gICBFc3RvIHB1ZWRlIGFob3JyYXIgdGllbXBvIHkgcmVkdWNpciBlcnJvcmVzIGFsIGVzY3JpYmlyIGPDs2RpZ28gcmVwZXRpdGl2byBvIGVzdMOhbmRhci4NCg0KMi4gICoqTWVqb3JhIGRlIGxhIFByb2R1Y3RpdmlkYWQ6KioNCg0KICAgIC0gICBBbCBwcm9wb3JjaW9uYXIgc3VnZXJlbmNpYXMgZW4gdGllbXBvIHJlYWwsIENvcGlsb3QgcHVlZGUgYXl1ZGFyIGEgYWNlbGVyYXIgZWwgcHJvY2VzbyBkZSBkZXNhcnJvbGxvLCBwZXJtaXRpw6luZG90ZSBjb25jZW50cmFydGUgZW4gbGEgbMOzZ2ljYSB5IGVsIGFuw6FsaXNpcyBlbiBsdWdhciBkZSBsYSBzaW50YXhpcy4NCg0KMy4gICoqQXByZW5kaXphamUgeSBFZHVjYWNpw7NuOioqDQoNCiAgICAtICAgQ29waWxvdCBwdWVkZSBzZXIgdW5hIGhlcnJhbWllbnRhIGVkdWNhdGl2YSDDunRpbCBwYXJhIGFwcmVuZGVyIG51ZXZhcyBmdW5jaW9uZXMgeSBtZWpvcmVzIHByw6FjdGljYXMgZGUgcHJvZ3JhbWFjacOzbiBlbiBSIGFsIHN1Z2VyaXIgc29sdWNpb25lcyBhIHByb2JsZW1hcyBjb211bmVzLg0KDQogICAgLSAgIFB1ZWRlIGFjdHVhciBjb21vIHVuIHR1dG9yIHF1ZSBzdWdpZXJlIG1lam9yYXMgeSBhbHRlcm5hdGl2YXMgYWwgY8OzZGlnbyBxdWUgZXNjcmliZXMuDQoNCjQuICAqKlJlZHVjY2nDs24gZGUgRXJyb3JlczoqKg0KDQogICAgLSAgIEFsIHByb3BvcmNpb25hciBmcmFnbWVudG9zIGRlIGPDs2RpZ28gcHJlLXByb2JhZG9zIHkgc3VnZXJlbmNpYXMgYmFzYWRhcyBlbiBwYXRyb25lcyBjb211bmVzLCBDb3BpbG90IHB1ZWRlIGF5dWRhciBhIHJlZHVjaXIgbGEgY2FudGlkYWQgZGUgZXJyb3JlcyB5IGJ1Z3MgZW4gZWwgY8OzZGlnby4NCg0KNS4gICoqRXhwbG9yYWNpw7NuIGRlIE51ZXZhcyBGdW5jaW9uYWxpZGFkZXM6KioNCg0KICAgIC0gICBDb3BpbG90IHB1ZWRlIHN1Z2VyaXIgZnVuY2lvbmVzIHkgYmlibGlvdGVjYXMgcXVlIHF1aXrDoXMgbm8gY29ub2PDrWFzLCBleHBhbmRpZW5kbyB0dXMgY2FwYWNpZGFkZXMgeSBjb25vY2ltaWVudG8gZGVsIGxlbmd1YWplIFIuDQoNCiMgUHJveWVjdG8gZW4gUg0KDQpVbiBwcm95ZWN0byBlbiBSIGVzIHVuYSBlc3RydWN0dXJhIGRlIHRyYWJham8gcXVlIGF5dWRhIGEgb3JnYW5pemFyIHkgZ2VzdGlvbmFyIHR1IGPDs2RpZ28sIGRhdG9zLCByZXN1bHRhZG9zIHkgb3Ryb3MgYXJjaGl2b3MgcmVsYWNpb25hZG9zIGNvbiB1biBhbsOhbGlzaXMgbyBkZXNhcnJvbGxvIGVuIFIuIFV0aWxpemFyIHByb3llY3RvcyBlbiBSU3R1ZGlvLCBmYWNpbGl0YSBsYSBnZXN0acOzbiBkZSBtw7psdGlwbGVzIGFyY2hpdm9zIHkgbGEgY29uZmlndXJhY2nDs24gZGVsIGVudG9ybm8gZGUgdHJhYmFqbywgYXNlZ3VyYW5kbyBxdWUgdG9kbyBsbyBuZWNlc2FyaW8gcGFyYSBlbCBhbsOhbGlzaXMgZXN0w6kgZW4gdW4gc29sbyBsdWdhci4NCg0KIyMgQ2FyYWN0ZXLDrXN0aWNhcyBkZSB1biBQcm95ZWN0byBlbiBSDQoNCjEuICAqKkRpcmVjdG9yaW8gZGUgVHJhYmFqbzoqKg0KDQogICAgLSAgIFVuIHByb3llY3RvIGVuIFIgdGllbmUgc3UgcHJvcGlvIGRpcmVjdG9yaW8gZGUgdHJhYmFqbywgbG8gcXVlIGF5dWRhIGEgbWFudGVuZXIgdG9kb3MgbG9zIGFyY2hpdm9zIG9yZ2FuaXphZG9zIHkgcmVsYWNpb25hZG9zIGNvbiBlbCBwcm95ZWN0byBlbiB1biBzb2xvIGx1Z2FyLg0KDQoyLiAgKipDb25maWd1cmFjacOzbiBkZWwgRW50b3JubzoqKg0KDQogICAgLSAgIFJTdHVkaW8gZ3VhcmRhIGxhIGNvbmZpZ3VyYWNpw7NuIGVzcGVjw61maWNhIGRlbCBwcm95ZWN0bywgY29tbyBsb3MgYXJjaGl2b3MgYWJpZXJ0b3MsIGVsIGhpc3RvcmlhbCBkZSBjb21hbmRvcyB5IGxhIGNvbmZpZ3VyYWNpw7NuIGRlbCBlbnRvcm5vLg0KDQozLiAgKipGYWNpbGlkYWQgZGUgQ29tcGFydGlyOioqDQoNCiAgICAtICAgTG9zIHByb3llY3RvcyBzb24gZsOhY2lsZXMgZGUgY29tcGFydGlyIGNvbiBvdHJvcywgeWEgcXVlIHRvZG9zIGxvcyBhcmNoaXZvcyByZWxhY2lvbmFkb3Mgc2UgZW5jdWVudHJhbiBlbiBlbCBtaXNtbyBkaXJlY3RvcmlvLiBFc3RvIGVzIGVzcGVjaWFsbWVudGUgw7p0aWwgcGFyYSBsYSBjb2xhYm9yYWNpw7NuLg0KDQojIyBDcmVhciB1biBQcm95ZWN0byBlbiBSU3R1ZGlvDQoNCkFxdcOtIHRpZW5lcyB1bmEgZ3XDrWEgcGFzbyBhIHBhc28gcGFyYSBjcmVhciB1biBwcm95ZWN0byBlbiBSU3R1ZGlvOg0KDQoxLiAgKipBYnJpciBSU3R1ZGlvOioqDQoNCiAgICAtICAgSW5pY2lhIFJTdHVkaW8gZW4gdHUgY29tcHV0YWRvcmEuDQoNCjIuICAqKkNyZWFyIHVuIE51ZXZvIFByb3llY3RvOioqDQoNCiAgICAtICAgVmUgYWwgbWVuw7ogc3VwZXJpb3IgeSBzZWxlY2Npb25hIGBGaWxlYCBcPiBgTmV3IFByb2plY3QuLi5gLg0KDQogICAgKipTZWxlY2Npb25hciBUaXBvIGRlIFByb3llY3RvOioqDQoNCiAgICAtICAgQXBhcmVjZXLDoSB1bmEgdmVudGFuYSBjb24gdmFyaWFzIG9wY2lvbmVzLiBTZWxlY2Npb25hIGBOZXcgRGlyZWN0b3J5YCBzaSBkZXNlYXMgY3JlYXIgdW4gcHJveWVjdG8gZGVzZGUgY2VybywgYEV4aXN0aW5nIERpcmVjdG9yeWAgc2kgZGVzZWFzIGNvbnZlcnRpciB1bmEgY2FycGV0YSBleGlzdGVudGUgZW4gdW4gcHJveWVjdG8sIG8gYFZlcnNpb24gQ29udHJvbGAgc2kgZGVzZWFzIGNyZWFyIHVuIHByb3llY3RvIGEgcGFydGlyIGRlIHVuIHJlcG9zaXRvcmlvIGRlIEdpdC4NCg0KICAgICoqUGFyYSBlc3RlIGVqZW1wbG8sIHNlbGVjY2lvbmFyZW1vcyBgTmV3IERpcmVjdG9yeWAuKioNCg0KMy4gICoqRWxlZ2lyIHVuIFRpcG8gZGUgRGlyZWN0b3JpbzoqKg0KDQogICAgLSAgIFNlbGVjY2lvbmEgYE5ldyBQcm9qZWN0YCBwYXJhIGNyZWFyIHVuIHByb3llY3RvIHZhY8Otby4gVGFtYmnDqW4gcHVlZGVzIGVsZWdpciBgUiBQYWNrYWdlYCBzaSBlc3TDoXMgZGVzYXJyb2xsYW5kbyB1biBwYXF1ZXRlIGRlIFIgbyBgU2hpbnkgV2ViIEFwcGxpY2F0aW9uYCBwYXJhIGFwbGljYWNpb25lcyB3ZWIgaW50ZXJhY3RpdmFzLg0KDQo0LiAgKipDb25maWd1cmFyIGVsIFByb3llY3RvOioqDQoNCiAgICAtICAgRXNjcmliZSB1biBub21icmUgcGFyYSB0dSBwcm95ZWN0byB5IHNlbGVjY2lvbmEgbGEgdWJpY2FjacOzbiBkb25kZSBkZXNlYXMgY3JlYXIgZWwgZGlyZWN0b3JpbyBkZWwgcHJveWVjdG8uIFJTdHVkaW8gY3JlYXLDoSB1bmEgbnVldmEgY2FycGV0YSBjb24gZXN0ZSBub21icmUgZW4gbGEgdWJpY2FjacOzbiBzZWxlY2Npb25hZGEuDQoNCjUuICAqKkNyZWFyIGVsIFByb3llY3RvOioqDQoNCiAgICAtICAgSGF6IGNsaWMgZW4gYENyZWF0ZSBQcm9qZWN0YC4gUlN0dWRpbyBjcmVhcsOhIGVsIG51ZXZvIHByb3llY3RvIHkgYWJyaXLDoSB1bmEgbnVldmEgc2VzacOzbiBjb24gZXN0ZSBwcm95ZWN0byBhY3Rpdm8uDQoNCiFbXShpbWFnZXMvcnN0dWRpb19aUjNRT1pBS0JtLmdpZil7d2lkdGg9IjU4MCJ9DQoNCiMjIEVzdHJ1Y3R1cmEgZGUgdW4gUHJveWVjdG8gZW4gUg0KDQpVbmEgdmV6IGNyZWFkbyBlbCBwcm95ZWN0bywgUlN0dWRpbyBjb25maWd1cmFyw6EgYXV0b23DoXRpY2FtZW50ZSB1bmEgZXN0cnVjdHVyYSBiw6FzaWNhIGRlIGNhcnBldGFzIHkgYXJjaGl2b3MuIEFxdcOtIGhheSB1biBlamVtcGxvIGRlIGPDs21vIHBvZHLDrWEgdmVyc2U6DQoNCmBtaV9wcm95ZWN0by9gDQoNCmDilIJgDQoNCmDilJzilIDilIAgbWlfcHJveWVjdG8uUnByb2ogICAgICAjIEFyY2hpdm8gZGUgcHJveWVjdG8gZGUgUlN0dWRpb2ANCg0KYOKUnOKUgOKUgCBkYXRhLyAgICAgICAgICAgICAgICAgICMgQ2FycGV0YSBwYXJhIGRhdG9zYA0KDQpg4pSc4pSA4pSAIHNjcmlwdHMvICAgICAgICAgICAgICAgIyBDYXJwZXRhIHBhcmEgc2NyaXB0cyBkZSBSYA0KDQpg4pSc4pSA4pSAIG91dHB1dC8gICAgICAgICAgICAgICAgIyBDYXJwZXRhIHBhcmEgcmVzdWx0YWRvcyB5IGdyw6FmaWNvc2ANCg0KYOKUnOKUgOKUgCBSRUFETUUubWQgICAgICAgICAgICAgICMgQXJjaGl2byBkZSBkb2N1bWVudGFjacOzbiBkZWwgcHJveWVjdG9gDQoNCmDilJTilIDilIAgLmdpdC8gICAgICAgICAgICAgICAgICAjIENhcnBldGEgZGUgR2l0IHNpIGVsIHByb3llY3RvIGVzdMOhIHZlcnNpb25hZG9gDQoNCiMjIFRyYWJhamFyIGNvbiB1biBQcm95ZWN0byBlbiBSDQoNCjEuICAqKkFicmlyIGVsIFByb3llY3RvOioqDQoNCiAgICAtICAgQWJyZSBSU3R1ZGlvIHkgc2VsZWNjaW9uYSBgRmlsZWAgXD4gYE9wZW4gUHJvamVjdC4uLmAgcGFyYSBhYnJpciB1biBwcm95ZWN0byBleGlzdGVudGUuDQoNCjIuICAqKk9yZ2FuaXphciBBcmNoaXZvczoqKg0KDQogICAgLSAgIENvbG9jYSB0dXMgc2NyaXB0cyBkZSBSIGVuIGxhIGNhcnBldGEgYHNjcmlwdHMvYCxcDQogICAgICAgIHR1cyBkYXRvcyBlbiBsYSBjYXJwZXRhIGBkYXRhL2BcDQogICAgICAgIHkgdHVzIHJlc3VsdGFkb3MgZW4gbGEgY2FycGV0YSBgb3V0cHV0L2AuDQoNCiMgUiBzaW4gcHJveWVjdG8gLSDCv0PDs21vIGRlZmluaXIgbGEgcnV0YT8NCg0KQ3VhbmRvIHRyYWJhamFzIGVuIFIgeSBubyBlc3TDoXMgdXRpbGl6YW5kbyB1biBwcm95ZWN0byBkZSBSU3R1ZGlvLCBkZWZpbmlyIHJ1dGFzIGEgYXJjaGl2b3MgcHVlZGUgc2VyIHVuIHBvY28gbcOhcyBkZXNhZmlhbnRlIHBvcnF1ZSBubyB0aWVuZXMgbGEgZXN0cnVjdHVyYSBvcmdhbml6YWRhIGRlIHVuIHByb3llY3RvIHF1ZSBmYWNpbGl0ZSBsYSBnZXN0acOzbiBkZSBkaXJlY3Rvcmlvcy4gQXF1w60gdGUgZXhwbGljbyBjw7NtbyBtYW5lamFyIHJ1dGFzIGVuIFIgZW4gZXN0ZSBjb250ZXh0byB5IGFsZ3VuYXMgcHLDoWN0aWNhcyByZWNvbWVuZGFkYXMuDQoNCiMjIERlZmluaXIgUnV0YXMgZW4gUiBzaW4gdW4gUHJveWVjdG8NCg0KMS4gICoqUnV0YSBBYnNvbHV0YToqKg0KDQogICAgLSAgIFVuYSBydXRhIGFic29sdXRhIGVzcGVjaWZpY2EgbGEgdWJpY2FjacOzbiBjb21wbGV0YSBkZWwgYXJjaGl2byBlbiBlbCBzaXN0ZW1hIGRlIGFyY2hpdm9zLCBkZXNkZSBsYSByYcOteiBoYXN0YSBlbCBhcmNoaXZvLg0KDQogICAgLSAgICoqRWplbXBsbyBlbiBXaW5kb3dzOioqDQoNCiAgICAgICAgfCBgZmlsZV9wYXRoIDwtICJDOi9Vc2Vycy9Vc3VhcmlvL0RvY3VtZW50cy9kYXRvcy9taV9hcmNoaXZvLmNzdiIgZGF0YSA8LSByZWFkLmNzdihmaWxlX3BhdGgpYA0KDQogICAgLSAgICoqRWplbXBsbyBlbiBNYWMvTGludXg6KioNCg0KICAgICAgICB8IGBmaWxlX3BhdGggPC0gIi9Vc2Vycy9Vc3VhcmlvL0RvY3VtZW50cy9kYXRvcy9taV9hcmNoaXZvLmNzdiIgZGF0YSA8LSByZWFkLmNzdihmaWxlX3BhdGgpYA0KDQoyLiAgKipSdXRhIFJlbGF0aXZhOioqDQoNCiAgICAtICAgVW5hIHJ1dGEgcmVsYXRpdmEgZXNwZWNpZmljYSBsYSB1YmljYWNpw7NuIGRlbCBhcmNoaXZvIGVuIHJlbGFjacOzbiBjb24gZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIGFjdHVhbCBkZSBSLiBFbCBkaXJlY3RvcmlvIGRlIHRyYWJham8gZXMgZWwgZGlyZWN0b3JpbyBlbiBlbCBxdWUgUiBlc3TDoSBvcGVyYW5kbyBlbiBlc2UgbW9tZW50by4NCg0KICAgIC0gICBQYXJhIHZlciBvIGNhbWJpYXIgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvLCBwdWVkZXMgdXNhciBgZ2V0d2QoKWAgeSBgc2V0d2QoKWAgcmVzcGVjdGl2YW1lbnRlLg0KDQogICAgLSAgICoqRWplbXBsbyBwYXJhIG9idGVuZXIgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvIGFjdHVhbDoqKg0KDQogICAgICAgIHwgYGdldHdkKClgDQoNCiAgICAtICAgKipFamVtcGxvIHBhcmEgZXN0YWJsZWNlciB1biBudWV2byBkaXJlY3RvcmlvIGRlIHRyYWJham86KioNCg0KICAgICAgICB8IGBzZXR3ZCgiQzovVXNlcnMvVXN1YXJpby9Eb2N1bWVudHMvZGF0b3MiKSBmaWxlX3BhdGggPC0gIm1pX2FyY2hpdm8uY3N2IiBkYXRhIDwtIHJlYWQuY3N2KGZpbGVfcGF0aClgDQoNCiAgICAtICAgKipFamVtcGxvIHBhcmEgdW5hIHJ1dGEgcmVsYXRpdmEgZGVzZGUgZWwgZGlyZWN0b3JpbyBkZSB0cmFiYWpvOioqDQoNCiAgICAgICAgfCBgZmlsZV9wYXRoIDwtICJkYXRvcy9taV9hcmNoaXZvLmNzdiIgZGF0YSA8LSByZWFkLmNzdihmaWxlX3BhdGgpYA0KDQozLiAgKipVc28gZGVsIFBhcXVldGUgYGhlcmVgOioqDQoNCiAgICAtICAgQXVucXVlIGBoZXJlYCBzZSB1dGlsaXphIGNvbcO6bm1lbnRlIGRlbnRybyBkZSB1biBwcm95ZWN0byBkZSBSU3R1ZGlvLCBwdWVkZXMgdXNhcmxvIGZ1ZXJhIGRlIHVuIHByb3llY3RvIHBhcmEgY29uc3RydWlyIHJ1dGFzIHJlbGF0aXZhcyBkZSBtYW5lcmEgbcOhcyByb2J1c3RhLg0KDQogICAgLSAgIFByaW1lcm8sIGluc3RhbGEgZWwgcGFxdWV0ZSBzaSBubyBsbyB0aWVuZXM6DQoNCiAgICAgICAgfCBgaW5zdGFsbC5wYWNrYWdlcygiaGVyZSIpYA0KDQogICAgLSAgIEx1ZWdvLCB1dGlsaXphIGBoZXJlKClgIHBhcmEgY29uc3RydWlyIHJ1dGFzIHJlbGF0aXZhczoNCg0KICAgICAgICB8IGBsaWJyYXJ5KGhlcmUpIGZpbGVfcGF0aCA8LSBoZXJlKCJkYXRvcyIsICJtaV9hcmNoaXZvLmNzdiIpIGRhdGEgPC0gcmVhZC5jc3YoZmlsZV9wYXRoKWANCg0KIyMgQ8OzbW8gRGVmaW5pciBSdXRhcyBkZSBNYW5lcmEgRWZpY2llbnRlDQoNCjEuICAqKkNvbm9jZSB0dSBEaXJlY3RvcmlvIGRlIFRyYWJham86KioNCg0KICAgIC0gICBTaWVtcHJlIHZlcmlmaWNhIHR1IGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBhY3R1YWwgdXRpbGl6YW5kbyBgZ2V0d2QoKWAgcGFyYSBhc2VndXJhcnRlIGRlIHF1ZSBsYXMgcnV0YXMgcmVsYXRpdmFzIHNlYW4gY29ycmVjdGFzLg0KDQoyLiAgKipVdGlsaXphIFJ1dGFzIFJlbGF0aXZhcyBTaWVtcHJlIHF1ZSBTZWEgUG9zaWJsZToqKg0KDQogICAgLSAgIExhcyBydXRhcyByZWxhdGl2YXMgaGFjZW4gcXVlIGVsIGPDs2RpZ28gc2VhIG3DoXMgZmxleGlibGUgeSBwb3J0w6F0aWxlcywgZXNwZWNpYWxtZW50ZSBzaSBlbCBzY3JpcHQgc2UgbXVldmUgZW50cmUgZGlmZXJlbnRlcyBkaXJlY3RvcmlvcyBvIG3DoXF1aW5hcy4NCg0KMy4gICoqTWFudMOpbiB1bmEgRXN0cnVjdHVyYSBkZSBEaXJlY3RvcmlvcyBDbGFyYToqKg0KDQogICAgLSAgIE9yZ2FuaXphIHR1cyBhcmNoaXZvcyB5IGRhdG9zIGVuIGNhcnBldGFzIGNsYXJhbWVudGUgZXN0cnVjdHVyYWRhcyBwYXJhIGZhY2lsaXRhciBsYSByZWZlcmVuY2lhIHkgZXZpdGFyIGVycm9yZXMgZW4gbGFzIHJ1dGFzLg0KDQo0LiAgKipEb2N1bWVudGEgbGFzIFJ1dGFzOioqDQoNCiAgICAtICAgQXNlZ8O6cmF0ZSBkZSBkb2N1bWVudGFyIGVuIGVsIHNjcmlwdCBxdcOpIGRpcmVjdG9yaW8gZGUgdHJhYmFqbyBzZSBhc3VtZSBvIGPDs21vIHNlIGVzcGVyYSBxdWUgc2UgZXN0cnVjdHVyZW4gbGFzIGNhcnBldGFzIHBhcmEgZXZpdGFyIGNvbmZ1c2lvbmVzLg0KDQojIFNjcmlwdCBlbiBSDQoNClVuIHNjcmlwdCBlbiBSIGVzIHVuIGFyY2hpdm8gZGUgdGV4dG8gcXVlIGNvbnRpZW5lIHVuYSBzZXJpZSBkZSBjb21hbmRvcyB5IGPDs2RpZ28gZXNjcml0byBlbiBlbCBsZW5ndWFqZSBkZSBwcm9ncmFtYWNpw7NuIFIuIEVzdG9zIGNvbWFuZG9zIHNlIGVqZWN1dGFuIHNlY3VlbmNpYWxtZW50ZSBwYXJhIHJlYWxpemFyIHRhcmVhcyBlc3BlY8OtZmljYXMsIGNvbW8gYW7DoWxpc2lzIGRlIGRhdG9zLCB2aXN1YWxpemFjacOzbiBkZSBkYXRvcywgbWFuaXB1bGFjacOzbiBkZSBkYXRvcyB5IG3DoXMuIExvcyBzY3JpcHRzIGVuIFIgdGllbmVuIGdlbmVyYWxtZW50ZSBsYSBleHRlbnNpw7NuIGAuUmAuDQoNCiMjIENhcmFjdGVyw61zdGljYXMgZGUgdW4gU2NyaXB0IGVuIFINCg0KMS4gICoqQXJjaGl2byBkZSBUZXh0bzoqKg0KDQogICAgLSAgIFVuIHNjcmlwdCBlcyBzaW1wbGVtZW50ZSB1biBhcmNoaXZvIGRlIHRleHRvIHBsYW5vIHF1ZSBwdWVkZSBzZXIgZWRpdGFkbyBjb24gY3VhbHF1aWVyIGVkaXRvciBkZSB0ZXh0bywgYXVucXVlIG5vcm1hbG1lbnRlIHNlIGVkaXRhIHkgZWplY3V0YSBkZW50cm8gZGUgdW4gZW50b3JubyBkZSBkZXNhcnJvbGxvIGludGVncmFkbyAoSURFKSBjb21vIFJTdHVkaW8uDQoNCjIuICAqKlNlY3VlbmNpYSBkZSBDb21hbmRvczoqKg0KDQogICAgLSAgIENvbnRpZW5lIHVuYSBzZWN1ZW5jaWEgZGUgY29tYW5kb3MgcXVlIFIgZWplY3V0YSBlbiBlbCBvcmRlbiBlbiBxdWUgYXBhcmVjZW4uIEVzdG8gcGVybWl0ZSBhdXRvbWF0aXphciBwcm9jZXNvcyB5IHJlYWxpemFyIGFuw6FsaXNpcyByZXBldGlibGVzLg0KDQozLiAgKipSZXV0aWxpemFjacOzbjoqKg0KDQogICAgLSAgIExvcyBzY3JpcHRzIHB1ZWRlbiBndWFyZGFyc2UgeSByZXV0aWxpemFyc2UsIGxvIHF1ZSBlcyBlc3BlY2lhbG1lbnRlIMO6dGlsIHBhcmEgcmVwZXRpciBhbsOhbGlzaXMgbyBjb21wYXJ0aXIgbWV0b2RvbG9nw61hcyBjb24gb3Ryb3MuDQoNCjQuICAqKkRvY3VtZW50YWNpw7NuOioqDQoNCiAgICAtICAgTG9zIHNjcmlwdHMgc3VlbGVuIGluY2x1aXIgY29tZW50YXJpb3MgKGzDrW5lYXMgcXVlIGNvbWllbnphbiBjb24gYCNgKSBxdWUgZGVzY3JpYmVuIGxvIHF1ZSBoYWNlIGNhZGEgcGFydGUgZGVsIGPDs2RpZ28sIGZhY2lsaXRhbmRvIGxhIGNvbXByZW5zacOzbiB5IGVsIG1hbnRlbmltaWVudG8gZGVsIHNjcmlwdC4NCg0KIyMgQ3JlYXIgeSBFamVjdXRhciB1biBTY3JpcHQgZW4gUlN0dWRpbw0KDQpBcXXDrSBoYXkgdW5hIGd1w61hIHBhc28gYSBwYXNvIHBhcmEgY3JlYXIgeSBlamVjdXRhciB1biBzY3JpcHQgZW4gUlN0dWRpbzoNCg0KMS4gICoqQWJyaXIgUlN0dWRpbzoqKg0KDQogICAgLSAgIEluaWNpYSBSU3R1ZGlvIGVuIHR1IGNvbXB1dGFkb3JhLg0KDQoyLiAgKipDcmVhciB1biBOdWV2byBTY3JpcHQ6KioNCg0KICAgIC0gICBWZSBhbCBtZW7DuiBzdXBlcmlvciB5IHNlbGVjY2lvbmEgYEZpbGVgIFw+IGBOZXcgRmlsZWAgXD4gYFIgU2NyaXB0YC4gRXN0byBhYnJpcsOhIHVuYSBudWV2YSB2ZW50YW5hIGRlIHNjcmlwdCBlbiBSU3R1ZGlvLg0KDQogICAgICAgICFbXShpbWFnZXMvcnN0dWRpb195WVBhbURiazgxLTAxLmdpZil7d2lkdGg9IjU0MiJ9DQoNCjMuICAqKkVzY3JpYmlyIEPDs2RpZ28gZW4gZWwgU2NyaXB0OioqDQoNCiAgICAtICAgRXNjcmliZSBsb3MgY29tYW5kb3MgcXVlIGRlc2VhcyBlamVjdXRhciBlbiBlbCBzY3JpcHQuDQoNCjQuICAqKkd1YXJkYXIgZWwgU2NyaXB0OioqDQoNCiAgICAtICAgR3VhcmRhIGVsIHNjcmlwdCBzZWxlY2Npb25hbmRvIGBGaWxlYCBcPiBgU2F2ZWAgbyBwcmVzaW9uYW5kbyBgQ3RybCtTYCAoQ21kK1MgZW4gTWFjKS4gRWxpZ2UgdW4gbm9tYnJlIHkgdW5hIHViaWNhY2nDs24gcGFyYSBlbCBhcmNoaXZvLCBhc2VndXLDoW5kb3RlIGRlIHF1ZSB0aWVuZSBsYSBleHRlbnNpw7NuIGAuUmAuDQoNCjUuICAqKkVqZWN1dGFyIGVsIFNjcmlwdDoqKg0KDQogICAgLSAgIFB1ZWRlcyBlamVjdXRhciBlbCBzY3JpcHQgY29tcGxldG8gc2VsZWNjaW9uYW5kbyBgQ29kZWAgXD4gYFJ1biBSZWdpb25gIFw+IGBSdW4gQWxsYCBvIHByZXNpb25hbmRvIGBDdHJsK1NoaWZ0K0VudGVyYCAoQ21kK1NoaWZ0K0VudGVyIGVuIE1hYykuDQoNCiAgICAtICAgVGFtYmnDqW4gcHVlZGVzIGVqZWN1dGFyIHBhcnRlcyBkZWwgc2NyaXB0IHNlbGVjY2lvbmFuZG8gbGFzIGzDrW5lYXMgZGUgY8OzZGlnbyBxdWUgZGVzZWFzIGVqZWN1dGFyIHkgcHJlc2lvbmFuZG8gYEN0cmwrRW50ZXJgIChDbWQrRW50ZXIgZW4gTWFjKS4NCg0KIyMgRG9jdW1lbnRhY2nDs24geSBDb21lbnRhcmlvcw0KDQpMb3MgY29tZW50YXJpb3MgZW4gbG9zIHNjcmlwdHMgZGUgUiBzb24gaW1wb3J0YW50ZXMgcGFyYSBsYSBjbGFyaWRhZCB5IGxhIGNvbGFib3JhY2nDs24uIExvcyBjb21lbnRhcmlvcyBjb21pZW56YW4gY29uIGVsIHPDrW1ib2xvIGAjYCB5IHNlIHV0aWxpemFuIHBhcmEgZXhwbGljYXIgZWwgcHJvcMOzc2l0byBkZSBkaWZlcmVudGVzIHBhcnRlcyBkZWwgY8OzZGlnby4NCg0KIyBSIE1hcmtkb3duDQoNCkVzIHVuYSBleHRlbnNpw7NuIGRlIE1hcmtkb3duIHF1ZSBwZXJtaXRlIGxhIGludGVncmFjacOzbiBkZSBjw7NkaWdvIFIgZGlyZWN0YW1lbnRlIGVuIGRvY3VtZW50b3MgcXVlIGNvbWJpbmFuIHRleHRvLCBjw7NkaWdvIHkgcmVzdWx0YWRvcyBkZSBhbsOhbGlzaXMuIFIgTWFya2Rvd24gZXMgdW5hIGhlcnJhbWllbnRhIG11eSBwb2Rlcm9zYSBwYXJhIGNyZWFyIGluZm9ybWVzIHJlcHJvZHVjaWJsZXMgeSBkb2N1bWVudG9zIHF1ZSBpbmNsdXllbiBhbsOhbGlzaXMgZXN0YWTDrXN0aWNvcywgZ3LDoWZpY29zLCB0YWJsYXMgeSBtw6FzLg0KDQojIyBDYXJhY3RlcsOtc3RpY2FzIGRlIFIgTWFya2Rvd24NCg0KMS4gICoqQ29tYmluYSBUZXh0byB5IEPDs2RpZ286KioNCg0KICAgIC0gICBQdWVkZXMgZXNjcmliaXIgdGV4dG8gZW4gZm9ybWF0byBNYXJrZG93biB5IGFncmVnYXIgYmxvcXVlcyBkZSBjw7NkaWdvIFIgcXVlIHNlIGVqZWN1dGFyw6FuIHkgbW9zdHJhcsOhbiBsb3MgcmVzdWx0YWRvcyBlbiBlbCBkb2N1bWVudG8uDQoNCjIuICAqKlJlcHJvZHVjaWJpbGlkYWQ6KioNCg0KICAgIC0gICBQZXJtaXRlIGdlbmVyYXIgZG9jdW1lbnRvcyByZXByb2R1Y2libGVzIGRvbmRlIGVsIGPDs2RpZ28geSBsb3MgcmVzdWx0YWRvcyBzZSBhY3R1YWxpemFuIGF1dG9tw6F0aWNhbWVudGUgY3VhbmRvIGNhbWJpYXMgZWwgY8OzZGlnbyBvIGxvcyBkYXRvcy4NCg0KMy4gICoqU2FsaWRhIGVuIE3Dumx0aXBsZXMgRm9ybWF0b3M6KioNCg0KICAgIC0gICBQdWVkZXMgcmVuZGVyaXphciBkb2N1bWVudG9zIGVuIEhUTUwsIFBERiwgV29yZCwgeSBvdHJvcyBmb3JtYXRvcy4NCg0KNC4gICoqSW50ZXJhY3RpdmlkYWQ6KioNCg0KICAgIC0gICBQZXJtaXRlIGNyZWFyIGRvY3VtZW50b3MgaW50ZXJhY3Rpdm9zLCBjb21vIGFwbGljYWNpb25lcyBTaGlueSB5IHByZXNlbnRhY2lvbmVzIGNvbiBSIE1hcmtkb3duLg0KDQojIyBFc3RydWN0dXJhIGRlIHVuIERvY3VtZW50byBSIE1hcmtkb3duDQoNClVuIGRvY3VtZW50byBSIE1hcmtkb3duIHTDrXBpY28gdGllbmUgdHJlcyBwYXJ0ZXM6DQoNCjEuICAqKllBTUwgSGVhZGVyOioqDQoNCiAgICAtICAgQ29udGllbmUgbWV0YWRhdG9zIHNvYnJlIGVsIGRvY3VtZW50bywgY29tbyBlbCB0w610dWxvLCBlbCBhdXRvciwgbGEgZmVjaGEgeSBlbCBmb3JtYXRvIGRlIHNhbGlkYS4NCg0KICAgICAgICA+IGAtLS1gDQogICAgICAgID4NCiAgICAgICAgPiBgdGl0bGU6ICJBbsOhbGlzaXMgZGUgRGF0b3MiYA0KICAgICAgICA+DQogICAgICAgID4gYGF1dGhvcjogIlR1IE5vbWJyZSJgDQogICAgICAgID4NCiAgICAgICAgPiBgZGF0ZTogIjIwMjQtMDctMzAiYA0KICAgICAgICA+DQogICAgICAgID4gYG91dHB1dDogaHRtbF9kb2N1bWVudGANCiAgICAgICAgPg0KICAgICAgICA+IGAtLS1gDQoNCjIuICAqKkNodW5rcyBkZSBDw7NkaWdvOioqDQoNCiAgICAtICAgU2UgdXRpbGl6YW4gcGFyYSBpbnNlcnRhciB5IGVqZWN1dGFyIGPDs2RpZ28gUi4gTG9zIGNodW5rcyBkZSBjw7NkaWdvIGVzdMOhbiBkZWxpbWl0YWRvcyBwb3IgYGBgYCBgYGB7cn0gYGBgYCBhbCBwcmluY2lwaW8geSBgYGBgIGBgYCBgYGBgIGFsIGZpbmFsLg0KDQogICAgICAgID4gYGBgYCBgYGB7cn0gYGBgYA0KICAgICAgICA+DQogICAgICAgID4gYCMgQ2FyZ2FyIGRhdG9zYA0KICAgICAgICA+DQogICAgICAgID4gYGRhdGEgPC0gcmVhZC5jc3YoImRhdGEvbWlfYXJjaGl2by5jc3YiKWANCiAgICAgICAgPg0KICAgICAgICA+IGBzdW1tYXJ5KGRhdGEpYA0KICAgICAgICA+DQogICAgICAgID4gYGBgYCBgYGAgYGBgYA0KDQoqKlRleHRvIGVuIE1hcmtkb3duOioqDQoNCi0gICBVdGlsaXphIGxhIHNpbnRheGlzIGRlIE1hcmtkb3duIHBhcmEgZm9ybWF0ZWFyIHRleHRvLCBlbmNhYmV6YWRvcywgbGlzdGFzLCBlbmxhY2VzIHkgb3Ryb3MgZWxlbWVudG9zLg0KDQojIyBDcmVhciB5IEVqZWN1dGFyIHVuIERvY3VtZW50byBSIE1hcmtkb3duIGVuIFJTdHVkaW8NCg0KMS4gICoqQWJyaXIgUlN0dWRpbzoqKg0KDQogICAgLSAgIEluaWNpYSBSU3R1ZGlvIGVuIHR1IGNvbXB1dGFkb3JhLg0KDQoyLiAgKipDcmVhciB1biBOdWV2byBEb2N1bWVudG8gUiBNYXJrZG93bjoqKg0KDQogICAgLSAgIFZlIGFsIG1lbsO6IHN1cGVyaW9yIHkgc2VsZWNjaW9uYSBgRmlsZWAgXD4gYE5ldyBGaWxlYCBcPiBgUiBNYXJrZG93bi4uLmAuDQoNCjMuICAqKkNvbmZpZ3VyYXIgZWwgRG9jdW1lbnRvOioqDQoNCiAgICAtICAgQXBhcmVjZXLDoSB1bmEgdmVudGFuYSBwYXJhIGNvbmZpZ3VyYXIgZWwgbnVldm8gZG9jdW1lbnRvLiBJbnRyb2R1Y2UgZWwgdMOtdHVsbywgZWwgYXV0b3IgeSBzZWxlY2Npb25hIGVsIGZvcm1hdG8gZGUgc2FsaWRhIChIVE1MLCBQREYsIFdvcmQpLiBIYXogY2xpYyBlbiBgT0tgLg0KDQogICAgICAgICFbXShpbWFnZXMvcnN0dWRpb196SU9PQ01WSkhXLmdpZil7d2lkdGg9IjUyNiJ9DQoNCjQuICAqKkVzY3JpYmlyIGVsIENvbnRlbmlkbzoqKg0KDQogICAgLSAgIFJTdHVkaW8gYWJyaXLDoSB1biBudWV2byBhcmNoaXZvIFIgTWFya2Rvd24gY29uIHVuYSBlc3RydWN0dXJhIGLDoXNpY2EuIFB1ZWRlcyBlZGl0YXIgZXN0ZSBhcmNoaXZvIHBhcmEgYWdyZWdhciB0dSBwcm9waW8gdGV4dG8sIGNodW5rcyBkZSBjw7NkaWdvIHkgb3Ryb3MgZWxlbWVudG9zLg0KDQo1LiAgKipSZW5kZXJpemFyIGVsIERvY3VtZW50bzoqKg0KDQogICAgLSAgIFBhcmEgZ2VuZXJhciBlbCBkb2N1bWVudG8gZmluYWwsIGhheiBjbGljIGVuIGVsIGJvdMOzbiBgS25pdGAgZW4gbGEgYmFycmEgZGUgaGVycmFtaWVudGFzIGRlIFJTdHVkaW8uIEVzdG8gZWplY3V0YXLDoSBlbCBjw7NkaWdvIGVuIGxvcyBjaHVua3MgeSBnZW5lcmFyw6EgdW4gZG9jdW1lbnRvIGVuIGVsIGZvcm1hdG8gc2VsZWNjaW9uYWRvIChIVE1MLCBQREYsIFdvcmQpLg0KDQojIFBhcXVldGVzIGVuIFINCg0KLSAgIFVuICoqcGFxdWV0ZSoqIGVzIHVuYSBjb2xlY2Npw7NuIGRlIGZ1bmNpb25lcywgZGF0b3MgeSBkb2N1bWVudGFjacOzbiBxdWUgZXh0aWVuZGUgbGFzIGNhcGFjaWRhZGVzIGRlbCBsZW5ndWFqZSBSLg0KDQotICAgTG9zIHBhcXVldGVzIHBlcm1pdGVuIGEgbG9zIHVzdWFyaW9zIHJlYWxpemFyIHRhcmVhcyBlc3BlY8OtZmljYXMsIGNvbW8gYW7DoWxpc2lzIGVzdGFkw61zdGljb3MsIGdyw6FmaWNvcywgbWFuaXB1bGFjacOzbiBkZSBkYXRvcywgeSBtdWNobyBtw6FzLg0KDQotICAgTG9zIHBhcXVldGVzIHNvbiB1bmEgZm9ybWEgZGUgY29tcGFydGlyIHkgcmV1dGlsaXphciBjw7NkaWdvIHkgcmVjdXJzb3MgZW4gUi4NCg0KIyMgUXXDqSBlcyB1biBQYXF1ZXRlDQoNCjEuICAqKkZ1bmNpb25lczoqKg0KDQogICAgLSAgIENvbnRpZW5lIGZ1bmNpb25lcyBxdWUgcmVhbGl6YW4gdGFyZWFzIGVzcGVjw61maWNhcy4gUG9yIGVqZW1wbG8sIGVsIHBhcXVldGUgYGdncGxvdDJgIGluY2x1eWUgZnVuY2lvbmVzIHBhcmEgY3JlYXIgZ3LDoWZpY29zIGF2YW56YWRvcy4NCg0KMi4gICoqRGF0b3M6KioNCg0KICAgIC0gICBJbmNsdXllIGNvbmp1bnRvcyBkZSBkYXRvcyBxdWUgcHVlZGVuIHNlciB1dGlsaXphZG9zIHBhcmEgcHJhY3RpY2FyIG8gZGVtb3N0cmFyIGZ1bmNpb25hbGlkYWRlcyBkZWwgcGFxdWV0ZS4NCg0KMy4gICoqRG9jdW1lbnRhY2nDs246KioNCg0KICAgIC0gICBQcm9wb3JjaW9uYSBkb2N1bWVudGFjacOzbiB5IGVqZW1wbG9zIHNvYnJlIGPDs21vIHVzYXIgbGFzIGZ1bmNpb25lcyBkZWwgcGFxdWV0ZS4NCg0KNC4gICoqVmlnbmV0dGVzOioqDQoNCiAgICAtICAgRG9jdW1lbnRvcyBhZGljaW9uYWxlcyBxdWUgcHJvcG9yY2lvbmFuIHR1dG9yaWFsZXMgeSBlamVtcGxvcyBtw6FzIGRldGFsbGFkb3Mgc29icmUgZWwgdXNvIGRlbCBwYXF1ZXRlLg0KDQojIyBJbnN0YWxhY2nDs24gZGUgdW4gUGFxdWV0ZQ0KDQpQYXJhIGluc3RhbGFyIHVuIHBhcXVldGUgZW4gUiwgdXRpbGl6YXMgbGEgZnVuY2nDs24gYGluc3RhbGwucGFja2FnZXMoKWAuIEVzdGEgZnVuY2nDs24gZGVzY2FyZ2EgZSBpbnN0YWxhIGVsIHBhcXVldGUgZGVzZGUgQ1JBTiAoQ29tcHJlaGVuc2l2ZSBSIEFyY2hpdmUgTmV0d29yaykgbyBkZXNkZSBvdHJvIHJlcG9zaXRvcmlvIGVzcGVjaWZpY2Fkby4NCg0KKipFamVtcGxvIGRlIEluc3RhbGFjacOzbjoqKg0KDQp8IGAjIEluc3RhbGFyIGVsIHBhcXVldGUgZ2dwbG90MiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpYA0KDQpQdWVkZXMgaW5zdGFsYXIgdmFyaW9zIHBhcXVldGVzIGEgbGEgdmV6IHNpIHNlcGFyYXMgc3VzIG5vbWJyZXMgY29uIGNvbWFzOg0KDQp8IGAjIEluc3RhbGFyIG3Dumx0aXBsZXMgcGFxdWV0ZXMgIGluc3RhbGwucGFja2FnZXMoYygiZHBseXIiLCAidGlkeXIiLCAicmVhZHIiKSlgDQoNCiMjIENhcmdhIGRlIHVuIFBhcXVldGUNCg0KVW5hIHZleiBxdWUgdW4gcGFxdWV0ZSBlc3TDoSBpbnN0YWxhZG8sIGRlYmVzIGNhcmdhcmxvIGVuIHR1IHNlc2nDs24gZGUgUiBwYXJhIHVzYXIgc3VzIGZ1bmNpb25lcyB5IGRhdG9zLiBFc3RvIHNlIGhhY2UgY29uIGxhIGZ1bmNpw7NuIGBsaWJyYXJ5KClgLg0KDQoqKkVqZW1wbG8gZGUgQ2FyZ2E6KioNCg0KfCBgIyBDYXJnYXIgZWwgcGFxdWV0ZSBnZ3Bsb3QyICBsaWJyYXJ5KGdncGxvdDIpYA0KDQojIyBWZXJpZmljYXIgUGFxdWV0ZXMgSW5zdGFsYWRvcw0KDQpQYXJhIHZlciB1bmEgbGlzdGEgZGUgbG9zIHBhcXVldGVzIHF1ZSB0aWVuZXMgaW5zdGFsYWRvcyBlbiB0dSBzaXN0ZW1hLCBwdWVkZXMgdXNhciBsYSBmdW5jacOzbiBgaW5zdGFsbGVkLnBhY2thZ2VzKClgOg0KDQp8IGAjIE1vc3RyYXIgbG9zIHBhcXVldGVzIGluc3RhbGFkb3MgIGluc3RhbGxlZC5wYWNrYWdlcygpYA0KDQojIyBBY3R1YWxpemFjacOzbiBkZSBQYXF1ZXRlcw0KDQpQYXJhIGFjdHVhbGl6YXIgdW4gcGFxdWV0ZSBhIGxhIMO6bHRpbWEgdmVyc2nDs24gZGlzcG9uaWJsZSwgdXNhIGxhIGZ1bmNpw7NuIGB1cGRhdGUucGFja2FnZXMoKWA6DQoNCnwgXGAjIEFjdHVhbGl6YXIgdG9kb3MgbG9zIHBhcXVldGVzIGluc3RhbGFkb3MgdXBkYXRlLnBhY2thZ2VzKCkNCg0KXCMgQWN0dWFsaXphciB1biBwYXF1ZXRlIGVzcGVjw61maWNvIHVwZGF0ZS5wYWNrYWdlcygiZ2dwbG90MiIpXGANCg0KIyMgRWxpbWluYWNpw7NuIGRlIFBhcXVldGVzDQoNClBhcmEgZWxpbWluYXIgdW4gcGFxdWV0ZSBxdWUgeWEgbm8gbmVjZXNpdGFzLCB1dGlsaXphIGxhIGZ1bmNpw7NuIGByZW1vdmUucGFja2FnZXMoKWA6DQoNCnwgYCMgRWxpbWluYXIgZWwgcGFxdWV0ZSBnZ3Bsb3QyICByZW1vdmUucGFja2FnZXMoImdncGxvdDIiKWANCg0KIyBDb21hbmRvcyBlbiBSDQoNCkVuIGVzdGUgZG9jdW1lbnRvIHNlIHByZXNlbnRhbiB2YXJpb3MgY29tYW5kb3Mgw7p0aWxlcyBlbiBSIHkgc2UgZXhwbGljYW4gc3VzIHVzb3MgY29uIGVqZW1wbG9zIHByw6FjdGljb3MuDQoNCiMjIENvbnRyaWJ1aWRvcmVzIGFsIERlc2Fycm9sbG8gZGUgUg0KDQpFbCBjb21hbmRvIFxgY29udHJpYnV0b3JzKClcYCBtdWVzdHJhIHVuYSBsaXN0YSBkZSBwZXJzb25hcyBxdWUgaGFuIGNvbnRyaWJ1aWRvIGFsIGRlc2Fycm9sbG8gZGUgUi4NCg0KYGBge1IgQ29udHJpYnVpZG9yZXMgc2V0dXAsIGVjaG89VFJVRX0NCiMgTW9zdHJhciBsb3MgY29udHJpYnVpZG9yZXMgYWwgZGVzYXJyb2xsbyBkZSBSDQpjb250cmlidXRvcnMoKQ0KDQpgYGANCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMjUyNzkzNzgxNC5wbmcpe3dpZHRoPSI1OTEifQ0KDQojIyBDaXRhciB1biBQYXF1ZXRlIEVzcGVjw61maWNvDQoNCkVsIGNvbWFuZG8gYGNpdGF0aW9uKClgIHByb3BvcmNpb25hIGluZm9ybWFjacOzbiBzb2JyZSBjw7NtbyBjaXRhciBSIHkgc3VzIHBhcXVldGVzIGVuIHB1YmxpY2FjaW9uZXMuIFBhcmEgY2l0YXIgdW4gcGFxdWV0ZSBlc3BlY8OtZmljbywgcHVlZGVzIHVzYXIgYGNpdGF0aW9uKCJub21icmVfZGVsX3BhcXVldGUiKWAuDQoNCmBgYHtSIENpdGEgc2V0dXAsIGVjaG89VFJVRX0NCiMgQ2l0YXIgUg0KY2l0YXRpb24oKQ0KDQojQ2l0YXIgZWwgcGFxdWV0ZSBkcGx5cg0KY2l0YXRpb24oImRwbHlyIikNCmBgYA0KDQojIyBJbmZvcm1hY2nDs24gZGUgbGEgU2VzacOzbiBkZSBSDQoNCkVsIGNvbWFuZG8gc2Vzc2lvbkluZm8oKSBtdWVzdHJhIGluZm9ybWFjacOzbiBzb2JyZSBsYSBzZXNpw7NuIGFjdHVhbCBkZSBSLCBpbmNsdXllbmRvIHZlcnNpb25lcyBkZSBwYXF1ZXRlcywgc2lzdGVtYSBvcGVyYXRpdm8sIHkgbcOhcy4NCg0KYGBge1Igc2VzaW9uIHNldHVwLCBlY2hvPVRSVUV9DQojIE1vc3RyYXIgaW5mb3JtYWNpw7NuIGRlIGxhIHNlc2nDs24gZGUgUg0Kc2Vzc2lvbkluZm8oKQ0KYGBgDQoNCiMjIEZlY2hhIHkgSG9yYSBkZWwgU2lzdGVtYQ0KDQpMb3MgY29tYW5kb3MgU3lzLkRhdGUoKSB5IFN5cy50aW1lKCkgc2UgdXRpbGl6YW4gcGFyYSBvYnRlbmVyIGxhIGZlY2hhIHkgaG9yYSBhY3R1YWwgZGVsIHNpc3RlbWEsIHJlc3BlY3RpdmFtZW50ZS4NCg0KYGBge1IgRmVjaGEgc2V0dXAsIGVjaG89VFJVRX0NCiMgTW9zdHJhciBsYSBmZWNoYSBhY3R1YWwgZGVsIHNpc3RlbWENClN5cy5EYXRlKCkNCg0KIyBNb3N0cmFyIGxhIGhvcmEgYWN0dWFsIGRlbCBzaXN0ZW1hDQpTeXMudGltZSgpDQpgYGANCg0KIyMgQXl1ZGENCg0KKipgaGVscCgpYCoqIHkgKipgP2AqKiBwcm9wb3JjaW9uYW4gYXl1ZGEgc29icmUgcGFxdWV0ZXMsIGZ1bmNpb25lcyB5IG9iamV0b3MgcXVlIGVzdMOpbiBpbnN0YWxhZG9zDQoNCmBgYHtSIGhlbHAgc2V0dXAsIGVjaG89VFJVRX0NCiMgQXl1ZGEgc29icmUgbGEgZnVuY2nDs24gbG0NCiAgaGVscChsbSkNCiMgT3RyYSBmb3JtYQ0KICA/bG0NCmBgYA0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0yMzI0NjM0NzA3LnBuZykNCg0KU2kgZWwgcGFxdWV0ZSBvIGZ1bmNpw7NuIG5vIGVzdGEgaW5zdGFsYWRvIHNlIGRlYmUgdXNhciAqKmA/P2AqKg0KDQojIyBPYmpldG9zIGRlbCBlbnRvcm5vIGRlIHRyYWJham8NCg0KKipgbHNgKiogbGlzdGEgdG9kb3MgbG9zIG9iamV0b3MgZGVsIGVudG9ybm8gZGUgdHJhYmFqbw0KDQpgYGB7UiBscyBzZXR1cCwgZWNobz1UUlVFfQ0KbHMoKQ0KYGBgDQoNCiMjIEVsaW1pbmFyIG9iamV0b3MgZGVsIGVudG9ybm8gZGUgdHJhYmFqbw0KDQpgcm1gZWxpbWluYSB1bm8gbyBtw6FzIGxvcyBvYmpldG9zIGRlbCBlbnRvcm5vIGRlIHRyYWJham8NCg0KfCBgcm0oYmFzZSlgDQoNCiMjIENvZXJjacOzbiBkZSBUaXBvcyB5IENsYXNlcyBkZSBWYXJpYWJsZXMNCg0KRWwgY29tYW5kbyBhcy5udW1lcmljKCkgcHVlZGUgdXNhcnNlIHBhcmEgY29udmVydGlyIHZhbG9yZXMgYSB0aXBvIG51bcOpcmljby4gQXF1w60sIGxvIHVzYW1vcyBwYXJhIGNvbnZlcnRpciBlbCB2YWxvciBkZSBTeXMudGltZSgpIGEgdW4gbsO6bWVyby4gVGFtYmnDqW4gc2UgcHVlZGUgdXNhciBjbGFzcygpIHBhcmEgdmVyaWZpY2FyIGxhIGNsYXNlIGRlIHVuYSB2YXJpYWJsZS4NCg0KYGBge1IgdGlwbyBzZXR1cCwgZWNobz1UUlVFfQ0KIyBDb252ZXJ0aXIgbGEgaG9yYSBhY3R1YWwgYSB0aXBvIG51bcOpcmljbw0KYXMubnVtZXJpYyhTeXMudGltZSgpKQ0KDQojIFZlcmlmaWNhciBsYSBjbGFzZSBkZSBsYSB2YXJpYWJsZSBTeXMudGltZSgpDQpjbGFzcyhTeXMudGltZSgpKQ0KYGBgDQoNCiMjIEJhc2VzIGRlIERhdG9zIGRlIEVqZW1wbG8gRGlzcG9uaWJsZXMgZW4gUg0KDQpFbCBjb21hbmRvIGRhdGEoKSBsaXN0YSBsYXMgYmFzZXMgZGUgZGF0b3MgZGUgZWplbXBsbyBkaXNwb25pYmxlcyBlbiBSLg0KDQpgYGB7UiBkYXRhIHNldHVwLCBlY2hvPVRSVUV9DQojIExpc3RhciBsYXMgYmFzZXMgZGUgZGF0b3MgZGUgZWplbXBsbyBkaXNwb25pYmxlcyBlbiBSDQpkYXRhKCkNCmBgYA0KDQojIyBDYXJnYXIgdW5hIEJhc2UgZGUgRGF0b3MgZGUgRWplbXBsbw0KDQpQYXJhIGNhcmdhciB1bmEgYmFzZSBkZSBkYXRvcyBkZSBlamVtcGxvLCBzZSBwdWVkZSB1c2FyIGVsIGNvbWFuZG8gZGF0YShub21icmVfZGVfbGFfYmFzZV9kZV9kYXRvcykuDQoNCmBgYHtSIGRhdGFleCBzZXR1cCwgZWNobz1UUlVFfQ0KIyBDYXJnYXIgdW5hIGRlIGxhcyBiYXNlcyBkZSBkYXRvcyBkZSBlamVtcGxvIGRlIFINCmRhdGEocHJlc2lkZW50cykNCg0KcHJlc2lkZW50cw0KYGBgDQoNCiMgTWlzIHByaW1lcm9zIHBhc29zIGVuIGVsIGFuw6FsaXNpcyBiYXNlcyBkZSBkYXRvcyBlbiBSDQoNCiMjIENhcmdhciBsYSBiYXNlIGRlIGRhdG9zDQoNClBhcmEgY2FyZ2FyIGJhc2VzIGRlIGRhdG9zLCBwdWVkZXMgdXNhciBkaWZlcmVudGVzIGNvbWFuZG9zIGRlcGVuZGllbmRvIGRlbCBmb3JtYXRvIGRlbCBhcmNoaXZvLiBBIGNvbnRpbnVhY2nDs24sIHNlIHByZXNlbnRhbiBhbGd1bm9zIGVqZW1wbG9zIGRlIGPDs21vIGNhcmdhciBkYXRvcyBkZXNkZSBhcmNoaXZvcyBDU1YgeSBhcmNoaXZvcyBkZSB0ZXh0bywgYXPDrSBjb21vIGRlc2RlIGFyY2hpdm9zIEV4Y2VsLg0KDQojIyMgQXJjaGl2b3MgQ1NWDQoNClNpIHRpZW5lcyB1biBhcmNoaXZvIENTViwgcHVlZGVzIHVzYXIgbGEgZnVuY2nDs24gXGByZWFkLmNzdigpXGAgcGFyYSBjYXJnYXIgbG9zIGRhdG9zLiBBcXXDrSBoYXkgdW4gZWplbXBsbyBkZSBjw7NtbyBoYWNlcmxvOg0KDQp8IFxgIyBDYXJnYXIgdW4gYXJjaGl2byBDU1YgY29uIHZhbG9yZXMgc2VwYXJhZG9zIHBvciBwdW50byB5IGNvbWEgZGYgXDwtIHJlYWQuY3N2KCJiZC5jc3YiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiOyIsIGRlYyA9ICIuIikNCg0KXCMgQ2FyZ2FyIHVuIGFyY2hpdm8gQ1NWIGNvbiB2YWxvcmVzIHNlcGFyYWRvcyBwb3IgdGFidWxhY2nDs24gZGYgXDwtIHJlYWQuY3N2KCJiZC50eHQiLCBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBkZWMgPSAiLiIpXGAgXHwNCg0KLSAgICoqYGhlYWRlciA9IFRSVUVgKio6IEluZGljYSBxdWUgbGEgcHJpbWVyYSBmaWxhIGRlbCBhcmNoaXZvIGNvbnRpZW5lIGxvcyBub21icmVzIGRlIGxhcyBjb2x1bW5hcy4NCg0KLSAgICoqYHNlcCA9ICI7ImAqKjogRXNwZWNpZmljYSBlbCBkZWxpbWl0YWRvciBkZSBsb3MgdmFsb3JlcyBlbiBlbCBhcmNoaXZvLiBFbiBlc3RlIGNhc28sIGVsIGRlbGltaXRhZG9yIGVzIGVsIHB1bnRvIHkgY29tYS4NCg0KLSAgICoqYHNlcCA9ICJcdCJgKio6IEVzcGVjaWZpY2EgcXVlIGVsIGRlbGltaXRhZG9yIGRlIGxvcyB2YWxvcmVzIGVuIGVsIGFyY2hpdm8gZXMgdW5hIHRhYnVsYWNpw7NuICh0YWIpLg0KDQogICAgKipgZGVjID0gIi4iYCoqOiBEZWZpbmUgZWwgY2Fyw6FjdGVyIHV0aWxpemFkbyBwYXJhIGxvcyBkZWNpbWFsZXMuIEFxdcOtLCBlbCBwdW50byBlcyBlbCBzZXBhcmFkb3IgZGVjaW1hbC4NCg0KIyMjIEFyY2hpdm9zIEV4Y2VsDQoNClBhcmEgY2FyZ2FyIGRhdG9zIGRlc2RlIHVuIGFyY2hpdm8gRXhjZWwsIHB1ZWRlcyB1c2FyIGVsIHBhcXVldGUgYHJlYWR4bGAgeSBzdSBmdW5jacOzbiBgcmVhZF94bHN4KClgLiBQcmltZXJvLCBhc2Vnw7pyYXRlIGRlIHRlbmVyIGVsIHBhcXVldGUgaW5zdGFsYWRvIHkgY2FyZ2FkbzoNCg0KfCBcYCMgSW5zdGFsYXIgZWwgcGFxdWV0ZSByZWFkeGwgc2kgYcO6biBubyBlc3TDoSBpbnN0YWxhZG8gaW5zdGFsbC5wYWNrYWdlcygicmVhZHhsIikNCg0KXCMgQ2FyZ2FyIGVsIHBhcXVldGUgcmVhZHhsIGxpYnJhcnkocmVhZHhsKQ0KDQpcIyBDYXJnYXIgZGF0b3MgZGVzZGUgdW4gYXJjaGl2byBFeGNlbCBkZiBcPC0gcmVhZHhsOjpyZWFkX3hsc3goIlU6XFxNaSB1bmlkYWRcXEFuYWx5dGljc1xcZGYueGxzeCIpXGANCg0KLSAgICoqYHJlYWR4bDo6cmVhZF94bHN4KClgKio6IEVzdGEgZnVuY2nDs24gbGVlIGFyY2hpdm9zIEV4Y2VsIGNvbiBleHRlbnNpw7NuIGAueGxzeGAuDQoNCi0gICAqKlJ1dGEgZGVsIGFyY2hpdm8qKjogRXNwZWNpZmljYSBsYSBydXRhIGNvbXBsZXRhIGFsIGFyY2hpdm8gRXhjZWwuIEFzZWfDunJhdGUgZGUgcXVlIGxhIHJ1dGEgc2VhIGNvcnJlY3RhIHkgcXVlIHRlbmdhcyBwZXJtaXNvcyBkZSBsZWN0dXJhIHBhcmEgZWwgYXJjaGl2by4NCg0KIyMjIERlc2RlIHVuIHByb3llY3RvDQoNCkVuIGxhIHBhbnRhbGxhIGBGaWxlc2AgYnVzcXVlIGxhIGJhc2UgZGUgZGF0b3MsIGNsaWMgc29icmUgZWxsYSBlIGBJbXBvcnQgZGF0YXNldGANCg0KIVtdKGltYWdlcy9yc3R1ZGlvX2hWMnhOMjNhdzkuZ2lmKXt3aWR0aD0iNTIyIn0NCg0KQWRpY2lvbmFsbWVudGUsIGRlc2RlIGVsIG1lbsO6IGBGaWxlID4gSW1wb3J0IERhdGFzZXQgPmBwdWVkZSBzZWxlY2Npb25hciBkaWZlcmVudGVzIG3DqXRvZG9zIHBhcmEgY2FyZ2FyIGRhdG9zIGRlcGVuZGllbmRvIGRlIHN1IGZvcm1hdG8NCg0KIyMjIERlc2RlIGVsIHBvcnRhcGFwZWxlcw0KDQpQYXJhIGxlZXIgZGF0b3MgZGlyZWN0YW1lbnRlIGRlc2RlIGVsIHBvcnRhcGFwZWxlcyBkZWwgc2lzdGVtYSBvcGVyYXRpdm8geSBjYXJnYXJsb3MgZW4gdW4gZGF0YWZyYW1lIGVuIFIsIHNlIHVzYSBlbCBzaWd1aWVudGUgY8OzZGlnbzoNCg0KMS4gIFNlIHRpZW5lIGVzdGEgYmFzZSBkZSBkYXRvcyBlbiBFeGNlbCFbXShpbWFnZXMvY2xpcGJvYXJkLTQwNzY4NDQwNzcucG5nKXt3aWR0aD0iMzkyIn0NCg0KMi4gIFNlIHNlbGVjY2lvbmFuIGxhcyBjZWxkYXMgeSBzZSBjb3BpYW4gYGN0cmwgKyBjYA0KDQozLiAgU2UgY29ycmUgZWwgc2lndWllbnRlIGNvZGlnbyBlbiBSDQoNCiAgICB8IGBkYXRvcyA8LSByZWFkLnRhYmxlKGZpbGUgPSAiY2xpcGJvYXJkIiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IilgDQoNCi0gICAqKmByZWFkLnRhYmxlKClgKio6IGVzIHVuYSBmdW5jacOzbiBlbiBSIHF1ZSBzZSB1dGlsaXphIHBhcmEgbGVlciBkYXRvcyBlbiBmb3JtYSBkZSB0YWJsYSBkZXNkZSB1biBhcmNoaXZvIG8gdW5hIGNvbmV4acOzbi4gUG9yIGRlZmVjdG8sIGVzdGEgZnVuY2nDs24gbGVlIGFyY2hpdm9zIGRlIHRleHRvIGRvbmRlIGxvcyB2YWxvcmVzIGVzdMOhbiBzZXBhcmFkb3MgcG9yIGVzcGFjaW9zIG8gdGFidWxhY2lvbmVzLg0KDQotICAgKipgZmlsZSA9ICJjbGlwYm9hcmQiYCoqOiBlc3BlY2lmaWNhIGxhIGZ1ZW50ZSBkZSBsb3MgZGF0b3MgcXVlIHNlIHZhbiBhIGxlZXIuIEVuIGVzdGUgY2FzbywgYCJjbGlwYm9hcmQiYCBpbmRpY2EgcXVlIGxvcyBkYXRvcyBkZWJlbiBzZXIgbGXDrWRvcyBkZXNkZSBlbCBwb3J0YXBhcGVsZXMgZGVsIHNpc3RlbWEgb3BlcmF0aXZvLg0KDQogICAgVXNhciBgImNsaXBib2FyZCJgIGVzIMO6dGlsIGN1YW5kbyBoYXMgY29waWFkbyBkYXRvcyB0YWJ1bGFyZXMgZGVzZGUgb3RyYSBhcGxpY2FjacOzbiAoY29tbyBFeGNlbCBvIHVuIGVkaXRvciBkZSB0ZXh0bykgeSBxdWllcmVzIHBlZ2FybG9zIGRpcmVjdGFtZW50ZSBlbiBSIHNpbiB0ZW5lciBxdWUgZ3VhcmRhcmxvcyBlbiB1biBhcmNoaXZvIHByaW1lcm8uDQoNCi0gICAqKmBoZWFkZXIgPSBUUlVFYCoqOiBpbmRpY2Egc2kgZWwgYXJjaGl2byBkZSBkYXRvcyB0aWVuZSB1bmEgZmlsYSBkZSBlbmNhYmV6YWRvIChub21icmVzIGRlIGNvbHVtbmFzKSBlbiBsYSBwcmltZXJhIGZpbGEuIEN1YW5kbyBzZSBlc3RhYmxlY2UgZW4gYFRSVUVgLCBgcmVhZC50YWJsZSgpYCB0cmF0YSBsYSBwcmltZXJhIGZpbGEgZGVsIGFyY2hpdm8gY29tbyBsb3Mgbm9tYnJlcyBkZSBsYXMgY29sdW1uYXMgZGVsIGRhdGFmcmFtZSByZXN1bHRhbnRlLg0KDQotICAgKipgc2VwID0gIlx0ImAqKjogZXNwZWNpZmljYSBlbCBkZWxpbWl0YWRvciBxdWUgc2UgdXRpbGl6YSBwYXJhIHNlcGFyYXIgbG9zIHZhbG9yZXMgZW4gbG9zIGRhdG9zLiBFbiBlc3RlIGNhc28sIGAiXHQiYCBpbmRpY2EgcXVlIGVsIGRlbGltaXRhZG9yIGVzIHVuYSB0YWJ1bGFjacOzbiAodGFiKSwgcXVlIGVzIGNvbcO6biBlbiBhcmNoaXZvcyB0YWJ1bGFkb3MuDQoNCiAgICBFc3RvIHNpZ25pZmljYSBxdWUgYHJlYWQudGFibGUoKWAgZXNwZXJhIHF1ZSBsb3MgZGF0b3MgZW4gZWwgcG9ydGFwYXBlbGVzIGVzdMOpbiBzZXBhcmFkb3MgcG9yIHRhYnVsYWNpb25lcy4NCg0KYGBge1IgcGVndWUgc2V0dXAsIGVjaG89VFJVRX0NCg0KZGF0b3MgPC0gcmVhZC50YWJsZShmaWxlID0gImNsaXBib2FyZCIsIGhlYWRlciA9IFRSVUUsIHNlcCA9ICJcdCIpDQpkYXRvcw0KYGBgDQoNCiMjIERlc2NyaWJpciBsYSBiYXNlIGRlIGRhdG9zDQoNClBhcmEgZXN0YSBzZWNjacOzbiBzZSB1c2FyYSBsYSBiYXNlIGRlIGRhdG9zIGRlIGxhIHBydWViYSBkZSByZWFsaXphZGEgbGxhbWFkYSBgZGZgDQoNCiMjIyBDYXJndWUgZGUgbGEgYmFzZSBkZSBkYXRvcw0KDQpgYGB7UiBkZiBzZXR1cCwgZWNobz1UUlVFfQ0KDQpsaWJyYXJ5KHJlYWR4bCkNCmRmIDwtIHJlYWRfZXhjZWwoIkJhc2VzIGRlIGRhdG9zL2RmLnhsc3giKQ0KVmlldyhkZikNCg0KYGBgDQoNCiMjIyBEaW1lbnNpw7NuIGRlIGxhIEJhc2UgZGUgRGF0b3MNCg0KUGFyYSBvYnRlbmVyIGxhIGRpbWVuc2nDs24gZGUgdW4gZGF0YWZyYW1lLCBlcyBkZWNpciwgZWwgbsO6bWVybyBkZSBmaWxhcyB5IGNvbHVtbmFzLCB1dGlsaXphIGxhIGZ1bmNpw7NuIFxgZGltKClcYDoNCg0KYGBge1IgZGltIHNldHVwLCBlY2hvPVRSVUV9DQojIERpbWVuc2nDs24gZGUgbGEgYmFzZSBkZSBkYXRvcyAoZmlsYXMgeCBjb2x1bW5hcykNCmRpbShkZikNCmBgYA0KDQoqKmBkaW0oZGYpYCoqOiBEZXZ1ZWx2ZSB1biB2ZWN0b3IgY29uIGVsIG7Dum1lcm8gZGUgZmlsYXMgeSBlbCBuw7ptZXJvIGRlIGNvbHVtbmFzIGRlbCBkYXRhZnJhbWUgYGRmYC4NCg0KWypQYXJhIGVsIGNhc28gZGUgbGEgYmFzZSBkZSBkYXRvcyBhbmFsaXphZGEsIHNlIHRpZW5lbiAyMDAgZmlsYXMgeSA0IGNvbHVtbmFzKl17LnVuZGVybGluZX0NCg0KIyMjIE7Dum1lcm8gZGUgQ29sdW1uYXMgeSBGaWxhcyBwb3Igc2VwYXJhZG8NCg0KUGFyYSBvYnRlbmVyIGVsIG7Dum1lcm8gZGUgY29sdW1uYXMgeSBmaWxhcywgcHVlZGVzIHVzYXIgbG9zIHNpZ3VpZW50ZXMgY29tYW5kb3M6DQoNCi0gICAqKmBsZW5ndGgoZGYpYCoqOiBEZXZ1ZWx2ZSBlbCBuw7ptZXJvIGRlIGNvbHVtbmFzIGVuIGVsIGRhdGFmcmFtZSBgZGZgLg0KDQotICAgKipgbnJvdyhkZilgKio6IERldnVlbHZlIGVsIG7Dum1lcm8gZGUgZmlsYXMgZW4gZWwgZGF0YWZyYW1lIGBkZmAuDQoNCmBgYHtSIGNvbCB5IGZpbCBzZXR1cCwgZWNobz1UUlVFfQ0KIyBOw7ptZXJvIGRlIGNvbHVtbmFzDQpsZW5ndGgoZGYpDQoNCiMgTsO6bWVybyBkZSBmaWxhcw0KbnJvdyhkZikNCmBgYA0KDQpbKkVsIHByaW1lciByZXN1bHRhZG8gaW5kaWNhIHF1ZSBsYSBiYXNlIGRlIGRhdG9zIGRmIGN1ZW50YSBjb24gNCBjb2x1bW5hcywgZWwgc2VndW5kbyBpbmRpY2EgZWwgbsO6bWVybyBkZSBmaWxhcyAoMjAwIHJlZ2lzdHJvcykqXXsudW5kZXJsaW5lfQ0KDQojIyMgTsO6bWVybyBkZSBFbGVtZW50b3MgZW4gdW5hIFZhcmlhYmxlDQoNClBhcmEgY29udGFyIGxvcyBlbGVtZW50b3MgZW4gdW5hIHZhcmlhYmxlIGVzcGVjw61maWNhIGRlbnRybyBkZWwgZGF0YWZyYW1lIHNlIGRlYmUgYWdyZWdhciBlbCBub21icmUgZGUgbGEgdmFyaWFibGUgZGVzcHXDqXMgZGUgZWwgbm9tYnJlIGRlIGxhIGJhc2UgZGUgZGF0b3MgeSBlbCBzaWdubyBgJGANCg0KYGBge3IgbGVuZ3RoIHNldHVwLCBlY2hvPVRSVUV9DQojIE7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGxhIHZhcmlhYmxlICd1YmljYWNpb24nDQpsZW5ndGgoZGYkdWJpY2FjaW9uKQ0KDQojIE7Dum1lcm8gZGUgZWxlbWVudG9zIGVuIGxhIHZhcmlhYmxlICdkaXN0YW5jaWEnDQpsZW5ndGgoZGYkZWRhZCkNCmBgYA0KDQpbKkxhcyB2YXJpYWJsZXMgYW5hbGl6YWRhcyBjdWVudGFuIGNvbiAyMDAgcmVnaXN0cm9zIGNhZGEgdW5hKl17LnVuZGVybGluZX0NCg0KIyMjIE5vbWJyZXMgZGUgbGFzIFZhcmlhYmxlcw0KDQpQYXJhIG9idGVuZXIgbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcyAoY29sdW1uYXMpIGVuIGVsIGRhdGFmcmFtZToNCg0KYGBge3IgbmFtZXMgc2V0dXAsIGVjaG89VFJVRX0NCiMgTm9tYnJlcyBkZSBsYXMgdmFyaWFibGVzDQpuYW1lcyhkZikNCmBgYA0KDQoqKmBuYW1lcyhkZilgKio6IERldnVlbHZlIHVuIHZlY3RvciBjb24gbG9zIG5vbWJyZXMgZGUgbGFzIGNvbHVtbmFzIGRlbCBkYXRhZnJhbWUgYGRmYC4NCg0KWypQYXJhIGVsIGVqZW1wbG8sIHNhYmVtb3MgcXVlIGxhIGJhc2UgY29udGFiYSBjb24gNCBjb2x1bW5hcyBvIHZhcmlhYmxlcywgeSBzdXMgbm9tYnJlcyBzb246IGNhbmRpZGF0b3MsIHViaWNhY2nDs24sIGVkYWQgeSBkaXN0YW5jaWEuKl17LnVuZGVybGluZX0NCg0KIyMjIFByaW1lcm9zIHkgw5psdGltb3MgUmVnaXN0cm9zIGRlIGxhIFRhYmxhDQoNClBhcmEgdmVyIGxvcyBwcmltZXJvcyB5IMO6bHRpbW9zIHJlZ2lzdHJvcyBkZWwgZGF0YWZyYW1lOg0KDQotICAgKipgaGVhZChkZilgKio6IE11ZXN0cmEgbGFzIHByaW1lcmFzIDYgZmlsYXMgZGVsIGRhdGFmcmFtZS4NCg0KLSAgICoqYGhlYWQoZGYsIDEwKWAqKjogTXVlc3RyYSBsYXMgcHJpbWVyYXMgMTAgZmlsYXMuDQoNCi0gICAqKmB0YWlsKGRmKWAqKjogTXVlc3RyYSBsYXMgw7psdGltYXMgNiBmaWxhcy4NCg0KLSAgICoqYHRhaWwoZGYsIDQpYCoqOiBNdWVzdHJhIGxhcyDDumx0aW1hcyA0IGZpbGFzLg0KDQpgYGB7ciBoZWFkIHNldHVwLCBlY2hvPVRSVUV9DQojIFByaW1lcm9zIHJlZ2lzdHJvcyBkZSBsYSB0YWJsYQ0KaGVhZChkZikNCmhlYWQoZGYsIDEwKSAgIyBQcmltZXJvcyAxMCByZWdpc3Ryb3MNCg0KIyDDmmx0aW1vcyByZWdpc3Ryb3MgZGUgbGEgdGFibGENCnRhaWwoZGYpDQp0YWlsKGRmLCA0KSAgICMgw5psdGltb3MgNCByZWdpc3Ryb3MNCmBgYA0KDQojIyMgRWxlbWVudG9zIEFsZWF0b3Jpb3MgZGUgbGEgVGFibGENCg0KUGFyYSBvYnRlbmVyIHVuYSBtdWVzdHJhIGFsZWF0b3JpYSBkZSByZWdpc3Ryb3MgZGVsIGRhdGFmcmFtZSwgdXRpbGl6YSBsYSBmdW5jacOzbiBgc29tZSgpYCBkZWwgcGFxdWV0ZSBgY2FyYDoNCg0KYGBge3Igc29tZSBzZXR1cCwgZWNobz1UUlVFfQ0KDQojIEluc3RhbGFyIGxpYnJlcsOtYSBjYXINCiNpbnN0YWxsLnBhY2thZ2VzKCJjYXIiKQ0KDQojIENhcmdhciBsYSBsaWJyZXLDrWEgY2FyDQpsaWJyYXJ5KGNhcikNCg0KI0RPUyBGT1JNQVMgREUgRUpFQ1VUQVIgTEEgRlVOQ0nDk04gU09NRQ0KDQojQS4gT2J0ZW5lciB1bmEgbXVlc3RyYSBhbGVhdG9yaWENCnNvbWUoZGYpDQoNCiNCLiBVc2FyIGxhIGZ1bmNpw7NuIGNvbiBlbCBwcmVmaWpvIGRlbCBwYXF1ZXRlIHBhcmEgZXZpdGFyIGNvbmZsaWN0b3MNCmNhcjo6c29tZShkZikNCmBgYA0KDQojIyMgSW5mb3JtYWNpw7NuIHNvYnJlIGxhIEJhc2UgZGUgRGF0b3MNCg0KUGFyYSBvYnRlbmVyIHVuYSB2aXNpw7NuIGdlbmVyYWwgZGUgbGEgZXN0cnVjdHVyYSBkZSBsb3MgZGF0b3MgeSBlbCBwb3JjZW50YWplIGRlIGRhdG9zIHBlcmRpZG9zOg0KDQotICAgKipgQWJzdHJhY3QoZGYpYCoqOiBQcm9wb3JjaW9uYSB1biByZXN1bWVuIGNvbiBlbCBwb3JjZW50YWplIGRlIGRhdG9zIHBlcmRpZG9zIHkgb3RyYXMgZXN0YWTDrXN0aWNhcyBkZXNjcmlwdGl2YXMuDQoNCi0gICAqKmBzdHIoZGYpYCoqOiBNdWVzdHJhIGxhIGVzdHJ1Y3R1cmEgaW50ZXJuYSBkZWwgZGF0YWZyYW1lIGBkZmAsIGluY2x1eWVuZG8gZWwgdGlwbyBkZSBjYWRhIGNvbHVtbmEgeSB1bmEgbXVlc3RyYSBkZSBsb3MgZGF0b3MuDQoNCmBgYHtyIEFic3RyYWN0IHNldHVwLCBlY2hvPVRSVUV9DQoNCg0KIyBJbmZvcm1hY2nDs24gZ2VuZXJhbCBzb2JyZSBsYSBiYXNlIGRlIGRhdG9zDQpzdHIoZGYpDQoNCmBgYA0KDQpbKkxhIGJhc2UgZGUgZGF0b3MgZGYgY29udGllbmUgMjAwIGNvbHVtbmFzIHggNCB2YXJpYWJsZXMqXXsudW5kZXJsaW5lfQ0KDQpbKkxhcyB2YXJpYWJsZXMgc29uOiBjYW5kaWRhdG9zLCB1YmljYWNpw7NuLCBlZGFkIHkgZGlzdGFuY2lhLipdey51bmRlcmxpbmV9DQoNClsqUGFyYSBlbCBjYXNvIGRlIGNhbmRpZGF0b3MsIGVzIHVuYSB2YXJpYWJsZSBkZWwgdGlwbyBjYXLDoWN0ZXIgeSBzdXMgcHJpbWVyb3MgcmVnaXN0cm9zIHRvbWFuIGxvcyB2YWxvcmVzICJQdXRpbiIsIlB1dGluIiwiUHV0aW4iLCJVcmliZSIqXXsudW5kZXJsaW5lfQ0KDQpbKlBhcmEgZWwgY2FzbyBkZSBsYSB2YXJpYWJsZSBkaXN0YW5jaWEsIGVzIHVuYSB2YXJpYWJsZSBkZSB0aXBvIG7Dum1lcmljYSwgeSBjdWVudGEgY29uIGRlY2ltYWxlcy4qXXsudW5kZXJsaW5lfQ0KDQpgYGB7ciBzdHIgc2V0dXAsIGVjaG89VFJVRX0NCiMgSW5zdGFsYXIgeSBjYXJnYXIgbGlicmVyw61hIERlc2NUb29scw0KI2luc3RhbGwucGFja2FnZXMoIkRlc2NUb29scyIpDQpsaWJyYXJ5KERlc2NUb29scykNCg0KIyBJbmZvcm1hY2nDs24gZ2VuZXJhbCBzb2JyZSBsYSBiYXNlIGRlIGRhdG9zDQoNCkFic3RyYWN0KGRmKQ0KYGBgDQoNClsqTGFzIHZhcmlhYmxlcyBzb246IGNhbmRpZGF0b3MsIHViaWNhY2nDs24sIGVkYWQgeSBkaXN0YW5jaWEuKl17LnVuZGVybGluZX0NCg0KWypQYXJhIGVsIGNhc28gZGUgbGEgdmFyaWFibGUgZGlzdGFuY2lhLCBlcyB1bmEgdmFyaWFibGUgZGUgdGlwbyBuw7ptZXJpY2EsIHkgY3VlbnRhIGNvbiBkZWNpbWFsZXMsIHNlIGN1ZW50YW4gY29uIDcgcmVnaXN0cm9zIGVuIE5BIChlcyBkZWNpciwgc2luIGluZm9ybWFjacOzbikgbG8gcXVlIGNvcnJlc3BvbmRlIGFsIDMuNSUgZGUgbG9zIDIwMCByZWdpc3Ryb3MgZGUgbGEgYmFzZSBkZSBkYXRvcy4qXXsudW5kZXJsaW5lfQ0KDQojIyMgQ2xhc2UgZGUgdW4gb2JqZXRvIHkgZGUgc3VzIHZhcmlhYmxlcw0KDQpQYXJhIHZlcmlmaWNhciBsYSBjbGFzZSBkZSB1biBkYXRhZnJhbWUgeSBzdXMgdmFyaWFibGVzOg0KDQotICAgKipgY2xhc3MoZGYpYCoqOiBEZXZ1ZWx2ZSBsYSBjbGFzZSBkZWwgb2JqZXRvIGBkZmAgKGRlYmVyw61hIHNlciBgImRhdGEuZnJhbWUiYCkuDQoNCi0gICAqKmBjbGFzcyhkZiRjYW5kaWRhdG9zKWAsIGBjbGFzcyhkZiR1YmljYWNpb24pYCwgZXRjLioqOiBEZXZ1ZWx2ZW4gbGEgY2xhc2UgZGUgbGFzIGNvbHVtbmFzIGVzcGVjw61maWNhcy4NCg0KYGBge3IgY2xhc3Mgc2V0dXAsIGVjaG89VFJVRX0NCiMgQ2xhc2UgZGVsIGRhdGFmcmFtZSANCmNsYXNzKGRmKQ0KDQojIENsYXNlIGRlIGxhcyAgdmFyaWFibGVzDQpjbGFzcyhkZiRjYW5kaWRhdG9zKQ0KY2xhc3MoZGYkdWJpY2FjaW9uKQ0KY2xhc3MoZGYkZGlzdGFuY2lhKQ0KY2xhc3MoZGYkZWRhZCkNCmBgYA0KDQojIyMgVmFsb3JlcyDDum5pY29zIGRlIGxhcyB2YXJpYWJsZXMNCg0KUGFyYSBvYnRlbmVyIGxvcyB2YWxvcmVzIMO6bmljb3MgZW4gdW5hIHZhcmlhYmxlIGRpc2NyZXRhLCBlcyBkZWNpciwgbG9zIHZhbG9yZXMgbyBjYXRlZ29yw61hcyBxdWUgdG9tYToNCg0KKipgdW5pcXVlKGRmJHViaWNhY2lvbilgKio6IERldnVlbHZlIGxvcyB2YWxvcmVzIMO6bmljb3MgZW4gbGEgY29sdW1uYSBgdWJpY2FjaW9uYC4NCg0KYGBge3IgdW5pcXVlIHNldHVwLCBlY2hvPVRSVUV9DQojIFZhbG9yZXMgw7puaWNvcyBlbiBsYXMgdmFyaWFibGVzDQp1bmlxdWUoZGYkdWJpY2FjaW9uKQ0KdW5pcXVlKGRmJGNhbmRpZGF0b3MpDQojI3VuaXF1ZShkZiRkaXN0YW5jaWEpDQp1bmlxdWUoZGYkZWRhZCkNCg0KYGBgDQoNCiMjIyBSZXN1bWVuIGRlIGVzdGFkw61zdGljYXMgeSBmcmVjdWVuY2lhcw0KDQpQYXJhIG9idGVuZXIgdW4gcmVzdW1lbiBkZSBlc3RhZMOtc3RpY2FzIGRlc2NyaXB0aXZhcyB5IGZyZWN1ZW5jaWFzOg0KDQotICAgKipgc3VtbWFyeShkZilgKio6IFByb3BvcmNpb25hIHVuIHJlc3VtZW4gZXN0YWTDrXN0aWNvIGLDoXNpY28gZGUgY2FkYSBjb2x1bW5hIGRlbCBkYXRhZnJhbWUuDQoNCi0gICAqKmBEZXNjKGRmKWAqKjogT2ZyZWNlIHVuIHJlc3VtZW4gZGV0YWxsYWRvIHkgZ3LDoWZpY29zIHBhcmEgY2FkYSB2YXJpYWJsZSBlbiBlbCBkYXRhZnJhbWUuDQoNCi0gICAqKmBkZlN1bW1hcnkoZGYpYCoqOiBSZXN1bWVuIGRldGFsbGFkbyBjb24gZXN0YWTDrXN0aWNhcyB5IGdyw6FmaWNvcyB1c2FuZG8gZWwgcGFxdWV0ZSBgc3VtbWFyeXRvb2xzYC4NCg0KYGBge3Igc3VtbWFyeSBzZXR1cCwgZWNobz1UUlVFfQ0KIyBSZXN1bWVuIGRlIGVzdGFkw61zdGljYXMgZGVzY3JpcHRpdmFzDQpzdW1tYXJ5KGRmKQ0KYGBgDQoNClsqRGVzY3JpYmUgbGFzIHZhcmlhYmxlcyBuw7ptZXJpY2FzIGRlIGxhIGJhc2UgZGUgZGF0b3MsIHBvciBlamVtcGxvLCBsYSBlZGFkIG3DrW5pbWEgZGUgbG9zIGVuY3Vlc3RhZG9zIGVzIDEgYcOxbyBtaWVudHJhcyBxdWUgbGEgbcOheGltYSBlcyBkZSAxMjcgYcOxb3MsIGNvbiB1bmEgbWVkaWFuYSBkZSA0Mi41IGHDsW9zIHkgdW5hIG1lZGlhIGRlIDQ1LjM3IGHDsW9zLipdey51bmRlcmxpbmV9DQoNClsqUGFyYSBlbCBjYXNvIGRlIGxhcyB2YXJpYWJsZXMgY2FuZGlkYXRvcyB5IHViaWNhY2nDs24sIGNvbW8gZXN0YXMgc29uIGNhcmFjdGVyZXMgc29sbyBtdWVzdHJhIHF1ZSB0aWVuZW4gMjAwIHJlZ2lzdHJvcy4qXXsudW5kZXJsaW5lfQ0KDQoqKsK/UXXDqSBwYXNhIHNpIHNlIGNvbnZpZXJ0ZW4gbGFzIHZhcmlhYmxlcyBjYXJhY3RlciBhIGZhY3Rvcj8qKg0KDQpgYGB7ciBzdW1tYXJ5IGZhY3RvciBzZXR1cCwgZWNobz1UUlVFfQ0KDQojQ29udmVydGlyIHVuYSB2YXJpYWJsZSBhIGZhY3Rvcg0KZGYkdWJpY2FjaW9uIDwtIGFzLmZhY3RvcihkZiR1YmljYWNpb24pIA0KZGYkY2FuZGlkYXRvcyA8LSBhcy5mYWN0b3IoZGYkY2FuZGlkYXRvcykgDQoNCiMgUmVzdW1lbiBkZSBlc3RhZMOtc3RpY2FzIGRlc2NyaXB0aXZhcw0Kc3VtbWFyeShkZikNCmBgYA0KDQpbKkFsIGNvbnZlcnRpciBhIGZhY3RvciwgZXMgcG9zaWJsZSBhbmFsaXphciBsYXMgdmFyaWFibGVzIGNhbmRpZGF0b3MgeSB1YmljYWNpw7NuLiBBaG9yYSBlbCBjb21hbmRvIHN1bW1hcnkgbXVlc3RyYSBsYXMgZnJlY3VlbmNpYXMgZGUgY2FkYSB1bmEgZGUgbGFzIGFsdGVybmF0aXZhcyBxdWUgdG9tYSBjYWRhIHZhcmlhYmxlLlwNCipdey51bmRlcmxpbmV9DQoNCmBgYHtyIERlc2Mgc2V0dXAsIGVjaG89VFJVRX0NCg0KIyBSZXN1bWVuIGRlIGZyZWN1ZW5jaWFzIHkgZ3LDoWZpY29zDQpsaWJyYXJ5KERlc2NUb29scykNCkRlc2MoZGYpDQpgYGANCg0KKipgRGVzYyhkZilgKio6IE9mcmVjZSB1biByZXN1bWVuIGRldGFsbGFkbyB5IGdyw6FmaWNvcyBwYXJhIGNhZGEgdmFyaWFibGUgZW4gZWwgZGF0YWZyYW1lLg0KDQpgYGB7ciBzdW1tYXJ5dG9vbHMgc2V0dXAsIGVjaG89VFJVRX0NCiMgT3Ryb3MgcGFxdWV0ZXMgZGUgZnJlY3VlbmNpYXMNCiNpbnN0YWxsLnBhY2thZ2VzKCJzdW1tYXJ5dG9vbHMiKQ0KbGlicmFyeShzdW1tYXJ5dG9vbHMpDQpkZlN1bW1hcnkoZGYsIA0KICAgICAgICAgIHZhcm51bWJlcnMgPSBGQUxTRSwgDQogICAgICAgICAgdmFsaWQuY29sID0gRkFMU0UsIA0KICAgICAgICAgIGdyYXBoLm1hZ25pZiA9IDAuNzYpDQpgYGANCg0KKipgZGZTdW1tYXJ5KGRmKWAqKjogUmVzdW1lbiBkZXRhbGxhZG8gY29uIGVzdGFkw61zdGljYXMgeSBncsOhZmljb3MgdXNhbmRvIGVsIHBhcXVldGUgYHN1bW1hcnl0b29sc2AuDQoNClBhcmEgZ2VuZXJhciB5IHZpc3VhbGl6YXIgZWwgcmVzdW1lbiBhbnRlcmlvciBkZSBzdW1tYXJ5dG9vbHMgZW4gZm9ybWF0byBIVE1MOg0KDQoqKmBodG1sdG9vbHM6Omh0bWxfcHJpbnQoaHRtbF9zdW1tYXJ5KWAqKjogSW1wcmltZSBlbCByZXN1bWVuIGVuIGZvcm1hdG8gSFRNTA0KDQpgYGB7ciBodG1sIHNldHVwLCBlY2hvPVRSVUV9DQojIEdlbmVyYXIgcmVzdW1lbiBlbiBIVE1MDQpyZXN1bWVuIDwtIGRmU3VtbWFyeShkZiwgDQogICAgICAgICAgICAgICAgICAgIHZhcm51bWJlcnMgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgIHZhbGlkLmNvbCA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICAgZ3JhcGgubWFnbmlmID0gMC43NikNCmh0bWxfc3VtbWFyeSA8LSBwcmludChyZXN1bWVuLCBtZXRob2QgPSAicmVuZGVyIikNCmh0bWx0b29sczo6aHRtbF9wcmludChodG1sX3N1bW1hcnkpDQpgYGANCg0KIVtdKGltYWdlcy9jbGlwYm9hcmQtMTI3NTQzODkzNy5wbmcpDQoNCioqYGd0X3BsdF9zdW1tYXJ5KGRmLCB0aXRsZSA9ICJSZXN1bWVuIGRlIGxhIGJhc2UgZGUgZGF0b3MiKWAqKjogQ3JlYSB1bmEgdGFibGEgcmVzdW1lbiBjb24gdW4gdMOtdHVsbyB1dGlsaXphbmRvIGVsIHBhcXVldGUgYGd0RXh0cmFzYC4NCg0KYGBge3J9DQojIEluc3RhbGFyIHkgY2FyZ2FyIGd0RXh0cmFzDQojaW5zdGFsbC5wYWNrYWdlcygiZ3RFeHRyYXMiKQ0KbGlicmFyeShndEV4dHJhcykNCg0KIyBHZW5lcmFyIHVuYSB0YWJsYSByZXN1bWVuDQpndF9wbHRfc3VtbWFyeShkZiwgdGl0bGUgPSAiUmVzdW1lbiBkZSBsYSBiYXNlIGRlIGRhdG9zIikNCmBgYA0KDQojIyBDcmVhY2nDs24gZGUgdmFyaWFibGVzDQoNCmB4IDwtIDVgOiBDcmVhIHVuYSB2YXJpYWJsZSBgeGAgeSBsZSBhc2lnbmEgZWwgdmFsb3IgYDVgLg0KDQpgYGB7ciBjcmVhIHZhciwgZWNobz1UUlVFfQ0KDQp4IDwtIDUNCg0KDQpgYGANCg0KYHJtKHgpYDogRWxpbWluYSBsYSB2YXJpYWJsZSBgeGAgZGVsIGVudG9ybm8gZGUgdHJhYmFqby4NCg0KYGBge3IgZWxpLCBlY2hvPVRSVUV9DQoNCnJtKHgpDQoNCmBgYA0KDQpgeCA9IDVgOiBBc2lnbmEgZWwgdmFsb3IgYDVgIGEgYHhgLiBFcyB1bmEgZm9ybWEgYWx0ZXJuYXRpdmEgZGUgYXNpZ25hY2nDs24sIGF1bnF1ZSBlbiBSIGVzIHByZWZlcmlibGUgdXNhciBgPC1gDQoNCmBgYHtyIGNyZWEyLCBlY2hvPVRSVUV9DQoNCnggPSA1DQoNCmBgYA0KDQojIyMgwqFPam8hIERpZmVyZW5jaWEgZW50cmUgb3BlcmFkb3JlcyBkZSBBc2lnbmFjacOzbiB5IENvbXBhcmFjacOzbg0KDQpgYGB7ciBhc2ksIGVjaG89VFJVRX0NCiMgwr9FcyBsbyBtaXNtbz8NCnggPCAgLTUNCmBgYA0KDQpFc3RlIGPDs2RpZ28gbm8gdGllbmUgdW4gc2lnbmlmaWNhZG8gbMOzZ2ljbyBjbGFybyBkZWJpZG8gYWwgZXNwYWNpbyBlbnRyZSBgPGAgeSBgLTVgLiBFbiBSLCBgeCA8IC01YCBjb21wYXJhcsOtYSBzaSBgeGAgZXMgbWVub3IgcXVlIGAtNWAuIEVsIGVzcGFjaW8gZ2VuZXJhIGNvbmZ1c2nDs24sIHBvciBsbyBxdWUgbm8gc2UgZGViZXLDrWEgdXNhciBhc8OtLg0KDQojIyMgU2Vuc2liaWxpZGFkIGEgTWF5w7pzY3VsYXMgeSBNaW7DunNjdWxhcw0KDQpgYGB7ciBtYXl1LCBlY2hvPVRSVUUsIHdhcm5pbmc9VFJVRX0NCiMgUXXDqSBwYXNhIGFxdcOtDQp4IDwtIDEwMA0KI1gNCiNSIGRpc3Rpbmd1ZSBlbnRyZSBtYXnDunNjdWxhcyB5IG1pbsO6c2N1bGFzDQojISBvYmpldG8gJ1gnIG5vIGVuY29udHJhZG8NCngNCmBgYA0KDQpSIGVzIHNlbnNpYmxlIGEgbGFzIG1hecO6c2N1bGFzIHkgbWluw7pzY3VsYXMuIGB4YCB5IGBYYCBzZSBjb25zaWRlcmFuIHZhcmlhYmxlcyBkaWZlcmVudGVzLg0KDQojIyBEaWZlcmVudGVzIEZvcm1hcyBkZSBDcmVhciB1biBWZWN0b3INCg0KYGBge3IgdmVjLCBlY2hvPVRSVUV9DQoNCndlaWdodCA8LSBjKDEwLDIwLDMwLDQwKQ0KYXNzaWduICgid2VpZ2h0MSIsIGMoMTAsMjAsMzAsNDApKSAjT3RyYSBmb3JtYSBkZSBjcmVhciB1biB2ZWN0b3INCndlaWdodCA9IGMoMTAsMjAsMzAsNDApDQpgYGANCg0KLSAgIGB3ZWlnaHQgPC0gYygxMCwyMCwzMCw0MClgOiBDcmVhIHVuIHZlY3RvciBsbGFtYWRvIGB3ZWlnaHRgLg0KDQotICAgYGFzc2lnbigid2VpZ2h0MSIsIGMoMTAsMjAsMzAsNDApKWA6IE90cmEgZm9ybWEgZGUgY3JlYXIgdW4gdmVjdG9yLCB1c2FuZG8gbGEgZnVuY2nDs24gYGFzc2lnbmAuDQoNCi0gICBgd2VpZ2h0ID0gYygxMCwyMCwzMCw0MClgOiBPdHJhIGZvcm1hIGRlIGFzaWduYXIgdmFsb3JlcyBhIHVuIHZlY3Rvciwgc2ltaWxhciBhbCB1c28gZGUgYDwtYC4NCg0KIyMgSW5jcmVtZW50byBkZSBWYXJpYWJsZXMNCg0KYGBge3IgaW5jciwgZWNobz1UUlVFfQ0KIyDCv1F1w6kgcGFzYSBhcXXDrT8NCm4gPC0gMSAgICAgICAgIzENCm4gPC0gbiArIDEgICAgIzEgKyAxIA0KbiAgICAgICAgICAgICAjMg0KYGBgDQoNCi0gICBgbiA8LSAxYDogQXNpZ25hIGAxYCBhIGBuYC4NCg0KLSAgIGBuIDwtIG4gKyAxYDogSW5jcmVtZW50YSBgbmAgZW4gYDFgLCBwb3IgbG8gcXVlIGBuYCBhaG9yYSBlcyBgMmAuDQoNCi0gICBgbmA6IE11ZXN0cmEgZWwgdmFsb3IgYWN0dWFsIGRlIGBuYCwgcXVlIGVzIGAyYC4NCg0KIyMgUmVkb25kZW8gZGUgTsO6bWVyb3MNCg0KYGBge3IgcmVkbywgZWNobz1UUlVFfQ0KZmxvb3IoNS43KSAjIyMgQXByb3hpbWEgaGFjaWEgYWJham8NCmNlaWxpbmcoNS43KSAgIyMjQXByb3hpbWEgaGFjaWEgYXJyaWJhDQpyb3VuZCg1Ljc3NywgZGlnaXRzID0gMikNCnJvdW5kKDUuNzcyLCBkaWdpdHMgPSAyKQ0Kcm91bmQoNS43NzIxNTQ1MTM2MTAsIGRpZ2l0cyA9IDUpDQpgYGANCg0KLSAgIGBmbG9vcig1LjcpYDogQXByb3hpbWEgNS43IGhhY2lhIGFiYWpvIChkZXZ1ZWx2ZSBgNWApLg0KDQotICAgYGNlaWxpbmcoNS43KWA6IEFwcm94aW1hIDUuNyBoYWNpYSBhcnJpYmEgKGRldnVlbHZlIGA2YCkuDQoNCi0gICBgcm91bmQoNS43NzcsIGRpZ2l0cyA9IDIpYDogUmVkb25kZWEgYSAyIGRlY2ltYWxlcyAoZGV2dWVsdmUgYDUuNzhgKS4NCg0KLSAgIGByb3VuZCg1Ljc3MjE1NDUxMzYxMCwgZGlnaXRzID0gNSlgOiBSZWRvbmRlYSBhIDUgZGVjaW1hbGVzIChkZXZ1ZWx2ZSBgNS43NzIxNWApLg0KDQojIyBDcmVhY2nDs24gZGUgU2VjdWVuY2lhcw0KDQpgYGB7ciBzZXEsIGVjaG89VFJVRX0NCiMgQ8OzbW8gY3JlYXIgc2VjdWVuY2lhcyBkZSBuw7ptZXJvcw0Kc2VxKGZyb20gPSAxLCB0byA9IDksIGJ5ID0gMikgIyMjU2VjdWVuY2lhIGRlc2RlIDEgaGFzdGEgOSBjYWRhIDIjIyMNCiPCv0PDs21vIGxvIGFzaWduYXLDrWEgYWwgdmVjdG9yIGxsYW1hZG8gIkhvIj8NCkhvID0gc2VxKGZyb20gPSAxLCB0byA9IDksIGJ5ID0gMikNCkhvIDwtIHNlcShmcm9tID0gMSwgdG8gPSA5LCBieSA9IDIpDQpgYGANCg0KLSAgIGBzZXEoZnJvbSA9IDEsIHRvID0gOSwgYnkgPSAyKWA6IEdlbmVyYSB1bmEgc2VjdWVuY2lhIGRlc2RlIGAxYCBoYXN0YSBgOWAsIGVuIGluY3JlbWVudG9zIGRlIGAyYC4NCg0KLSAgIGBIbyA8LSBzZXEoZnJvbSA9IDEsIHRvID0gOSwgYnkgPSAyKWA6IEFzaWduYSBsYSBzZWN1ZW5jaWEgZ2VuZXJhZGEgYWwgdmVjdG9yIGBIb2AuDQoNCiMjIyBPdHJhcyBGb3JtYXMgZGUgR2VuZXJhciBTZWN1ZW5jaWFzDQoNCmBgYHtyIHNlcTEsIGVjaG89VFJVRX0NCjEwOjE4IA0KMTg6MTANCi0wLjU6OC41DQpzZXEoMCwgMS41LCAwLjIpDQpzZXEoMS41LCAwLCAtMC4yKQ0KDQpgYGANCg0KLSAgIGAxMDoxOGA6IEdlbmVyYSB1bmEgc2VjdWVuY2lhIGRlbCBgMTBgIGFsIGAxOGAuDQoNCi0gICBgMTg6MTBgOiBHZW5lcmEgdW5hIHNlY3VlbmNpYSBpbnZlcnNhIGRlbCBgMThgIGFsIGAxMGAuDQoNCi0gICBgLTAuNTo4LjVgOiBHZW5lcmEgdW5hIHNlY3VlbmNpYSBkZSBgLTAuNWAgYSBgOC41YC4NCg0KLSAgIGBzZXEoMCwgMS41LCAwLjIpYDogR2VuZXJhIHVuYSBzZWN1ZW5jaWEgZGUgYDBgIGEgYDEuNWAgZW4gaW5jcmVtZW50b3MgZGUgYDAuMmAuDQoNCi0gICBgc2VxKDEuNSwgMCwgLTAuMilgOiBHZW5lcmEgdW5hIHNlY3VlbmNpYSBkZSBgMS41YCBhIGAwYCBlbiBkZWNyZW1lbnRvcyBkZSBgMC4yYC4NCg0KIyMgU2VjdWVuY2lhcyBjb24gbGEgRnVuY2nDs24gYHNlcXVlbmNlYA0KDQpgYGB7ciBzZXF1ZW5jZSwgZWNobz1UUlVFfQ0Kc2VxdWVuY2UoNSkgICMjI07Dum1lcm9zIGRlIDEgYSA1IyMjDQpzZXF1ZW5jZSg1OjEpICMjI07Dum1lcm9zIGRlIDEgYSA1LCBsdWVnbyBkZSAxIGEgNCwgbHVlZ28gZGUgMSBhIDMsIGRlIDEgYSAyLCB5IGZpbmFsbWVudGUgMSMjIw0Kc2VxdWVuY2UoYyg1LDIsNCkpICMjI1NlY3VlbmNpYSBkZSAxIGEgNSwgZGUgMSBhIDIsIGRlIDEgYSA0IyMjDQoNCmBgYA0KDQotICAgYHNlcXVlbmNlKDUpYDogR2VuZXJhIHVuYSBzZWN1ZW5jaWEgZGUgYDFgIGEgYDVgLg0KDQotICAgYHNlcXVlbmNlKDU6MSlgOiBHZW5lcmEgc2VjdWVuY2lhcyBkZXNkZSBgMWAgaGFzdGEgYDVgLCBsdWVnbyBgMWAgaGFzdGEgYDRgLCB5IGFzw60gc3VjZXNpdmFtZW50ZS4NCg0KLSAgIGBzZXF1ZW5jZShjKDUsMiw0KSlgOiBHZW5lcmEgc2VjdWVuY2lhcyBkZSBgMWAgYSBgNWAsIGRlIGAxYCBhIGAyYCwgeSBkZSBgMWAgYSBgNGAuDQoNCiMjIFJlcGV0aWNpb25lcw0KDQpgYGB7ciByZXAsIGVjaG89VFJVRX0NCnJlcCg5LDUpDQpyZXAoMTo0LCAyKSAjIyMgUmVwZXRpciAxLDIsMyw0IGRvcyB2ZWNlcyAjIyMNCnJlcCgxOjQsIGVhY2ggPSAyKSAjIyNSZXBpdGUgMSBkb3MgdmVjZXMsIDIgZG9zIHZlY2VzLC4uLiMjIw0KcmVwKDE6NCwgZWFjaCA9IDIsIHRpbWVzID0gMykNCnJlcCgxOjQsIDE6NCkgIyMjIFJlcGl0ZSAxIHVuYSB2ZXosIDIgZG9zIHZlY2VzLi4uIyMjDQpyZXAoMTo0LCBjKDQsMSw1LDIpKSAjIyNSZXBpdGUgMSBjdWF0cm8gdmVjZXMsIDIgdW5hIHZlei4uLiMjIw0KDQpgYGANCg0KLSAgIGByZXAoOSw1KWA6IFJlcGl0ZSBlbCBgOWAgY2luY28gdmVjZXMuDQoNCi0gICBgcmVwKDE6NCwgMilgOiBSZXBpdGUgbGEgc2VjdWVuY2lhIGAxLDIsMyw0YCBkb3MgdmVjZXMuDQoNCi0gICBgcmVwKDE6NCwgZWFjaCA9IDIpYDogUmVwaXRlIGNhZGEgbsO6bWVybyBkb3MgdmVjZXMuDQoNCi0gICBgcmVwKDE6NCwgMTo0KWA6IFJlcGl0ZSBlbCBgMWAgdW5hIHZleiwgZWwgYDJgIGRvcyB2ZWNlcywgeSBhc8OtIHN1Y2VzaXZhbWVudGUuDQoNCiMjIFZlY3RvcmVzIHkgQWNjZXNvIGEgRWxlbWVudG9zDQoNCmBgYHtyIGFjY2UsIGVjaG89VFJVRX0NCmEgPC0gYygxLCAyLCA1LCAzLCA2LCAtMiwgNCkNCmIgPC0gYygib25lIiwgInR3byIsICJ0aHJlZSIpDQpjIDwtIGMoVFJVRSwgVFJVRSwgVFJVRSwgRkFMU0UsIFRSVUUsIEZBTFNFKQ0KDQp0eXBlb2YoYSk7IHR5cGVvZihiKTsgdHlwZW9mKGMpICMjI1RpcG8gZGUgbG9zIGVsZW1lbnRvcyBxdWUgY29tcG9uZW4gY2FkYSB2ZWN0b3IjIyMNCg0KcSA8LSAxMDoxNjsgcQ0KDQpxWzNdICAgIyMjRWxlbWVudG8gMyBkZWwgdmVjdG9yIHEjIyMNCnFbYygyLDQsNyldICAjIyNFbGVtZW50b3MgMiw0LDcgZGVsIHZlY3RvciBxIyMjDQoNCnkgPC0gYygxMCwgMTEsIDEyLCAxMywgMTQsIDE1LCAxNikNCnlbMjo1XSAgICMjI0VsZW1lbnRvcyAyIGFsIDUgZGVsIHZlY3RvciB5IyMjDQp5Wy0zXSAgICAjIyNFbGltaW5hIGVsIHRlcmNlciBlbGVtZW50byBkZWwgdmVjdG9yIHkjIyMNCg0KciA8LSA0LjMNCnJbMl0NCnogPC0gclstMV0NCmxlbmd0aCh6KQ0KDQpgYGANCg0KLSAgIGB0eXBlb2YoYSlgLCBgdHlwZW9mKGIpYCwgYHR5cGVvZihjKWA6IE11ZXN0cmEgZWwgdGlwbyBkZSBsb3MgZWxlbWVudG9zIGVuIGxvcyB2ZWN0b3JlcyBgYWAsIGBiYCwgeSBgY2AuDQoNCi0gICBgcVszXWA6IEFjY2VkZSBhbCB0ZXJjZXIgZWxlbWVudG8gZGVsIHZlY3RvciBgcWAuDQoNCi0gICBgeVstM11gOiBFbGltaW5hIGVsIHRlcmNlciBlbGVtZW50byBkZWwgdmVjdG9yIGB5YC4NCg0KIyMgTWF0cmljZXMNCg0KYGBge3IgbWEsIGVjaG89VFJVRX0NCnkgPC0gbWF0cml4KDE6MjAsIG5yb3cgPSA1KTsgeSAgIyMjTWF0cml6IGVsZW1lbnRvcyAxIGEgMjAgY29uIDUgZmlsYXMsc2UgbGxlbmEgcG9yIGNvbHVtbmFzIyMjDQp5IDwtIG1hdHJpeCgxOjIwLCBucm93ID0gNSwgYnlyb3cgPSBUUlVFKTsgeSAgIyMjTWF0cml6IGVsZW1lbnRvcyAxIGEgMjAgY29uIDUgZmlsYXMsc2UgbGxlbmEgcG9yIGZpbGFzIyMjDQpjZWxscyA8LSBjKDEsIDI2LCAyNCwgNjgpDQpybmFtZXMgPC0gYygiUjEiLCAiUjIiKTsgY25hbWVzIDwtIGMoIkMxIiwgIkMyIikgIyMjVmVjdG9yIG5vbWJyZXMgZmlsYXMsIHZlY3RvciBub21icmVzIGNvbHVtbmFzIyMjDQpteW1hdHJpeCA8LSBtYXRyaXgoY2VsbHMsIG5yb3cgPSAyLCBuY29sID0gMiwgYnlyb3cgPSBUUlVFLCAgIyMjQ3JlYSB1bmEgbWF0cml6IGNvbiBsb3MgZWxlbWVudG9zIGRlbCB2ZWN0b3IgY2VsbHMgcG9yIGZpbGFzLCBjb24gMiBmaWxhcywgMiBjb2x1bW5hcywgeSBhc2lnbmEgbm9tYnJlcyMjIw0KICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChybmFtZXMsIGNuYW1lcykpDQpteW1hdHJpeA0KDQpteW1hdHJpeCA8LSBtYXRyaXgoY2VsbHMsIG5yb3cgPSAyLCBuY29sID0gMiwgYnlyb3cgPSBGQUxTRSwgIyMjQ3JlYSB1bmEgbWF0cml6IGNvbiBsb3MgZWxlbWVudG9zIGRlbCB2ZWN0b3IgY2VsbHMgcG9yIGNvbHVtbmFzLCBjb24gMiBmaWxhcywgMiBjb2x1bW5hcywgeSBhc2lnbmEgbm9tYnJlcyMjIw0KICAgICAgICAgICAgICAgICAgIGRpbW5hbWVzID0gbGlzdChybmFtZXMsIGNuYW1lcykpDQpteW1hdHJpeA0KDQoNCmBgYA0KDQotICAgYHkgPC0gbWF0cml4KDE6MjAsIG5yb3cgPSA1KWA6IENyZWEgdW5hIG1hdHJpeiBjb24gZWxlbWVudG9zIGRlIGAxYCBhIGAyMGAgZW4gYDVgIGZpbGFzLCBsbGVuYW5kbyBwb3IgY29sdW1uYXMuDQoNCi0gICBgbXltYXRyaXhgOiBDcmVhIHVuYSBtYXRyaXogZGUgYDJ4MmAgdXNhbmRvIGVsIHZlY3RvciBgY2VsbHNgIHkgYXNpZ25hIG5vbWJyZXMgYSBsYXMgZmlsYXMgeSBjb2x1bW5hcy4NCg0KIyMgTGVjdHVyYSBkZSBEYXRvcyBkZXNkZSBsYSBDb25zb2xhDQoNCmBgYHtyIGNvbiwgZWNobz1UUlVFfQ0KeSA8LSBzY2FuKCkgIyMjUmVhZCBkYXRhIGludG8gYSB2ZWN0b3Igb3IgbGlzdCBmcm9tIHRoZSBjb25zb2xlIG9yIGZpbGUuIFBpZGUgaW50cm9kdWNpciBsb3MgZGF0b3MjIyMNCg0KYGBgDQoNCi0gICBgc2NhbigpYDogUGVybWl0ZSBsZWVyIGRhdG9zIGRlc2RlIGxhIGNvbnNvbGEgbyB1biBhcmNoaXZvIGRpcmVjdGFtZW50ZSBhIHVuIHZlY3RvciBvIGxpc3RhLg0KDQojIyBDcmVhY2nDs24gZGUgU2VjdWVuY2lhcyB5IEFjY2VzbyBhIEVsZW1lbnRvcw0KDQpgYGB7ciBhY2Nlc28sIGVjaG89VFJVRX0NCih4IDwtIHNlcSgxLCAyMCwgYnkgPSAyKSkNCih5IDwtIHJlcCgzLCA0KSkgICMjI1JlcGV0aXIgNCB2ZWNlcyBlbCBuw7ptZXJvIDMjIyMNCih6IDwtIGMoeSwgeCkpICAgIyMjVW5pw7NuIGRlIGRvcyB2ZWN0b3JlcyMjIw0KKHggPC0gMTAwOjExMCkgICMjI0NyZWEgdW4gdmVjdG9yIGRlc2RlIDEwMCBoYXN0YSAxMTAjIyMNCg0KaSA8LSBjKDEsIDMsIDIpICMjI1ZlY3RvciMjIw0KeFtpXSAgICAgICAgICAgICMjI1RyYWUgbGFzIHBvc2ljaW9uZXMgY3JlYWRhcyBlbiBlbCB2ZWN0b3IgIGkgZGVsIHZlY3RvciB4IyMjDQpqIDwtIGMoLTEsIC0yLCAtMykNCnhbal0gICAgICAgICAgICAjIyNFbGltaW5hIGxhcyBwb3NpY2lvbmVzIGNyZWFkYXMgZW4gZWwgdmVjdG9yIGogZGVsIHZlY3RvciB4IyMjDQoNCmBgYA0KDQotICAgYHNlcSgxLCAyMCwgYnkgPSAyKWA6IEdlbmVyYSB1bmEgc2VjdWVuY2lhIGRlc2RlIGAxYCBoYXN0YSBgMjBgLCBlbiBpbmNyZW1lbnRvcyBkZSBgMmAuDQoNCi0gICBgcmVwKDMsIDQpYDogUmVwaXRlIGVsIG7Dum1lcm8gYDNgIGN1YXRybyB2ZWNlcy4NCg0KLSAgIGB4W2ldYDogQWNjZWRlIGEgbGFzIHBvc2ljaW9uZXMgaW5kaWNhZGFzIHBvciBgaWAgZW4gZWwgdmVjdG9yIGB4YC4NCg0KLSAgIGB4W2pdYDogRWxpbWluYSBsYXMgcG9zaWNpb25lcyBpbmRpY2FkYXMgcG9yIGBqYCBlbiBlbCB2ZWN0b3IgYHhgLg0K