Marcos Alberto Rosas Lara

Introducción

Microsoft Excel es la hoja de cálculo más utilizada en el mundo, algunos sitios de dependencias de gobierno como el INEGI, SCT, etc. Nos permiten exportar información en este tipo de archivos, asimismo son muchísimas las empresas y personas que utilizan este programa para guardar sus bases de datos.

De ahí lo importante que conozcamos la manera de importar y exportar información desde R a archivos de Microsoft Excel.

Aunado a la función de exportar información y gráficos a archivos de Excel, se tiene la posibilidad de crear estos archivos y editarlos. El presente trabajo pretende dar una guía rápida en la utilización del paquete XLSX para R.

Requerimientos del sistema

Para poder empezar a utilizar el paquete XLSX en R, se necesita tener previamente instalada la versión de 64 bits de java y los paquetes para R: rJava y xlsxjars.

Instalar xlsx en R

para instalar xlsx se puede utilizar el siguiente comando:

#Instalar paquete xlsx
install.packages("xlsx")

Cargar xlsx en R

Con el fin de evitar posibles mensajes de error se sugiere utilizar los siguientes comandos para cargar xlsx.

#Cargar paquete xlsx
library("rJava")
library("xlsxjars")
library("xlsx")

Leer una hoja de un libro de Excel.

Para poder utilizar la información de una hoja de cálculo, se requiere tener bien estructurada la tabla de datos y que xlsx la pueda leer correctamente, para ello utilizamos la siguiente estructura de comandos que nos permiten importar la información y utilizarla como si se tratara de un data set.

Nombre que se dará a la tabla <- read.xlsx(“nombre y extensión del archivo”, sheeName = Nombre de la hoja del libro de excel, rowIndex= Rango de filas, colIndex= Rango de Columnas)

Ejemplo:

#Abrir Hoja de Excel y leer área de base de datos
Matrimonios <- read.xlsx("Matrim.xlsx", #Nombre de archivo
                         sheetName = "1995", #Nombre de la hoja
                         rowIndex = 1:15, #Vector de filas
                         colIndex= 1:4, #Vector de columnas
                         header=TRUE) #La primer fila contiene los encabezados

Al ejecutar este comando podemos ya leer la información de la tabla en R e interactuar con los datos en todas las formas que R nos permite.

(Matrimonios)

La tabla nos muestra el numero de personas que se casaron en México en 1995 por edad y sexo.

Se pueden añadir comandos adicionales para definir el área a importar de la hoja de cálculo. En donde los argumentos de los comandos, pueden ser los siguientes:

File: Archivo a leerse 

sheetName: Nombre de la hoja en el archivo 

sheetIndex: Número de la hoja en el archivo

rowIndex: Vector numérico que indica las filas que desea extraer. Si NULL, todas las filas encontradas se extraerán, a menos que se especifique startRow o endRow

colIndex: Vector numérico que indica las columnas que desea extraer. Si NULL, se extraerán todas las columnas encontradas

as.data.frame: Valor lógico que indica si el resultado debe ser coaccionado en un archivo data.frame. Si FALSE, el resultado es una lista con un elemento para cada columna.

Header: Valor lógico que indica si la primera fila correspondiente al primer elemento del vector rowIndex contiene los nombres de las variables.

ColClasses: Para read.xlsx un vector de caracteres que representa la clase de cada columna. Si el vector de caracteres es nombrado, los valores no especificados se toman como NA.

KeepFormulas: Valor lógico que indica si las fórmulas de Excel deben mostrarse como texto en R y no evaluadas antes de introducirlas.

Encoding: Codificación para las cadenas de entrada.

StartRow: Número que especifica el índice de la fila inicial. Para read.xlsx este argumento sólo está activo si rowIndex es NULL.

EndRow: Número que especifica el índice de la última fila a importar. Si es NULL, lea todas las filas de la hoja. Para read.xlsx este argumento sólo está activo si rowIndex es NULL.

Editar archivos en Excel

