Metadados na prática - utilizando o pacote {EML}

Vamos agora construir um metadado utilizando o pacote EML (Ecological Metadata Language). O pacote EML foi desenvolvido com o intuito de oferecer um conjunto de funções que permite a construção de metadados do tipo XML mas que abrangem as especificidades presentes nos dados ecológicos. Para relembrar a estrutura dos metadados peço que revejam os slides dessa aula aqui.

Nesta seção temos duas opções de prática. Aqueles que fizeram os metadados em palnilhas eletrônicas, podem praticar com seus próprios dados. Para aqueles que não tem dados próprios iremos utilizar o conjunto de dados do artigo “Organic-matter loading determines regime shifts and alternative states in an aquatic ecosystem” para construir um arquivo de metadados. Optamos por usar este dado pois ele já apresenta um arquivo de metadados associado, assim podemos reconstruí-lo e verificar como fica. Além disso ele é o mesmo dado utilizado no tutorial original do pacote EML, sendo conveniente para fins didáticos. Ao final vamos praticar com um outro conjunto de dados. Lembrando que se você baixou o repositório do material desta disciplina, você conseguirá ler os dados sem problemas.

Instalação

Para instalar o pacote EML faça o seguinte:

install.packages("EML")

library(EML)

Vamos utilizar um conjunto de dados contidos no próprio pacote EML para facilitar a compreensão dos passos necessários para construir um EML completo

Construindo um EML - Harvard Forest Data

Construindo os Atributos

Os atributos são as descrições para as variáveis contidas na nossa tabela. Aquela aba contendo as informações das variáveis, pois então, esta seria os atributos da sua tabela de dados. Então, primeiro precisamos de uma tabela de atributos que define o significado geral de cada variável em nossa tabela de dados. Esta tabela pode ser gerada aqui no R direto, como no exemplo abaixo, mas pode ser importada a partir de uma tabela .csv. A tabela segue o formato a seguir:

attributes <-
tibble::tribble(
~attributeName, ~attributeDefinition,                                                 ~formatString, ~definition,        ~unit,   ~numberType,
  "run.num",    "which run number (=block). Range: 1 - 6. (integer)",                 NA,            "which run number", NA,       NA,
  "year",       "year, 2012",                                                         "YYYY",        NA,                 NA,       NA,
  "day",        "Julian day. Range: 170 - 209.",                                      "DDD",         NA,                 NA,       NA,
  "hour.min",   "hour and minute of observation. Range 1 - 2400 (integer)",           "hhmm",        NA,                 NA,       NA,
  "i.flag",     "is variable Real, Interpolated or Bad (character/factor)",           NA,            NA,                 NA,       NA,
  "variable",   "what variable being measured in what treatment (character/factor).", NA,            NA,                 NA,       NA,
  "value.i",    "value of measured variable for run.num on year/day/hour.min.",       NA,            NA,                 NA,       NA,
  "length",    "length of the species in meters (dummy example of numeric data)",     NA,            NA,                 "meter",  "real")

Dê uma olhada na tabela de atributos (pode ser feito usano a função View ou abrindo no Excel). A maioria dos dados seguem este formato geral.

A seguir precisamos descrever os atributos que apresentam níveis. Por exemplo o atributo chamado variable apresenta oito níveis. A estratégia seguida aqui foi criar vetores com os códigos usados para cada nível dos atributos e uma pequena explicação.

i.flag <- c(R = "real",
            I = "interpolated",
            B = "bad")
variable <- c(
  control  = "no prey added",
  low      = "0.125 mg prey added ml-1 d-1",
  med.low  = "0,25 mg prey added ml-1 d-1",
  med.high = "0.5 mg prey added ml-1 d-1",
  high     = "1.0 mg prey added ml-1 d-1",
  air.temp = "air temperature measured just above all plants (1 thermocouple)",
  water.temp = "water temperature measured within each pitcher",
  par       = "photosynthetic active radiation (PAR) measured just above all plants (1 sensor)"
)

value.i <- c(
  control  = "% dissolved oxygen",
  low      = "% dissolved oxygen",
  med.low  = "% dissolved oxygen",
  med.high = "% dissolved oxygen",
  high     = "% dissolved oxygen",
  air.temp = "degrees C",
  water.temp = "degrees C",
  par      = "micromoles m-1 s-1"
)

## Write these into the data.frame format
factors <- rbind(
data.frame(
  attributeName = "i.flag",
  code = names(i.flag),
  definition = unname(i.flag)
),
data.frame(
  attributeName = "variable",
  code = names(variable),
  definition = unname(variable)
),
data.frame(
  attributeName = "value.i",
  code = names(value.i),
  definition = unname(value.i)
)
)

Após especificadas as características das variáveis categóricas (as contínuas já estão especificadas na tabela geral de atributos attributes), usamos a função set_attributes para criar a lista de atributos geral dos dados, onde devemos especificar o tipo de atributo que estamos inserindo (se caracter, se data, se fator etc..)

attributeList <- set_attributes(attributes, factors, col_classes = c("character", "Date", "Date", "Date", "factor", "factor", "factor", "numeric"))

Construindo as características físicas do dado

Aqui vamos especificar as características físicas do próprio arquivo de dados que utilizamos, ou seja, o formato, o tipo de compressão do arquivo, o separador etc. Resumindo, aqui é especificado a característica do arquivo. Isso é feito facilmente com a função set_physical. Se estivermos utilizando um arquivo .csv a função já extrai automaticamente a maior parte das características relevantes do arquivo de dados.

physical <- set_physical(here::here("data", "hf205-01-TPexp1-EML-Exemple.csv"))

Juntando Atributos e Características físicas

Com o objeto de atributos e o objeto de características físicas temos o essencial para descrever o nosso conjunto de dados. Agora vamos juntar ambos com mais algumas informações em uma única lista e denominar isso como sendo o nosso dataTable

dataTable <- list(
                 entityName = "hf205-01-TPexp1.csv",
                 entityDescription = "tipping point experiment 1",
                 physical = physical,
                 attributeList = attributeList)

Características adicionais - extensão geográfica, métodos e outros

geographicDescription <- "Harvard Forest Greenhouse, Tom Swamp Tract (Harvard Forest)"


coverage <- 
  set_coverage(begin = '2012-06-01', end = '2013-12-31',
               sci_names = "Sarracenia purpurea",
               geographicDescription = geographicDescription,
               west = -122.44, east = -117.15, 
               north = 37.38, south = 30.00,
               altitudeMin = 160, altitudeMaximum = 330,
               altitudeUnits = "meter")

Para descrever os métodos em metadados

methods_file <- here::here("data", "hf205-methods.docx")
methods <- set_methods(methods_file)

Identificação dos indivíduos endereço e outras informações sobre contatos

R_person <- person("Aaron", "Ellison", ,"fakeaddress@email.com", "cre", 
                  c(ORCID = "0000-0003-4151-6081"))
aaron <- as_emld(R_person)
others <- c(as.person("Benjamin Baiser"), as.person("Jennifer Sirota"))
associatedParty <- as_emld(others)
associatedParty[[1]]$role <- "Researcher"
associatedParty[[2]]$role <- "Researcher"

HF_address <- list(
                  deliveryPoint = "324 North Main Street",
                  city = "Petersham",
                  administrativeArea = "MA",
                  postalCode = "01366",
                  country = "USA")

publisher <- list(
                 organizationName = "Harvard Forest",
                 address = HF_address)


contact <- 
  list(
    individualName = aaron$individualName,
    electronicMailAddress = aaron$electronicMailAddress,
    address = HF_address,
    organizationName = "Harvard Forest",
    phone = "000-000-0000")

criando keywords para os dados

keywordSet <- list(
    list(
        keywordThesaurus = "LTER controlled vocabulary",
        keyword = list("bacteria",
                    "carnivorous plants",
                    "genetics",
                    "thresholds")
        ),
    list(
        keywordThesaurus = "LTER core area",
        keyword =  list("populations", "inorganic nutrients", "disturbance")
        ),
    list(
        keywordThesaurus = "HFR default",
        keyword = list("Harvard Forest", "HFR", "LTER", "USA")
        ))

Outras informações relevantes como abstract etc

pubDate <- "2012" 

title <- "Thresholds and Tipping Points in a Sarracenia 
Microecosystem at Harvard Forest since 2012"

abstract <- "The primary goal of this project is to determine
  experimentally the amount of lead time required to prevent a state
change. To achieve this goal, we will (1) experimentally induce state
changes in a natural aquatic ecosystem - the Sarracenia microecosystem;
(2) use proteomic analysis to identify potential indicators of states
and state changes; and (3) test whether we can forestall state changes
by experimentally intervening in the system. This work uses state-of-the
art molecular tools to identify early warning indicators in the field
of aerobic to anaerobic state changes driven by nutrient enrichment
in an aquatic ecosystem. The study tests two general hypotheses: (1)
proteomic biomarkers can function as reliable indicators of impending
state changes and may give early warning before increasing variances
and statistical flickering of monitored variables; and (2) well-timed
intervention based on proteomic biomarkers can avert future state changes
in ecological systems."  

intellectualRights <- "This dataset is released to the public and may be freely
  downloaded. Please keep the designated Contact person informed of any
plans to use the dataset. Consultation or collaboration with the original
investigators is strongly encouraged. Publications and data products
that make use of the dataset must include proper acknowledgement. For
more information on LTER Network data access and use policies, please
see: http://www.lternet.edu/data/netpolicy.html."

Criando o arquivo XML

Agora que todas as informações foram criadas (atributos, dados físicos do arquivo, coverage, dados de contato), precisamos juntar todas elas em uma única lista.

dataset <- list(
               title = title,
               creator = aaron,
               pubDate = pubDate,
               intellectualRights = intellectualRights,
               abstract = abstract,
               associatedParty = associatedParty,
               keywordSet = keywordSet,
               coverage = coverage,
               contact = contact,
               methods = methods,
               dataTable = dataTable)

Antes de criar o arquivo XML é importante criar um uuid para o arquivo, que nada mais é que um identificador que qualquer um pode criar, como demonstrado a seguir. Para mais informações sobre o que é um uuid ver esta explicação no Wikipedia

eml <- list(
           packageId = uuid::UUIDgenerate(),
           system = "uuid", # type of identifier
           dataset = dataset)

Finalmente criando o arquivo XML e validando.

write_eml(eml, here::here("data", "eml_tutorialHF205.xml"))
eml_validate(here::here("data", "eml_tutorialHF205.xml"))

Metadados para dados de biodiversidade

Se você ainda não passou pela seção de metadados usando o pacote EML, volte um pouco, por favor. Lá apresento uma introdução geral sobre metadados em XML e como montá-los usando o pacote EML no R. Aqui abordarei especificamente como montar arquivos com metadados para dados de biodiversidade (ocorrência, lista de espécies, pontos etc).

Leituras sugeridas

Esta seção é baseada no documento produzido pelo Living Norway Project, que também oferece uma introdução muito interessante sobre Darwin Core e metadados para biodiversidade.

Exemplos de metadados de biodiversidade

O melhor exemplo de esturutura digital que utiliza metadados de biodiversidade construídos desta maneira que mostrarei é o GBIF.

Metadados na prática - utilizando o pacote {LivingNorwayR}

Instalação e dados

Primeiro vamos instalar o pacote

devtools::install_github("https://github.com/LivingNorway/LivingNorwayR")
library(LivingNorwayR)

Aqui os dados que serão utilizados no exemplo. Estes são os mesmos dados utilizados no tuturial do Living Norway R. Para mais detalhes visite a página deles.

Vamos utilizar um conjunto de dados que fizemos o download e está dentro da nossa pasta do curso para criar nosso arquivo Darwin Core.

# Lendo o arquivo core
TOVEEventTableDF <- read.table(here::here("data", "TOVEData", "event.txt"), sep = "\t", header = TRUE)
head(TOVEEventTableDF)

Obtendo os arquivos extendidos, neste caso o arquivo de ocorrência de espécies. Podemos ver que se trata de um simples data frame onde os nomes das colunas seguem os códigos do Darwin Core

TOVEOccTable <- read.table(here::here("data", "TOVEData", "occurrence.txt"), sep = "\t", header = TRUE)
head(TOVEOccTable)

Construindo um XML baseado em Darwin Core

Agora que temos nossos dados podemos construir um arquivo do tipo Darwin Core

Construindo o metadado para a tabela de eventos core

Primeiro precisamos definir o que será a tabela core e o que serão as suas extensões, que por sua vez podem ser mapeadas a partir de atributos comuns presentes em ambas as tabelas (neste caso o ID).

# 1. Inicializando automaticamente - só funciona caso os nomes já estejam de acordo com o Darwin Core
newTOVEEventTable <- initializeGBIFEvent(TOVEEventTableDF, "id", nameAutoMap = TRUE)

# 2. Iniciando manualmente - podemos usar o nome dos atributos que correspondem a cada categoria do Darwin Core. 
    # podemos usar o nome ou o número da coluna
newTOVEEventTable <- initializeGBIFEvent(TOVEEventTableDF, "id",
  type = "type",
  modified = "modified",
  datasetName = "datasetName",
  ownerInstitutionCode = "ownerInstitutionCode",
  informationWithheld = "informationWithheld",
  dataGeneralizations = "dataGeneralizations",
  eventID = "eventID",
  samplingProtocol = "samplingProtocol",
  sampleSizeValue = "sampleSizeValue",
  sampleSizeUnit = "sampleSizeUnit",
  samplingEffort = "samplingEffort",
  eventDate = "eventDate",
  eventTime = "eventTime",
  year = "year",
  month = "month",
  day = "day",
  locationID = "locationID",
  country = "country",
  countryCode = "countryCode",
  stateProvince = "stateProvince",
  municipality = "municipality",
  locality = "locality",
  minimumElevationInMeters = "minimumElevationInMeters",
  maximumElevationInMeters = "maximumElevationInMeters",
  decimalLatitude = "decimalLatitude",
  decimalLongitude = "decimalLongitude",
  geodeticDatum = "geodeticDatum",
  coordinateUncertaintyInMeters = "coordinateUncertaintyInMeters")

# Podemos checar se o mapeamento dos termos se deu corretamente
newTOVEEventTable$getTermMapping()

Construindo os metadados para as extensões da tabela core

Neste caso a tabela a extensão que temos da tabela chave é a tabela de ocorrência de espécies. Vamos mapear ela da mesma forma que fizemos para a tabela core

# Função para inicialização da tabela
newTOVEOccTable <- initializeGBIFOccurrence(TOVEOccTable, "id", nameAutoMap = TRUE)

# Checando quais eventos foram mapeados na tabela, o que aparece com NA não foi mapeado, ou porque não existe ou porque tem o nome incorreto
newTOVEOccTable$getTermMapping()