Al igual que para extraer la información tenemos que tomarla desde Microsoft Excel, al compartirla se vuelve importarnte el poder hacerlo en este formato de archivo. Para ello xlsx tiene la función de exportar información con el siguiente comando.

write.xlsx(x, file, sheetName="Sheet1",
col.names=TRUE, row.names=TRUE, append=FALSE)
write.xlsx2(x, file, sheetName="Sheet1",
col.names=TRUE, row.names=TRUE, append=FALSE)

En donde los argumentos dados en los comandos, son los siguientes:

X (Marco de datos que se escribirá en el libro)

File (Ruta al archivo de salida)

SheetName (Cadena de caracteres a utilizar para el nombre de la hoja.)

Col.names, row.names (Valor lógico que especifica si los nombres de columnas / nombres de filas de x deben escribirse en el archivo)

Append (Valor lógico que indica si se debe añadir x a un archivo existente)

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).

Dar formato a Celdas

S3 method for class GCellStyleG cs1 + object

Ejemplo:

cs <- CellStyle(wb) +
Font(wb, heightInPoints=20, isBold=TRUE, isItalic=TRUE, name="Courier New", color="orange") +
Fill(backgroundColor="lavender", foregroundColor="lavender", pattern="SOLID_FOREGROUND") +
Alignment(h="ALIGN_RIGHT")

Bordes

addDataFrame(x, sheet, col.names=TRUE, row.names=TRUE, startRow=1, startColumn=1, colStyle=NULL, colnamesStyle=NULL, rownamesStyle=NULL, showNA=FALSE, characterNA="", byrow=FALSE)

Principales argumentos

CellRange   Cadena que especifica el rango de celdas. 

ColIndex    Vector numérico que especifica las columnas que desea utilizar en el tamaño automático.

ColSplit    Valor numérico para la columna a dividir.

ColWidth        Valor numérico para especificar el ancho de la columna. Las unidades están en 1 / 256ths de un ancho de carácter.

Denominador Valor numérico que representa el denomiador de la relación de zoom.

EndColumn   Valor numérico para la columna final.

EndRow      Valor numérico para la fila final.

Ind     Valor numérico que indica la región fusionada que desea eliminar.

Numerator       Valor numérico que representa el numerador de la relación de zoom.

Position    Caractér. Los valores válidos son "PANE_LOWER_LEFT", "PANE_LOWER_RIGHT", "PANE_UPPER_LEFT", "PANE_UPPER_RIGHT".

RowSplit    Valor numérico para la fila para dividir.

Sheet   Objeto de hoja de cálculo.

SheetIndex  Valor numérico para el índice de hoja de cálculo. 

StartColumn Valor numérico para la columna de inicio. 

StartRow    Valor numérico para la fila de inicio.

XSplitPos   Valor numérico para la posición horizontal de split en 1/20 de un punto. 

YSplitPos   Valor numérico para la posición vertical de split en 1/20 de un punto. 

Wb      Objeto de libro.

Conclusion

La funcionabilidad que me agrado fue la de poder importar datos directamente desde Excel sin tener que estar manipulando archivos y creando datasets en forma manual.

En lo referente a la exportación y edición de archivos para compartir información en excel se vuelve bastante complicado, me parece una mejor opción hacerlo directamente desde R en Markdown y compartirlo en html o bien PDF.

Fuentes:

[Rstudio] (https://rstudio-pubs-static.s3.amazonaws.com/253296_a20608edefa34683855558551554aa8f.html)

[Edición html] (https://www.rstudio.com/wp-content/uploads/2015/03/rmarkdown-spanish.pdf)

[Tutoriales] (https://www.tutorialspoint.com/r/r_excel_files.htm)

[Adrian A. Dragulescu] (https://cran.r-project.org/web/packages/xlsx/xlsx.pdf)

LS0tDQp0aXRsZTogIk1hbnVhbCBi4XNpY28gZGVsIHBhY2thZ2UgWExTWCBwYXJhIFIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyNNYXJjb3MgQWxiZXJ0byBSb3NhcyBMYXJhDQoNCiMjIyNJbnRyb2R1Y2Np824NCg0KTWljcm9zb2Z0IEV4Y2VsIGVzIGxhIGhvamEgZGUgY+FsY3VsbyBt4XMgdXRpbGl6YWRhIGVuIGVsIG11bmRvLCBhbGd1bm9zIHNpdGlvcyBkZSBkZXBlbmRlbmNpYXMgZGUgZ29iaWVybm8gY29tbyBlbCBJTkVHSSwgU0NULCBldGMuIE5vcyBwZXJtaXRlbiBleHBvcnRhciBpbmZvcm1hY2nzbiBlbiBlc3RlIHRpcG8gZGUgYXJjaGl2b3MsIGFzaW1pc21vIHNvbiBtdWNo7XNpbWFzIGxhcyBlbXByZXNhcyB5IHBlcnNvbmFzIHF1ZSB1dGlsaXphbiBlc3RlIHByb2dyYW1hIHBhcmEgZ3VhcmRhciBzdXMgYmFzZXMgZGUgZGF0b3MuDQoNCkRlIGFo7SBsbyBpbXBvcnRhbnRlIHF1ZSBjb25vemNhbW9zIGxhIG1hbmVyYSBkZSBpbXBvcnRhciB5IGV4cG9ydGFyIGluZm9ybWFjafNuIGRlc2RlIFIgYSBhcmNoaXZvcyBkZSBNaWNyb3NvZnQgRXhjZWwuDQoNCkF1bmFkbyBhIGxhIGZ1bmNp824gZGUgZXhwb3J0YXIgaW5mb3JtYWNp824geSBncuFmaWNvcyBhIGFyY2hpdm9zIGRlIEV4Y2VsLCBzZSB0aWVuZSBsYSBwb3NpYmlsaWRhZCBkZSBjcmVhciBlc3RvcyBhcmNoaXZvcyB5IGVkaXRhcmxvcy4NCkVsIHByZXNlbnRlIHRyYWJham8gcHJldGVuZGUgZGFyIHVuYSBnde1hIHLhcGlkYSBlbiBsYSB1dGlsaXphY2nzbiBkZWwgcGFxdWV0ZSBYTFNYIHBhcmEgUi4NCg0KIyMjI1JlcXVlcmltaWVudG9zIGRlbCBzaXN0ZW1hDQoNClBhcmEgcG9kZXIgZW1wZXphciBhIHV0aWxpemFyIGVsIHBhcXVldGUgWExTWCBlbiBSLCBzZSBuZWNlc2l0YSB0ZW5lciBwcmV2aWFtZW50ZSBpbnN0YWxhZGEgbGEgdmVyc2nzbiBkZSA2NCBiaXRzIGRlIGphdmEgeSBsb3MgcGFxdWV0ZXMgcGFyYSBSOiBySmF2YSB5IHhsc3hqYXJzLg0KDQojIyMjSW5zdGFsYXIgeGxzeCBlbiBSDQpwYXJhIGluc3RhbGFyIHhsc3ggc2UgcHVlZGUgdXRpbGl6YXIgZWwgc2lndWllbnRlIGNvbWFuZG86DQoNCmBgYHtyfQ0KI0luc3RhbGFyIHBhcXVldGUgeGxzeA0KaW5zdGFsbC5wYWNrYWdlcygieGxzeCIpDQpgYGANCg0KDQojIyMjQ2FyZ2FyIHhsc3ggZW4gUg0KQ29uIGVsIGZpbiBkZSBldml0YXIgcG9zaWJsZXMgbWVuc2FqZXMgZGUgZXJyb3Igc2Ugc3VnaWVyZSB1dGlsaXphciBsb3Mgc2lndWllbnRlcyBjb21hbmRvcyBwYXJhIGNhcmdhciB4bHN4Lg0KDQpgYGB7cn0NCiNDYXJnYXIgcGFxdWV0ZSB4bHN4DQpsaWJyYXJ5KCJySmF2YSIpDQpsaWJyYXJ5KCJ4bHN4amFycyIpDQpsaWJyYXJ5KCJ4bHN4IikNCmBgYA0KDQogDQoNCiMjIyNMZWVyIHVuYSBob2phIGRlIHVuIGxpYnJvIGRlIEV4Y2VsLg0KDQpQYXJhIHBvZGVyIHV0aWxpemFyIGxhIGluZm9ybWFjafNuIGRlIHVuYSBob2phIGRlIGPhbGN1bG8sIHNlIHJlcXVpZXJlIHRlbmVyIGJpZW4gZXN0cnVjdHVyYWRhIGxhIHRhYmxhIGRlIGRhdG9zIHkgcXVlIHhsc3ggbGEgcHVlZGEgbGVlciBjb3JyZWN0YW1lbnRlLCBwYXJhIGVsbG8gdXRpbGl6YW1vcyBsYSBzaWd1aWVudGUgZXN0cnVjdHVyYSBkZSBjb21hbmRvcyBxdWUgbm9zIHBlcm1pdGVuIGltcG9ydGFyIGxhIGluZm9ybWFjafNuIHkgdXRpbGl6YXJsYSBjb21vIHNpIHNlIHRyYXRhcmEgZGUgdW4gZGF0YSBzZXQuDQoNCg0KTm9tYnJlIHF1ZSBzZSBkYXLhIGEgbGEgdGFibGEgPC0gcmVhZC54bHN4KCJub21icmUgeSBleHRlbnNp824gZGVsIGFyY2hpdm8iLCBzaGVlTmFtZSA9IE5vbWJyZSBkZSBsYSBob2phIGRlbCBsaWJybyBkZSBleGNlbCwgcm93SW5kZXg9IFJhbmdvIGRlIGZpbGFzLCBjb2xJbmRleD0gUmFuZ28gZGUgQ29sdW1uYXMpDQoNCkVqZW1wbG86DQoNCmBgYHtyfQ0KI0FicmlyIEhvamEgZGUgRXhjZWwgeSBsZWVyIOFyZWEgZGUgYmFzZSBkZSBkYXRvcw0KTWF0cmltb25pb3MgPC0gcmVhZC54bHN4KCJNYXRyaW0ueGxzeCIsICNOb21icmUgZGUgYXJjaGl2bw0KICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0TmFtZSA9ICIxOTk1IiwgI05vbWJyZSBkZSBsYSBob2phDQogICAgICAgICAgICAgICAgICAgICAgICAgcm93SW5kZXggPSAxOjE1LCAjVmVjdG9yIGRlIGZpbGFzDQogICAgICAgICAgICAgICAgICAgICAgICAgY29sSW5kZXg9IDE6NCwgI1ZlY3RvciBkZSBjb2x1bW5hcw0KICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlcj1UUlVFKSAjTGEgcHJpbWVyIGZpbGEgY29udGllbmUgbG9zIGVuY2FiZXphZG9zDQpgYGANCg0KIA0KQWwgZWplY3V0YXIgZXN0ZSBjb21hbmRvIHBvZGVtb3MgeWEgbGVlciBsYSBpbmZvcm1hY2nzbiBkZSBsYSB0YWJsYSBlbiBSIGUgaW50ZXJhY3R1YXIgY29uIGxvcyBkYXRvcyBlbiB0b2RhcyBsYXMgZm9ybWFzIHF1ZSBSIG5vcyBwZXJtaXRlLg0KDQpgYGB7cn0NCihNYXRyaW1vbmlvcykNCmBgYA0KDQpMYSB0YWJsYSBub3MgbXVlc3RyYSBlbCBudW1lcm8gZGUgcGVyc29uYXMgcXVlIHNlIGNhc2Fyb24gZW4gTel4aWNvIGVuIDE5OTUgcG9yIGVkYWQgeSBzZXhvLg0KDQpTZSBwdWVkZW4gYfFhZGlyIGNvbWFuZG9zIGFkaWNpb25hbGVzIHBhcmEgZGVmaW5pciBlbCDhcmVhIGEgaW1wb3J0YXIgZGUgbGEgaG9qYSBkZSBj4WxjdWxvLiBFbiBkb25kZSBsb3MgYXJndW1lbnRvcyBkZSBsb3MgY29tYW5kb3MsIHB1ZWRlbiBzZXIgbG9zIHNpZ3VpZW50ZXM6DQoNCmBgYHtyfQ0KRmlsZTogQXJjaGl2byBhIGxlZXJzZSANCg0Kc2hlZXROYW1lOiBOb21icmUgZGUgbGEgaG9qYSBlbiBlbCBhcmNoaXZvIA0KDQpzaGVldEluZGV4OiBO+m1lcm8gZGUgbGEgaG9qYSBlbiBlbCBhcmNoaXZvDQoNCnJvd0luZGV4OiBWZWN0b3IgbnVt6XJpY28gcXVlIGluZGljYSBsYXMgZmlsYXMgcXVlIGRlc2VhIGV4dHJhZXIuIFNpIE5VTEwsIHRvZGFzIGxhcyBmaWxhcyBlbmNvbnRyYWRhcyBzZSBleHRyYWVy4W4sIGEgbWVub3MgcXVlIHNlIGVzcGVjaWZpcXVlIHN0YXJ0Um93IG8gZW5kUm93DQoNCmNvbEluZGV4OiBWZWN0b3IgbnVt6XJpY28gcXVlIGluZGljYSBsYXMgY29sdW1uYXMgcXVlIGRlc2VhIGV4dHJhZXIuIFNpIE5VTEwsIHNlIGV4dHJhZXLhbiB0b2RhcyBsYXMgY29sdW1uYXMgZW5jb250cmFkYXMNCg0KYXMuZGF0YS5mcmFtZTogVmFsb3IgbPNnaWNvIHF1ZSBpbmRpY2Egc2kgZWwgcmVzdWx0YWRvIGRlYmUgc2VyIGNvYWNjaW9uYWRvIGVuIHVuIGFyY2hpdm8gZGF0YS5mcmFtZS4gU2kgRkFMU0UsIGVsIHJlc3VsdGFkbyBlcyB1bmEgbGlzdGEgY29uIHVuIGVsZW1lbnRvIHBhcmEgY2FkYSBjb2x1bW5hLg0KDQpIZWFkZXI6IFZhbG9yIGzzZ2ljbyBxdWUgaW5kaWNhIHNpIGxhIHByaW1lcmEgZmlsYSBjb3JyZXNwb25kaWVudGUgYWwgcHJpbWVyIGVsZW1lbnRvIGRlbCB2ZWN0b3Igcm93SW5kZXggY29udGllbmUgbG9zIG5vbWJyZXMgZGUgbGFzIHZhcmlhYmxlcy4NCg0KQ29sQ2xhc3NlczogUGFyYSByZWFkLnhsc3ggdW4gdmVjdG9yIGRlIGNhcmFjdGVyZXMgcXVlIHJlcHJlc2VudGEgbGEgY2xhc2UgZGUgY2FkYSBjb2x1bW5hLiBTaSBlbCB2ZWN0b3IgZGUgY2FyYWN0ZXJlcyBlcyBub21icmFkbywgbG9zIHZhbG9yZXMgbm8gZXNwZWNpZmljYWRvcyBzZSB0b21hbiBjb21vIE5BLg0KDQpLZWVwRm9ybXVsYXM6IFZhbG9yIGzzZ2ljbyBxdWUgaW5kaWNhIHNpIGxhcyBm83JtdWxhcyBkZSBFeGNlbCBkZWJlbiBtb3N0cmFyc2UgY29tbyB0ZXh0byBlbiBSIHkgbm8gZXZhbHVhZGFzIGFudGVzIGRlIGludHJvZHVjaXJsYXMuDQoNCkVuY29kaW5nOiBDb2RpZmljYWNp824gcGFyYSBsYXMgY2FkZW5hcyBkZSBlbnRyYWRhLg0KDQpTdGFydFJvdzogTvptZXJvIHF1ZSBlc3BlY2lmaWNhIGVsIO1uZGljZSBkZSBsYSBmaWxhIGluaWNpYWwuIFBhcmEgcmVhZC54bHN4IGVzdGUgYXJndW1lbnRvIHPzbG8gZXN04SBhY3Rpdm8gc2kgcm93SW5kZXggZXMgTlVMTC4NCg0KRW5kUm93OiBO+m1lcm8gcXVlIGVzcGVjaWZpY2EgZWwg7W5kaWNlIGRlIGxhIPpsdGltYSBmaWxhIGEgaW1wb3J0YXIuIFNpIGVzIE5VTEwsIGxlYSB0b2RhcyBsYXMgZmlsYXMgZGUgbGEgaG9qYS4gUGFyYSByZWFkLnhsc3ggZXN0ZSBhcmd1bWVudG8gc/NsbyBlc3ThIGFjdGl2byBzaSByb3dJbmRleCBlcyBOVUxMLg0KDQpgYGANCg0KDQojI0VkaXRhciBhcmNoaXZvcyBlbiBFeGNlbA0KDQpBbCBpZ3VhbCBxdWUgcGFyYSBleHRyYWVyIGxhIGluZm9ybWFjafNuIHRlbmVtb3MgcXVlIHRvbWFybGEgZGVzZGUgTWljcm9zb2Z0IEV4Y2VsLCBhbCBjb21wYXJ0aXJsYSBzZSB2dWVsdmUgaW1wb3J0YXJudGUgZWwgcG9kZXIgaGFjZXJsbyBlbiBlc3RlIGZvcm1hdG8gZGUgYXJjaGl2by4gUGFyYSBlbGxvIHhsc3ggdGllbmUgbGEgZnVuY2nzbiBkZSBleHBvcnRhciBpbmZvcm1hY2nzbiBjb24gZWwgc2lndWllbnRlIGNvbWFuZG8uDQoNCmBgYHtyfQ0Kd3JpdGUueGxzeCh4LCBmaWxlLCBzaGVldE5hbWU9IlNoZWV0MSIsDQpjb2wubmFtZXM9VFJVRSwgcm93Lm5hbWVzPVRSVUUsIGFwcGVuZD1GQUxTRSkNCndyaXRlLnhsc3gyKHgsIGZpbGUsIHNoZWV0TmFtZT0iU2hlZXQxIiwNCmNvbC5uYW1lcz1UUlVFLCByb3cubmFtZXM9VFJVRSwgYXBwZW5kPUZBTFNFKQ0KYGBgDQoNCkVuIGRvbmRlIGxvcyBhcmd1bWVudG9zIGRhZG9zIGVuIGxvcyBjb21hbmRvcywgc29uIGxvcyBzaWd1aWVudGVzOg0KDQpYIChNYXJjbyBkZSBkYXRvcyBxdWUgc2UgZXNjcmliaXLhIGVuIGVsIGxpYnJvKQ0KDQpGaWxlIChSdXRhIGFsIGFyY2hpdm8gZGUgc2FsaWRhKQ0KDQpTaGVldE5hbWUgKENhZGVuYSBkZSBjYXJhY3RlcmVzIGEgdXRpbGl6YXIgcGFyYSBlbCBub21icmUgZGUgbGEgaG9qYS4pDQoNCkNvbC5uYW1lcywgcm93Lm5hbWVzIChWYWxvciBs82dpY28gcXVlIGVzcGVjaWZpY2Egc2kgbG9zIG5vbWJyZXMgZGUgY29sdW1uYXMgLyBub21icmVzIGRlIGZpbGFzIGRlIHggZGViZW4gZXNjcmliaXJzZSBlbiBlbCBhcmNoaXZvKQ0KDQpBcHBlbmQgKFZhbG9yIGzzZ2ljbyBxdWUgaW5kaWNhIHNpIHNlIGRlYmUgYfFhZGlyIHggYSB1biBhcmNoaXZvIGV4aXN0ZW50ZSkNCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg0KIyNEYXIgZm9ybWF0byBhIENlbGRhcw0KDQpgYGB7cn0NClMzIG1ldGhvZCBmb3IgY2xhc3MgR0NlbGxTdHlsZUcgY3MxICsgb2JqZWN0DQpgYGANCg0KRWplbXBsbzoNCmBgYHtyfQ0KY3MgPC0gQ2VsbFN0eWxlKHdiKSArDQpGb250KHdiLCBoZWlnaHRJblBvaW50cz0yMCwgaXNCb2xkPVRSVUUsIGlzSXRhbGljPVRSVUUsIG5hbWU9IkNvdXJpZXIgTmV3IiwgY29sb3I9Im9yYW5nZSIpICsNCkZpbGwoYmFja2dyb3VuZENvbG9yPSJsYXZlbmRlciIsIGZvcmVncm91bmRDb2xvcj0ibGF2ZW5kZXIiLCBwYXR0ZXJuPSJTT0xJRF9GT1JFR1JPVU5EIikgKw0KQWxpZ25tZW50KGg9IkFMSUdOX1JJR0hUIikNCmBgYA0KKipCb3JkZXMqKg0KYGBge3J9DQphZGREYXRhRnJhbWUoeCwgc2hlZXQsIGNvbC5uYW1lcz1UUlVFLCByb3cubmFtZXM9VFJVRSwgc3RhcnRSb3c9MSwgc3RhcnRDb2x1bW49MSwgY29sU3R5bGU9TlVMTCwgY29sbmFtZXNTdHlsZT1OVUxMLCByb3duYW1lc1N0eWxlPU5VTEwsIHNob3dOQT1GQUxTRSwgY2hhcmFjdGVyTkE9IiIsIGJ5cm93PUZBTFNFKQ0KYGBgDQoNCiMjUHJpbmNpcGFsZXMgYXJndW1lbnRvcw0KDQpgYGB7cn0NCkNlbGxSYW5nZSAgIENhZGVuYSBxdWUgZXNwZWNpZmljYSBlbCByYW5nbyBkZSBjZWxkYXMuIA0KDQpDb2xJbmRleCAgICBWZWN0b3IgbnVt6XJpY28gcXVlIGVzcGVjaWZpY2EgbGFzIGNvbHVtbmFzIHF1ZSBkZXNlYSB1dGlsaXphciBlbiBlbCB0YW1h8W8gYXV0b23hdGljby4NCg0KQ29sU3BsaXQgICAgVmFsb3IgbnVt6XJpY28gcGFyYSBsYSBjb2x1bW5hIGEgZGl2aWRpci4NCg0KQ29sV2lkdGggICAgICAgIFZhbG9yIG51belyaWNvIHBhcmEgZXNwZWNpZmljYXIgZWwgYW5jaG8gZGUgbGEgY29sdW1uYS4gTGFzIHVuaWRhZGVzIGVzdOFuIGVuIDEgLyAyNTZ0aHMgZGUgdW4gYW5jaG8gZGUgY2Fy4WN0ZXIuDQoNCkRlbm9taW5hZG9yIFZhbG9yIG51belyaWNvIHF1ZSByZXByZXNlbnRhIGVsIGRlbm9taWFkb3IgZGUgbGEgcmVsYWNp824gZGUgem9vbS4NCg0KRW5kQ29sdW1uICAgVmFsb3IgbnVt6XJpY28gcGFyYSBsYSBjb2x1bW5hIGZpbmFsLg0KDQpFbmRSb3cgICAgICBWYWxvciBudW3pcmljbyBwYXJhIGxhIGZpbGEgZmluYWwuDQoNCkluZCAgICAgVmFsb3IgbnVt6XJpY28gcXVlIGluZGljYSBsYSByZWdp824gZnVzaW9uYWRhIHF1ZSBkZXNlYSBlbGltaW5hci4NCg0KTnVtZXJhdG9yICAgICAgIFZhbG9yIG51belyaWNvIHF1ZSByZXByZXNlbnRhIGVsIG51bWVyYWRvciBkZSBsYSByZWxhY2nzbiBkZSB6b29tLg0KDQpQb3NpdGlvbiAgICBDYXJhY3Tpci4gTG9zIHZhbG9yZXMgduFsaWRvcyBzb24gIlBBTkVfTE9XRVJfTEVGVCIsICJQQU5FX0xPV0VSX1JJR0hUIiwgIlBBTkVfVVBQRVJfTEVGVCIsICJQQU5FX1VQUEVSX1JJR0hUIi4NCg0KUm93U3BsaXQgICAgVmFsb3IgbnVt6XJpY28gcGFyYSBsYSBmaWxhIHBhcmEgZGl2aWRpci4NCg0KU2hlZXQgICBPYmpldG8gZGUgaG9qYSBkZSBj4WxjdWxvLg0KDQpTaGVldEluZGV4ICBWYWxvciBudW3pcmljbyBwYXJhIGVsIO1uZGljZSBkZSBob2phIGRlIGPhbGN1bG8uIA0KDQpTdGFydENvbHVtbiBWYWxvciBudW3pcmljbyBwYXJhIGxhIGNvbHVtbmEgZGUgaW5pY2lvLiANCg0KU3RhcnRSb3cgICAgVmFsb3IgbnVt6XJpY28gcGFyYSBsYSBmaWxhIGRlIGluaWNpby4NCg0KWFNwbGl0UG9zICAgVmFsb3IgbnVt6XJpY28gcGFyYSBsYSBwb3NpY2nzbiBob3Jpem9udGFsIGRlIHNwbGl0IGVuIDEvMjAgZGUgdW4gcHVudG8uIA0KDQpZU3BsaXRQb3MgICBWYWxvciBudW3pcmljbyBwYXJhIGxhIHBvc2ljafNuIHZlcnRpY2FsIGRlIHNwbGl0IGVuIDEvMjAgZGUgdW4gcHVudG8uIA0KDQpXYiAgICAgIE9iamV0byBkZSBsaWJyby4NCmBgYA0KDQojIyNDb25jbHVzaW9uDQoNCkxhIGZ1bmNpb25hYmlsaWRhZCBxdWUgbWUgYWdyYWRvIGZ1ZSBsYSBkZSBwb2RlciBpbXBvcnRhciBkYXRvcyBkaXJlY3RhbWVudGUgZGVzZGUgRXhjZWwgc2luIHRlbmVyIHF1ZSBlc3RhciBtYW5pcHVsYW5kbyBhcmNoaXZvcyB5IGNyZWFuZG8gZGF0YXNldHMgZW4gZm9ybWEgbWFudWFsLg0KDQpFbiBsbyByZWZlcmVudGUgYSBsYSBleHBvcnRhY2nzbiB5IGVkaWNp824gZGUgYXJjaGl2b3MgcGFyYSBjb21wYXJ0aXIgaW5mb3JtYWNp824gZW4gZXhjZWwgc2UgdnVlbHZlIGJhc3RhbnRlIGNvbXBsaWNhZG8sIG1lIHBhcmVjZSB1bmEgbWVqb3Igb3BjafNuIGhhY2VybG8gZGlyZWN0YW1lbnRlIGRlc2RlIFIgZW4gTWFya2Rvd24geSBjb21wYXJ0aXJsbyBlbiBodG1sIG8gYmllbiBQREYuDQoNCkZ1ZW50ZXM6DQoNCg0KDQpbUnN0dWRpb10gKGh0dHBzOi8vcnN0dWRpby1wdWJzLXN0YXRpYy5zMy5hbWF6b25hd3MuY29tLzI1MzI5Nl9hMjA2MDhlZGVmYTM0NjgzODU1NTU4NTUxNTU0YWE4Zi5odG1sKQ0KDQpbRWRpY2nzbiBodG1sXSAoaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvcm1hcmtkb3duLXNwYW5pc2gucGRmKQ0KDQpbVHV0b3JpYWxlc10gKGh0dHBzOi8vd3d3LnR1dG9yaWFsc3BvaW50LmNvbS9yL3JfZXhjZWxfZmlsZXMuaHRtKQ0KDQpbQWRyaWFuIEEuIERyYWd1bGVzY3VdIChodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMveGxzeC94bHN4LnBkZikNCg==