Construindo o EML

O último elemento que precisamos para construir nossos metadados para os arquivos de biodiversidade que estamos utilizando é o arquivo EML, que vocês já conhecem. O EML pode ser criado de várias formas. Vimos anteriormente como criar um EML usando o pacote {EML}. O pacote {LivingNorwayR} oferece uma maneira simples de criar um arquivo XML usando um arquivo .Rmd, ou seja, usando o Rmarkdown. Vamos entender os detalhes de um arquivo do tipo Rmd mais adiante, por hora precisamos apenas saber que ele é um arquivo que permite mesclar textos e códigos, além de possibilitar criar tags para seções (Como os títulos num documento Word). Essas tags são utilizadas pelas funções do LivingNorwayR para criar o arquivo XML. Vale notar que isso é mais limitante que criar um XML usando o EML, mas para questões didáticas vamos usar um arquivo .Rmd pronto, que está na pasta de dados deste diretório.

createdTOVEMetadata <- initializeDwCMetadata(fileLocation = here::here("data", "TOVEData", "LNWorkshopExample_Metadata.rmd"), fileType = "rmarkdown")

# Exportando o EML
createdTOVEMetadata$exportToEML(file.path(here::here("data", "TOVEData"), "newMetadata.xml"))

Este novo metadado precisa ser lido para o R e assim podemos juntá-lo com os outros arquivos

newTOVEMetadata <- initializeDwCMetadata(file.path(here::here("data", "TOVEData"), "newMetadata.xml"),
  fileType = "eml" # This line is not required if the file has the ".xml" file extension
)

Juntando todos os arquivos

Agora temos os arquivos necessários para juntar em um único arquivo zipado que pode ser guardado localmente ou submetido em alguma plataforma de dados, por exemplo, GBIF, como ilustrado abaixo

Para juntar todos os arquivos criados precisamos usar a seguinte função

# criando um único arquivo com todos os objetos criados anteriormente
newTOVEArchive <- initializeDwCArchive(newTOVEEventTable, list(newTOVEOccTable), newTOVEMetadata)

Para salvar o objeto .zip, que agora é nosso Darwin Core Archive, podemos fazer o seguinte

newTOVEArchive$exportAsDwCArchive(file.path(here::here("data", "NEWTOVEData"), "newDwCArchive.zip"))

Explorando o Darwin Core Archive

Uma vez criado o arquivo no formato Darwin Core Archive podemos fazer o caminho inverso e obter as tabelas a partir do arquivo que criamos.

Para tanto podemos usar as funções do pacote LivingNorwayR para ler o arquivo zip e extrair as tabelas, incluindo os metadados.

localDataLoc2 <- file.path(here::here("data", "NEWTOVEData"), "newDwCArchive.zip") 
NEWTOVEArchive <- initializeDwCArchive(localDataLoc2, "UTF-8")

Podemos agora explorar os dados dentro do arquivo Darwin Core criado nos passos anteriores. Ou seja, podemos retomar os dados e importar para dentro do R para fazer, por exemplo, novas análises

# obtendo o dado
NEWTOVEEventTable <- NEWTOVEArchive$getCoreTable()
class(NEWTOVEEventTable)

# Podemos exportar como um dataframe
NEWTOVEEventTableDF <- NEWTOVEEventTable$exportAsDataFrame()

# obtendo dados da tabela extendida
NEWTOVEEventTableDF <- NEWTOVEEventTable$exportAsDataFrame()
# exportando como dataframe
head(NEWTOVEEventTableDF)

Atividade

Para fixar os conceitos apresentados sobre metadados, vamos treinar um pouco mais utilizando um outro conjunto de dados.

Produza um arquivo .xml utilizando os dados iris do pacote {ggplot}. Algumas informações nós não temos, por exemplo, a cobertura de coleta, ano de coleta etc. Mas para fins de prática vamos inventar essas informações no momento de fazer o metadado.

Para acessar os dados:

library(ggplot2)
data("iris")
LS0tDQp0aXRsZTogIkNvbnN0cnVpbmRvIG1ldGFkYWRvcyBlbSBmb3JtYXRvIEVNTCINCm91dHB1dDogaHRtbF9kb2N1bWVudA0KZGF0ZTogIjIwMjQtMDYtMjciDQotLS0NCg0KYGBge3IgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0Ka2xpcHB5OjprbGlwcHkoKQ0KYGBgDQoNCg0KIyBNZXRhZGFkb3MgbmEgcHLDoXRpY2EgLSB1dGlsaXphbmRvIG8gcGFjb3RlIGB7RU1MfWANCg0KVmFtb3MgYWdvcmEgY29uc3RydWlyIHVtIG1ldGFkYWRvIHV0aWxpemFuZG8gbyBwYWNvdGUgRU1MIChFY29sb2dpY2FsIE1ldGFkYXRhIExhbmd1YWdlKS4gTyBwYWNvdGUgRU1MIGZvaSBkZXNlbnZvbHZpZG8gY29tIG8gaW50dWl0byBkZSBvZmVyZWNlciB1bSBjb25qdW50byBkZSBmdW7Dp8O1ZXMgcXVlIHBlcm1pdGUgYSBjb25zdHJ1w6fDo28gZGUgbWV0YWRhZG9zIGRvIHRpcG8gWE1MIG1hcyBxdWUgYWJyYW5nZW0gYXMgZXNwZWNpZmljaWRhZGVzIHByZXNlbnRlcyBub3MgZGFkb3MgZWNvbMOzZ2ljb3MuIFBhcmEgcmVsZW1icmFyIGEgZXN0cnV0dXJhIGRvcyBtZXRhZGFkb3MgcGXDp28gcXVlIHJldmVqYW0gb3Mgc2xpZGVzIGRlc3NhIGF1bGEgW2FxdWldKGh0dHBzOi8vZ2FicmllbG5ha2FtdXJhLmdpdGh1Yi5pby9VU1BfQklFNTc5OF9hcHJlc2VudGFjb2VzLyMzMykuDQoNCg0KDQpOZXN0YSBzZcOnw6NvIHRlbW9zIGR1YXMgb3DDp8O1ZXMgZGUgcHLDoXRpY2EuIEFxdWVsZXMgcXVlIGZpemVyYW0gb3MgbWV0YWRhZG9zIGVtIHBhbG5pbGhhcyBlbGV0csO0bmljYXMsIHBvZGVtIHByYXRpY2FyIGNvbSBzZXVzIHByw7NwcmlvcyBkYWRvcy4gUGFyYSBhcXVlbGVzIHF1ZSBuw6NvIHRlbSBkYWRvcyBwcsOzcHJpb3MgaXJlbW9zIHV0aWxpemFyIG8gY29uanVudG8gZGUgZGFkb3MgZG8gYXJ0aWdvIFsiT3JnYW5pYy1tYXR0ZXIgbG9hZGluZyBkZXRlcm1pbmVzIHJlZ2ltZSBzaGlmdHMgYW5kIGFsdGVybmF0aXZlIHN0YXRlcyBpbiBhbiBhcXVhdGljIGVjb3N5c3RlbSJdKGh0dHBzOi8vd3d3LnBuYXMub3JnL2RvaS8xMC4xMDczL3BuYXMuMTIyMTAzNzExMCkgcGFyYSBjb25zdHJ1aXIgdW0gYXJxdWl2byBkZSBtZXRhZGFkb3MuIE9wdGFtb3MgcG9yIHVzYXIgZXN0ZSBkYWRvIHBvaXMgZWxlIGrDoSBhcHJlc2VudGEgdW0gYXJxdWl2byBkZSBtZXRhZGFkb3MgYXNzb2NpYWRvLCBhc3NpbSBwb2RlbW9zIHJlY29uc3RydcOtLWxvIGUgdmVyaWZpY2FyIGNvbW8gZmljYS4gQWzDqW0gZGlzc28gZWxlIMOpIG8gbWVzbW8gZGFkbyB1dGlsaXphZG8gbm8gdHV0b3JpYWwgb3JpZ2luYWwgZG8gcGFjb3RlIEVNTCwgc2VuZG8gY29udmVuaWVudGUgcGFyYSBmaW5zIGRpZMOhdGljb3MuIEFvIGZpbmFsIHZhbW9zIHByYXRpY2FyIGNvbSB1bSBvdXRybyBjb25qdW50byBkZSBkYWRvcy4gTGVtYnJhbmRvIHF1ZSBzZSB2b2PDqiBiYWl4b3UgbyByZXBvc2l0w7NyaW8gZG8gbWF0ZXJpYWwgZGVzdGEgZGlzY2lwbGluYSwgdm9jw6ogY29uc2VndWlyw6EgbGVyIG9zIGRhZG9zIHNlbSBwcm9ibGVtYXMuDQoNCg0KIyMgSW5zdGFsYcOnw6NvDQoNClBhcmEgaW5zdGFsYXIgbyBwYWNvdGUgRU1MIGZhw6dhIG8gc2VndWludGU6DQoNCmBgYHtyIGVjaG89VFJVRSxldmFsPUZBTFNFfQ0KaW5zdGFsbC5wYWNrYWdlcygiRU1MIikNCg0KbGlicmFyeShFTUwpDQpgYGANCg0KVmFtb3MgdXRpbGl6YXIgdW0gY29uanVudG8gZGUgZGFkb3MgY29udGlkb3Mgbm8gcHLDs3ByaW8gcGFjb3RlIEVNTCBwYXJhIGZhY2lsaXRhciBhIGNvbXByZWVuc8OjbyBkb3MgcGFzc29zIG5lY2Vzc8OhcmlvcyBwYXJhIGNvbnN0cnVpciB1bSBFTUwgY29tcGxldG8NCg0KIyMgQ29uc3RydWluZG8gdW0gRU1MIC0gSGFydmFyZCBGb3Jlc3QgRGF0YSANCg0KIyMjIENvbnN0cnVpbmRvIG9zIEF0cmlidXRvcw0KDQpPcyBhdHJpYnV0b3Mgc8OjbyBhcyBkZXNjcmnDp8O1ZXMgcGFyYSBhcyB2YXJpw6F2ZWlzIGNvbnRpZGFzIG5hIG5vc3NhIHRhYmVsYS4gQXF1ZWxhIGFiYSBjb250ZW5kbyBhcyBpbmZvcm1hw6fDtWVzIGRhcyB2YXJpw6F2ZWlzLCBwb2lzIGVudMOjbywgZXN0YSBzZXJpYSBvcyBhdHJpYnV0b3MgZGEgc3VhIHRhYmVsYSBkZSBkYWRvcy4gRW50w6NvLCBwcmltZWlybyBwcmVjaXNhbW9zIGRlIHVtYSB0YWJlbGEgZGUgYXRyaWJ1dG9zIHF1ZSBkZWZpbmUgbyBzaWduaWZpY2FkbyBnZXJhbCBkZSBjYWRhIHZhcmnDoXZlbCBlbSBub3NzYSB0YWJlbGEgZGUgZGFkb3MuIEVzdGEgdGFiZWxhIHBvZGUgc2VyIGdlcmFkYSBhcXVpIG5vIFIgZGlyZXRvLCBjb21vIG5vIGV4ZW1wbG8gYWJhaXhvLCBtYXMgcG9kZSBzZXIgaW1wb3J0YWRhIGEgcGFydGlyIGRlIHVtYSB0YWJlbGEgYC5jc3ZgLiBBIHRhYmVsYSBzZWd1ZSBvIGZvcm1hdG8gYSBzZWd1aXI6DQoNCmBgYHtyfQ0KYXR0cmlidXRlcyA8LQ0KdGliYmxlOjp0cmliYmxlKA0KfmF0dHJpYnV0ZU5hbWUsIH5hdHRyaWJ1dGVEZWZpbml0aW9uLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+Zm9ybWF0U3RyaW5nLCB+ZGVmaW5pdGlvbiwgICAgICAgIH51bml0LCAgIH5udW1iZXJUeXBlLA0KICAicnVuLm51bSIsICAgICJ3aGljaCBydW4gbnVtYmVyICg9YmxvY2spLiBSYW5nZTogMSAtIDYuIChpbnRlZ2VyKSIsICAgICAgICAgICAgICAgICBOQSwgICAgICAgICAgICAid2hpY2ggcnVuIG51bWJlciIsIE5BLCAgICAgICBOQSwNCiAgInllYXIiLCAgICAgICAieWVhciwgMjAxMiIsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIllZWVkiLCAgICAgICAgTkEsICAgICAgICAgICAgICAgICBOQSwgICAgICAgTkEsDQogICJkYXkiLCAgICAgICAgIkp1bGlhbiBkYXkuIFJhbmdlOiAxNzAgLSAyMDkuIiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEREQiLCAgICAgICAgIE5BLCAgICAgICAgICAgICAgICAgTkEsICAgICAgIE5BLA0KICAiaG91ci5taW4iLCAgICJob3VyIGFuZCBtaW51dGUgb2Ygb2JzZXJ2YXRpb24uIFJhbmdlIDEgLSAyNDAwIChpbnRlZ2VyKSIsICAgICAgICAgICAiaGhtbSIsICAgICAgICBOQSwgICAgICAgICAgICAgICAgIE5BLCAgICAgICBOQSwNCiAgImkuZmxhZyIsICAgICAiaXMgdmFyaWFibGUgUmVhbCwgSW50ZXJwb2xhdGVkIG9yIEJhZCAoY2hhcmFjdGVyL2ZhY3RvcikiLCAgICAgICAgICAgTkEsICAgICAgICAgICAgTkEsICAgICAgICAgICAgICAgICBOQSwgICAgICAgTkEsDQogICJ2YXJpYWJsZSIsICAgIndoYXQgdmFyaWFibGUgYmVpbmcgbWVhc3VyZWQgaW4gd2hhdCB0cmVhdG1lbnQgKGNoYXJhY3Rlci9mYWN0b3IpLiIsIE5BLCAgICAgICAgICAgIE5BLCAgICAgICAgICAgICAgICAgTkEsICAgICAgIE5BLA0KICAidmFsdWUuaSIsICAgICJ2YWx1ZSBvZiBtZWFzdXJlZCB2YXJpYWJsZSBmb3IgcnVuLm51bSBvbiB5ZWFyL2RheS9ob3VyLm1pbi4iLCAgICAgICBOQSwgICAgICAgICAgICBOQSwgICAgICAgICAgICAgICAgIE5BLCAgICAgICBOQSwNCiAgImxlbmd0aCIsICAgICJsZW5ndGggb2YgdGhlIHNwZWNpZXMgaW4gbWV0ZXJzIChkdW1teSBleGFtcGxlIG9mIG51bWVyaWMgZGF0YSkiLCAgICAgTkEsICAgICAgICAgICAgTkEsICAgICAgICAgICAgICAgICAibWV0ZXIiLCAgInJlYWwiKQ0KYGBgDQoNCkTDqiB1bWEgb2xoYWRhIG5hIHRhYmVsYSBkZSBhdHJpYnV0b3MgKHBvZGUgc2VyIGZlaXRvIHVzYW5vIGEgZnVuw6fDo28gYFZpZXdgIG91IGFicmluZG8gbm8gRXhjZWwpLiBBIG1haW9yaWEgZG9zIGRhZG9zIHNlZ3VlbSBlc3RlIGZvcm1hdG8gZ2VyYWwuDQoNCkEgc2VndWlyIHByZWNpc2Ftb3MgZGVzY3JldmVyIG9zIGF0cmlidXRvcyBxdWUgYXByZXNlbnRhbSBuw612ZWlzLiBQb3IgZXhlbXBsbyBvIGF0cmlidXRvIGNoYW1hZG8gYHZhcmlhYmxlYCBhcHJlc2VudGEgb2l0byBuw612ZWlzLiBBIGVzdHJhdMOpZ2lhIHNlZ3VpZGEgYXF1aSBmb2kgY3JpYXIgdmV0b3JlcyBjb20gb3MgY8OzZGlnb3MgdXNhZG9zIHBhcmEgY2FkYSBuw612ZWwgZG9zIGF0cmlidXRvcyBlIHVtYSBwZXF1ZW5hIGV4cGxpY2HDp8Ojby4NCg0KYGBge3IgZWNobz1UUlVFLGV2YWw9RkFMU0V9DQppLmZsYWcgPC0gYyhSID0gInJlYWwiLA0KICAgICAgICAgICAgSSA9ICJpbnRlcnBvbGF0ZWQiLA0KICAgICAgICAgICAgQiA9ICJiYWQiKQ0KdmFyaWFibGUgPC0gYygNCiAgY29udHJvbCAgPSAibm8gcHJleSBhZGRlZCIsDQogIGxvdyAgICAgID0gIjAuMTI1IG1nIHByZXkgYWRkZWQgbWwtMSBkLTEiLA0KICBtZWQubG93ICA9ICIwLDI1IG1nIHByZXkgYWRkZWQgbWwtMSBkLTEiLA0KICBtZWQuaGlnaCA9ICIwLjUgbWcgcHJleSBhZGRlZCBtbC0xIGQtMSIsDQogIGhpZ2ggICAgID0gIjEuMCBtZyBwcmV5IGFkZGVkIG1sLTEgZC0xIiwNCiAgYWlyLnRlbXAgPSAiYWlyIHRlbXBlcmF0dXJlIG1lYXN1cmVkIGp1c3QgYWJvdmUgYWxsIHBsYW50cyAoMSB0aGVybW9jb3VwbGUpIiwNCiAgd2F0ZXIudGVtcCA9ICJ3YXRlciB0ZW1wZXJhdHVyZSBtZWFzdXJlZCB3aXRoaW4gZWFjaCBwaXRjaGVyIiwNCiAgcGFyICAgICAgID0gInBob3Rvc3ludGhldGljIGFjdGl2ZSByYWRpYXRpb24gKFBBUikgbWVhc3VyZWQganVzdCBhYm92ZSBhbGwgcGxhbnRzICgxIHNlbnNvcikiDQopDQoNCnZhbHVlLmkgPC0gYygNCiAgY29udHJvbCAgPSAiJSBkaXNzb2x2ZWQgb3h5Z2VuIiwNCiAgbG93ICAgICAgPSAiJSBkaXNzb2x2ZWQgb3h5Z2VuIiwNCiAgbWVkLmxvdyAgPSAiJSBkaXNzb2x2ZWQgb3h5Z2VuIiwNCiAgbWVkLmhpZ2ggPSAiJSBkaXNzb2x2ZWQgb3h5Z2VuIiwNCiAgaGlnaCAgICAgPSAiJSBkaXNzb2x2ZWQgb3h5Z2VuIiwNCiAgYWlyLnRlbXAgPSAiZGVncmVlcyBDIiwNCiAgd2F0ZXIudGVtcCA9ICJkZWdyZWVzIEMiLA0KICBwYXIgICAgICA9ICJtaWNyb21vbGVzIG0tMSBzLTEiDQopDQoNCiMjIFdyaXRlIHRoZXNlIGludG8gdGhlIGRhdGEuZnJhbWUgZm9ybWF0DQpmYWN0b3JzIDwtIHJiaW5kKA0KZGF0YS5mcmFtZSgNCiAgYXR0cmlidXRlTmFtZSA9ICJpLmZsYWciLA0KICBjb2RlID0gbmFtZXMoaS5mbGFnKSwNCiAgZGVmaW5pdGlvbiA9IHVubmFtZShpLmZsYWcpDQopLA0KZGF0YS5mcmFtZSgNCiAgYXR0cmlidXRlTmFtZSA9ICJ2YXJpYWJsZSIsDQogIGNvZGUgPSBuYW1lcyh2YXJpYWJsZSksDQogIGRlZmluaXRpb24gPSB1bm5hbWUodmFyaWFibGUpDQopLA0KZGF0YS5mcmFtZSgNCiAgYXR0cmlidXRlTmFtZSA9ICJ2YWx1ZS5pIiwNCiAgY29kZSA9IG5hbWVzKHZhbHVlLmkpLA0KICBkZWZpbml0aW9uID0gdW5uYW1lKHZhbHVlLmkpDQopDQopDQpgYGANCg0KQXDDs3MgZXNwZWNpZmljYWRhcyBhcyBjYXJhY3RlcsOtc3RpY2FzIGRhcyB2YXJpw6F2ZWlzIGNhdGVnw7NyaWNhcyAoYXMgY29udMOtbnVhcyBqw6EgZXN0w6NvIGVzcGVjaWZpY2FkYXMgbmEgdGFiZWxhIGdlcmFsIGRlIGF0cmlidXRvcyBgYXR0cmlidXRlc2ApLCB1c2Ftb3MgYSBmdW7Dp8OjbyBgc2V0X2F0dHJpYnV0ZXNgIHBhcmEgY3JpYXIgYSBsaXN0YSBkZSBhdHJpYnV0b3MgZ2VyYWwgZG9zIGRhZG9zLCBvbmRlIGRldmVtb3MgZXNwZWNpZmljYXIgbyB0aXBvIGRlIGF0cmlidXRvIHF1ZSBlc3RhbW9zIGluc2VyaW5kbyAoc2UgY2FyYWN0ZXIsIHNlIGRhdGEsIHNlIGZhdG9yIGV0Yy4uKQ0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NCmF0dHJpYnV0ZUxpc3QgPC0gc2V0X2F0dHJpYnV0ZXMoYXR0cmlidXRlcywgZmFjdG9ycywgY29sX2NsYXNzZXMgPSBjKCJjaGFyYWN0ZXIiLCAiRGF0ZSIsICJEYXRlIiwgIkRhdGUiLCAiZmFjdG9yIiwgImZhY3RvciIsICJmYWN0b3IiLCAibnVtZXJpYyIpKQ0KYGBgDQoNCiMjIyBDb25zdHJ1aW5kbyBhcyBjYXJhY3RlcsOtc3RpY2FzIGbDrXNpY2FzIGRvIGRhZG8NCg0KQXF1aSB2YW1vcyBlc3BlY2lmaWNhciBhcyBjYXJhY3RlcsOtc3RpY2FzIGbDrXNpY2FzIGRvIHByw7NwcmlvIGFycXVpdm8gZGUgZGFkb3MgcXVlIHV0aWxpemFtb3MsIG91IHNlamEsIG8gZm9ybWF0bywgbyB0aXBvIGRlIGNvbXByZXNzw6NvIGRvIGFycXVpdm8sIG8gc2VwYXJhZG9yIGV0Yy4gUmVzdW1pbmRvLCBhcXVpIMOpIGVzcGVjaWZpY2FkbyBhICoqY2FyYWN0ZXLDrXN0aWNhIGRvIGFycXVpdm8qKi4gSXNzbyDDqSBmZWl0byBmYWNpbG1lbnRlIGNvbSBhIGZ1bsOnw6NvIGBzZXRfcGh5c2ljYWxgLiBTZSBlc3RpdmVybW9zIHV0aWxpemFuZG8gdW0gYXJxdWl2byAuY3N2IGEgZnVuw6fDo28gasOhIGV4dHJhaSBhdXRvbWF0aWNhbWVudGUgYSBtYWlvciBwYXJ0ZSBkYXMgY2FyYWN0ZXLDrXN0aWNhcyByZWxldmFudGVzIGRvIGFycXVpdm8gZGUgZGFkb3MuDQoNCmBgYHtyIGVjaG89VFJVRSxldmFsPUZBTFNFfQ0KcGh5c2ljYWwgPC0gc2V0X3BoeXNpY2FsKGhlcmU6OmhlcmUoImRhdGEiLCAiaGYyMDUtMDEtVFBleHAxLUVNTC1FeGVtcGxlLmNzdiIpKQ0KYGBgDQoNCiMjIyBKdW50YW5kbyBBdHJpYnV0b3MgZSBDYXJhY3RlcsOtc3RpY2FzIGbDrXNpY2FzDQoNCkNvbSBvIG9iamV0byBkZSBhdHJpYnV0b3MgZSBvIG9iamV0byBkZSBjYXJhY3RlcsOtc3RpY2FzIGbDrXNpY2FzIHRlbW9zIG8gZXNzZW5jaWFsIHBhcmEgZGVzY3JldmVyIG8gbm9zc28gY29uanVudG8gZGUgZGFkb3MuIEFnb3JhIHZhbW9zIGp1bnRhciBhbWJvcyBjb20gbWFpcyBhbGd1bWFzIGluZm9ybWHDp8O1ZXMgZW0gdW1hIMO6bmljYSBsaXN0YSBlIGRlbm9taW5hciBpc3NvIGNvbW8gc2VuZG8gbyBub3NzbyBgZGF0YVRhYmxlYA0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NCg0KZGF0YVRhYmxlIDwtIGxpc3QoDQogICAgICAgICAgICAgICAgIGVudGl0eU5hbWUgPSAiaGYyMDUtMDEtVFBleHAxLmNzdiIsDQogICAgICAgICAgICAgICAgIGVudGl0eURlc2NyaXB0aW9uID0gInRpcHBpbmcgcG9pbnQgZXhwZXJpbWVudCAxIiwNCiAgICAgICAgICAgICAgICAgcGh5c2ljYWwgPSBwaHlzaWNhbCwNCiAgICAgICAgICAgICAgICAgYXR0cmlidXRlTGlzdCA9IGF0dHJpYnV0ZUxpc3QpDQpgYGANCg0KIyMjIENhcmFjdGVyw61zdGljYXMgYWRpY2lvbmFpcyAtIGV4dGVuc8OjbyBnZW9ncsOhZmljYSwgbcOpdG9kb3MgZSBvdXRyb3MNCg0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NCmdlb2dyYXBoaWNEZXNjcmlwdGlvbiA8LSAiSGFydmFyZCBGb3Jlc3QgR3JlZW5ob3VzZSwgVG9tIFN3YW1wIFRyYWN0IChIYXJ2YXJkIEZvcmVzdCkiDQoNCg0KY292ZXJhZ2UgPC0gDQogIHNldF9jb3ZlcmFnZShiZWdpbiA9ICcyMDEyLTA2LTAxJywgZW5kID0gJzIwMTMtMTItMzEnLA0KICAgICAgICAgICAgICAgc2NpX25hbWVzID0gIlNhcnJhY2VuaWEgcHVycHVyZWEiLA0KICAgICAgICAgICAgICAgZ2VvZ3JhcGhpY0Rlc2NyaXB0aW9uID0gZ2VvZ3JhcGhpY0Rlc2NyaXB0aW9uLA0KICAgICAgICAgICAgICAgd2VzdCA9IC0xMjIuNDQsIGVhc3QgPSAtMTE3LjE1LCANCiAgICAgICAgICAgICAgIG5vcnRoID0gMzcuMzgsIHNvdXRoID0gMzAuMDAsDQogICAgICAgICAgICAgICBhbHRpdHVkZU1pbiA9IDE2MCwgYWx0aXR1ZGVNYXhpbXVtID0gMzMwLA0KICAgICAgICAgICAgICAgYWx0aXR1ZGVVbml0cyA9ICJtZXRlciIpDQoNCg0KYGBgDQoNCg0KUGFyYSBkZXNjcmV2ZXIgb3MgbcOpdG9kb3MgZW0gbWV0YWRhZG9zDQoNCmBgYHtyIGVjaG89VFJVRSxldmFsPUZBTFNFfQ0KDQptZXRob2RzX2ZpbGUgPC0gaGVyZTo6aGVyZSgiZGF0YSIsICJoZjIwNS1tZXRob2RzLmRvY3giKQ0KbWV0aG9kcyA8LSBzZXRfbWV0aG9kcyhtZXRob2RzX2ZpbGUpDQpgYGANCg0KSWRlbnRpZmljYcOnw6NvIGRvcyBpbmRpdsOtZHVvcyBlbmRlcmXDp28gZSBvdXRyYXMgaW5mb3JtYcOnw7VlcyBzb2JyZSBjb250YXRvcw0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NClJfcGVyc29uIDwtIHBlcnNvbigiQWFyb24iLCAiRWxsaXNvbiIsICwiZmFrZWFkZHJlc3NAZW1haWwuY29tIiwgImNyZSIsIA0KICAgICAgICAgICAgICAgICAgYyhPUkNJRCA9ICIwMDAwLTAwMDMtNDE1MS02MDgxIikpDQphYXJvbiA8LSBhc19lbWxkKFJfcGVyc29uKQ0Kb3RoZXJzIDwtIGMoYXMucGVyc29uKCJCZW5qYW1pbiBCYWlzZXIiKSwgYXMucGVyc29uKCJKZW5uaWZlciBTaXJvdGEiKSkNCmFzc29jaWF0ZWRQYXJ0eSA8LSBhc19lbWxkKG90aGVycykNCmFzc29jaWF0ZWRQYXJ0eVtbMV1dJHJvbGUgPC0gIlJlc2VhcmNoZXIiDQphc3NvY2lhdGVkUGFydHlbWzJdXSRyb2xlIDwtICJSZXNlYXJjaGVyIg0KDQpIRl9hZGRyZXNzIDwtIGxpc3QoDQogICAgICAgICAgICAgICAgICBkZWxpdmVyeVBvaW50ID0gIjMyNCBOb3J0aCBNYWluIFN0cmVldCIsDQogICAgICAgICAgICAgICAgICBjaXR5ID0gIlBldGVyc2hhbSIsDQogICAgICAgICAgICAgICAgICBhZG1pbmlzdHJhdGl2ZUFyZWEgPSAiTUEiLA0KICAgICAgICAgICAgICAgICAgcG9zdGFsQ29kZSA9ICIwMTM2NiIsDQogICAgICAgICAgICAgICAgICBjb3VudHJ5ID0gIlVTQSIpDQoNCnB1Ymxpc2hlciA8LSBsaXN0KA0KICAgICAgICAgICAgICAgICBvcmdhbml6YXRpb25OYW1lID0gIkhhcnZhcmQgRm9yZXN0IiwNCiAgICAgICAgICAgICAgICAgYWRkcmVzcyA9IEhGX2FkZHJlc3MpDQoNCg0KY29udGFjdCA8LSANCiAgbGlzdCgNCiAgICBpbmRpdmlkdWFsTmFtZSA9IGFhcm9uJGluZGl2aWR1YWxOYW1lLA0KICAgIGVsZWN0cm9uaWNNYWlsQWRkcmVzcyA9IGFhcm9uJGVsZWN0cm9uaWNNYWlsQWRkcmVzcywNCiAgICBhZGRyZXNzID0gSEZfYWRkcmVzcywNCiAgICBvcmdhbml6YXRpb25OYW1lID0gIkhhcnZhcmQgRm9yZXN0IiwNCiAgICBwaG9uZSA9ICIwMDAtMDAwLTAwMDAiKQ0KYGBgDQoNCg0KY3JpYW5kbyBrZXl3b3JkcyBwYXJhIG9zIGRhZG9zDQoNCmBgYHtyIGVjaG89VFJVRSxldmFsPUZBTFNFfQ0Ka2V5d29yZFNldCA8LSBsaXN0KA0KICAgIGxpc3QoDQogICAgICAgIGtleXdvcmRUaGVzYXVydXMgPSAiTFRFUiBjb250cm9sbGVkIHZvY2FidWxhcnkiLA0KICAgICAgICBrZXl3b3JkID0gbGlzdCgiYmFjdGVyaWEiLA0KICAgICAgICAgICAgICAgICAgICAiY2Fybml2b3JvdXMgcGxhbnRzIiwNCiAgICAgICAgICAgICAgICAgICAgImdlbmV0aWNzIiwNCiAgICAgICAgICAgICAgICAgICAgInRocmVzaG9sZHMiKQ0KICAgICAgICApLA0KICAgIGxpc3QoDQogICAgICAgIGtleXdvcmRUaGVzYXVydXMgPSAiTFRFUiBjb3JlIGFyZWEiLA0KICAgICAgICBrZXl3b3JkID0gIGxpc3QoInBvcHVsYXRpb25zIiwgImlub3JnYW5pYyBudXRyaWVudHMiLCAiZGlzdHVyYmFuY2UiKQ0KICAgICAgICApLA0KICAgIGxpc3QoDQogICAgICAgIGtleXdvcmRUaGVzYXVydXMgPSAiSEZSIGRlZmF1bHQiLA0KICAgICAgICBrZXl3b3JkID0gbGlzdCgiSGFydmFyZCBGb3Jlc3QiLCAiSEZSIiwgIkxURVIiLCAiVVNBIikNCiAgICAgICAgKSkNCmBgYA0KT3V0cmFzIGluZm9ybWHDp8O1ZXMgcmVsZXZhbnRlcyBjb21vIGFic3RyYWN0IGV0Yw0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NCnB1YkRhdGUgPC0gIjIwMTIiIA0KDQp0aXRsZSA8LSAiVGhyZXNob2xkcyBhbmQgVGlwcGluZyBQb2ludHMgaW4gYSBTYXJyYWNlbmlhIA0KTWljcm9lY29zeXN0ZW0gYXQgSGFydmFyZCBGb3Jlc3Qgc2luY2UgMjAxMiINCg0KYWJzdHJhY3QgPC0gIlRoZSBwcmltYXJ5IGdvYWwgb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGRldGVybWluZQ0KICBleHBlcmltZW50YWxseSB0aGUgYW1vdW50IG9mIGxlYWQgdGltZSByZXF1aXJlZCB0byBwcmV2ZW50IGEgc3RhdGUNCmNoYW5nZS4gVG8gYWNoaWV2ZSB0aGlzIGdvYWwsIHdlIHdpbGwgKDEpIGV4cGVyaW1lbnRhbGx5IGluZHVjZSBzdGF0ZQ0KY2hhbmdlcyBpbiBhIG5hdHVyYWwgYXF1YXRpYyBlY29zeXN0ZW0gLSB0aGUgU2FycmFjZW5pYSBtaWNyb2Vjb3N5c3RlbTsNCigyKSB1c2UgcHJvdGVvbWljIGFuYWx5c2lzIHRvIGlkZW50aWZ5IHBvdGVudGlhbCBpbmRpY2F0b3JzIG9mIHN0YXRlcw0KYW5kIHN0YXRlIGNoYW5nZXM7IGFuZCAoMykgdGVzdCB3aGV0aGVyIHdlIGNhbiBmb3Jlc3RhbGwgc3RhdGUgY2hhbmdlcw0KYnkgZXhwZXJpbWVudGFsbHkgaW50ZXJ2ZW5pbmcgaW4gdGhlIHN5c3RlbS4gVGhpcyB3b3JrIHVzZXMgc3RhdGUtb2YtdGhlDQphcnQgbW9sZWN1bGFyIHRvb2xzIHRvIGlkZW50aWZ5IGVhcmx5IHdhcm5pbmcgaW5kaWNhdG9ycyBpbiB0aGUgZmllbGQNCm9mIGFlcm9iaWMgdG8gYW5hZXJvYmljIHN0YXRlIGNoYW5nZXMgZHJpdmVuIGJ5IG51dHJpZW50IGVucmljaG1lbnQNCmluIGFuIGFxdWF0aWMgZWNvc3lzdGVtLiBUaGUgc3R1ZHkgdGVzdHMgdHdvIGdlbmVyYWwgaHlwb3RoZXNlczogKDEpDQpwcm90ZW9taWMgYmlvbWFya2VycyBjYW4gZnVuY3Rpb24gYXMgcmVsaWFibGUgaW5kaWNhdG9ycyBvZiBpbXBlbmRpbmcNCnN0YXRlIGNoYW5nZXMgYW5kIG1heSBnaXZlIGVhcmx5IHdhcm5pbmcgYmVmb3JlIGluY3JlYXNpbmcgdmFyaWFuY2VzDQphbmQgc3RhdGlzdGljYWwgZmxpY2tlcmluZyBvZiBtb25pdG9yZWQgdmFyaWFibGVzOyBhbmQgKDIpIHdlbGwtdGltZWQNCmludGVydmVudGlvbiBiYXNlZCBvbiBwcm90ZW9taWMgYmlvbWFya2VycyBjYW4gYXZlcnQgZnV0dXJlIHN0YXRlIGNoYW5nZXMNCmluIGVjb2xvZ2ljYWwgc3lzdGVtcy4iICANCg0KaW50ZWxsZWN0dWFsUmlnaHRzIDwtICJUaGlzIGRhdGFzZXQgaXMgcmVsZWFzZWQgdG8gdGhlIHB1YmxpYyBhbmQgbWF5IGJlIGZyZWVseQ0KICBkb3dubG9hZGVkLiBQbGVhc2Uga2VlcCB0aGUgZGVzaWduYXRlZCBDb250YWN0IHBlcnNvbiBpbmZvcm1lZCBvZiBhbnkNCnBsYW5zIHRvIHVzZSB0aGUgZGF0YXNldC4gQ29uc3VsdGF0aW9uIG9yIGNvbGxhYm9yYXRpb24gd2l0aCB0aGUgb3JpZ2luYWwNCmludmVzdGlnYXRvcnMgaXMgc3Ryb25nbHkgZW5jb3VyYWdlZC4gUHVibGljYXRpb25zIGFuZCBkYXRhIHByb2R1Y3RzDQp0aGF0IG1ha2UgdXNlIG9mIHRoZSBkYXRhc2V0IG11c3QgaW5jbHVkZSBwcm9wZXIgYWNrbm93bGVkZ2VtZW50LiBGb3INCm1vcmUgaW5mb3JtYXRpb24gb24gTFRFUiBOZXR3b3JrIGRhdGEgYWNjZXNzIGFuZCB1c2UgcG9saWNpZXMsIHBsZWFzZQ0Kc2VlOiBodHRwOi8vd3d3Lmx0ZXJuZXQuZWR1L2RhdGEvbmV0cG9saWN5Lmh0bWwuIg0KYGBgDQoNCiMjIyBDcmlhbmRvIG8gYXJxdWl2byBYTUwNCg0KQWdvcmEgcXVlIHRvZGFzIGFzIGluZm9ybWHDp8O1ZXMgZm9yYW0gY3JpYWRhcyAoYXRyaWJ1dG9zLCBkYWRvcyBmw61zaWNvcyBkbyBhcnF1aXZvLCBjb3ZlcmFnZSwgZGFkb3MgZGUgY29udGF0byksIHByZWNpc2Ftb3MganVudGFyIHRvZGFzIGVsYXMgZW0gdW1hIMO6bmljYSBsaXN0YS4NCg0KYGBge3IgZWNobz1UUlVFLGV2YWw9RkFMU0V9DQpkYXRhc2V0IDwtIGxpc3QoDQogICAgICAgICAgICAgICB0aXRsZSA9IHRpdGxlLA0KICAgICAgICAgICAgICAgY3JlYXRvciA9IGFhcm9uLA0KICAgICAgICAgICAgICAgcHViRGF0ZSA9IHB1YkRhdGUsDQogICAgICAgICAgICAgICBpbnRlbGxlY3R1YWxSaWdodHMgPSBpbnRlbGxlY3R1YWxSaWdodHMsDQogICAgICAgICAgICAgICBhYnN0cmFjdCA9IGFic3RyYWN0LA0KICAgICAgICAgICAgICAgYXNzb2NpYXRlZFBhcnR5ID0gYXNzb2NpYXRlZFBhcnR5LA0KICAgICAgICAgICAgICAga2V5d29yZFNldCA9IGtleXdvcmRTZXQsDQogICAgICAgICAgICAgICBjb3ZlcmFnZSA9IGNvdmVyYWdlLA0KICAgICAgICAgICAgICAgY29udGFjdCA9IGNvbnRhY3QsDQogICAgICAgICAgICAgICBtZXRob2RzID0gbWV0aG9kcywNCiAgICAgICAgICAgICAgIGRhdGFUYWJsZSA9IGRhdGFUYWJsZSkNCmBgYA0KDQpBbnRlcyBkZSBjcmlhciBvIGFycXVpdm8gWE1MIMOpIGltcG9ydGFudGUgY3JpYXIgdW0gdXVpZCBwYXJhIG8gYXJxdWl2bywgcXVlIG5hZGEgbWFpcyDDqSBxdWUgdW0gaWRlbnRpZmljYWRvciBxdWUgcXVhbHF1ZXIgdW0gcG9kZSBjcmlhciwgY29tbyBkZW1vbnN0cmFkbyBhIHNlZ3Vpci4gUGFyYSBtYWlzIGluZm9ybWHDp8O1ZXMgc29icmUgbyBxdWUgw6kgdW0gdXVpZCB2ZXIgW2VzdGEgZXhwbGljYcOnw6NvIG5vIFdpa2lwZWRpYV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvVW5pdmVyc2FsbHlfdW5pcXVlX2lkZW50aWZpZXIpDQoNCmBgYHtyIGVjaG89VFJVRSxldmFsPUZBTFNFfQ0KZW1sIDwtIGxpc3QoDQogICAgICAgICAgIHBhY2thZ2VJZCA9IHV1aWQ6OlVVSURnZW5lcmF0ZSgpLA0KICAgICAgICAgICBzeXN0ZW0gPSAidXVpZCIsICMgdHlwZSBvZiBpZGVudGlmaWVyDQogICAgICAgICAgIGRhdGFzZXQgPSBkYXRhc2V0KQ0KYGBgDQoNCkZpbmFsbWVudGUgY3JpYW5kbyBvIGFycXVpdm8gWE1MIGUgdmFsaWRhbmRvLg0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NCndyaXRlX2VtbChlbWwsIGhlcmU6OmhlcmUoImRhdGEiLCAiZW1sX3R1dG9yaWFsSEYyMDUueG1sIikpDQplbWxfdmFsaWRhdGUoaGVyZTo6aGVyZSgiZGF0YSIsICJlbWxfdHV0b3JpYWxIRjIwNS54bWwiKSkNCmBgYA0KDQoNCjwhLS0gYXF1aSBhIHNlw6fDo28gZGUgbWV0YWRhdGEgcGFyYSBkYWRvcyBkZSBiaW9kaXZlcnNpZGFkZSB1c2FuZG8gRHdDb3JlIC0tPg0KDQpgYGB7ciBjaGlsZD0iRGFyd2luLWNvcmUtTE5QLlJtZCIsZXZhbD1UUlVFfQ0KYGBgDQoNCg0KIyBBdGl2aWRhZGUNCg0KUGFyYSBmaXhhciBvcyBjb25jZWl0b3MgYXByZXNlbnRhZG9zIHNvYnJlIG1ldGFkYWRvcywgdmFtb3MgdHJlaW5hciB1bSBwb3VjbyBtYWlzIHV0aWxpemFuZG8gdW0gb3V0cm8gY29uanVudG8gZGUgZGFkb3MuIA0KDQpQcm9kdXphIHVtIGFycXVpdm8gYC54bWxgIHV0aWxpemFuZG8gb3MgZGFkb3MgYGlyaXNgIGRvIHBhY290ZSBge2dncGxvdH1gLiBBbGd1bWFzIGluZm9ybWHDp8O1ZXMgbsOzcyBuw6NvIHRlbW9zLCBwb3IgZXhlbXBsbywgYSBjb2JlcnR1cmEgZGUgY29sZXRhLCBhbm8gZGUgY29sZXRhIGV0Yy4gTWFzIHBhcmEgZmlucyBkZSBwcsOhdGljYSB2YW1vcyBpbnZlbnRhciBlc3NhcyBpbmZvcm1hw6fDtWVzIG5vIG1vbWVudG8gZGUgZmF6ZXIgbyBtZXRhZGFkby4NCg0KUGFyYSBhY2Vzc2FyIG9zIGRhZG9zOg0KDQpgYGB7ciBlY2hvPVRSVUUsZXZhbD1GQUxTRX0NCmxpYnJhcnkoZ2dwbG90MikNCmRhdGEoImlyaXMiKQ0KYGBgDQoNCg0KDQoNCg==