1 Introduction

Najib asked for an initial conversion of the data provided by Maria Adelaida into the various formats expected by dbgap. He sent me two sheets provided by her: “20210115_EXP_ESPECIAL_TMRC3.xlsx” and “CODEBOOK_EXP_ESPECIAL_TMRC3_20210115.xlsx”. The first provides the data and the second for formatting.

In addition, we will require some of the data in the shared sample sheet.

With that in mind, here are our inputs: 1. The metadata sheet of patients. 2. The meta metadata sheet, the ‘codebook’. 3. The tmrc3 sample sheet. I am likely to add a worksheet to it to contain the meta metadata.

Here are the expected outputs: 1. SubjectConsent_DS.txt: Tab separated, containing SUBJECT_ID, CONSENT, SEX, SUBJECT_SOURCE, and SUBJECT_SOURCE_ID In my current setup, this is only the patient ID, 1, sex (1,2,NA or whatever), TMRC3, and the patient ID repeated. 2. SSM_DS.txt: Tab separated, containing SUBJECT_ID and SAMPLE_ID. This is the cross reference between the sample sheet and patient IDs, so it will require some sort of merge between the tmrc3 sample sheet and the EXP_ESPECIAL worksheet. 3. SubjectPhenotypes_DS.txt: This is pretty much everything else from the EXP_ESPECIAL sheet with at least the first column needing to be renamed to ‘SUBJECT_ID’ and probably the birthday removed. 4. SampleAttributes_DS.txt: This is pretty much everything else from the sample sheet. A bunch of columns will need to be removed. 5. SubjectConsent_DD.xlsx: xlsx file, the meta-metadata for #1 above. I think we can just create this de-novo and leave it unmodified because it is quite small. 6. SSM_DD.xlsx: Ditto. This just explains the columns from #2 above and is therefore even smaller. 7. SubjectPhenotypes_DD.xlsx: This is the CODEBOOK from above and will likely need some changes, but I think they are pretty minor. 8. SampleAttributes_DD.xlsx: I want to add this to the tmrc3 sample sheet and use it to define which columns to pull from it.

2 Reading existing data

rundate <- format(Sys.Date(), format = "%Y%m%d")
phenotype_file <- "inputs/202401/20220721_EXP_ESPECIAL_TMRC3_V3.xlsx"
phenotype_meta <- "inputs/202401/CODEBOOK_EXP_ESPECIAL_TMRC3_VERSION_3_20220512.xlsx"
sample_file <- "inputs/202401/tmrc3_samples_pruned.xlsx"
sample_meta <- "templates/SampleAttributes_DD.xlsx"

subject_phenotypes <- openxlsx::read.xlsx(phenotype_file)
subject_meta <- openxlsx::read.xlsx(phenotype_meta)
sample_attributes <- openxlsx::read.xlsx(sample_file)
sample_meta <- openxlsx::read.xlsx(sample_meta)

created <- dir.create(glue("outputs/{rundate}"))
## Warning in dir.create(glue("outputs/{rundate}")): 'outputs/20240208' already exists

3 Sanitize column names slightly.

There are a few things which are required by dbGap which I can trivially change here.

## Sanitize the column names a little.
colnames(subject_phenotypes) <- toupper(colnames(subject_phenotypes))

subject_meta[["VARNAME"]] <- toupper(subject_meta[["VARNAME"]])

colnames(sample_attributes) <- toupper(colnames(sample_attributes))
colnames(sample_attributes) <- gsub(pattern = "\\.+",
                                    replacement = "_",
                                    x = colnames(sample_attributes))
colnames(sample_attributes) <- gsub(pattern = "\\)|\\(|\\[|\\]|,|-|/|%|'|´|\\s+",
                                    replacement = "",
                                    x = colnames(sample_attributes),
                                    perl = TRUE)
subject_colname_substitutions <- list(
    "CODIGO_PACIENTE" = "SUBJECT_ID",
    "EB_LC_SEXO" = "SEX",
    "TUBE_LABEL_ORIGIN" = "SUBJECT_ID",
    "SAMPLE_NAME" = "SAMPLE_ID",
    "TMRC_IDENTIFIER" = "TMRC_ID"
)
for (i in 1:length(subject_colname_substitutions)) {
  from <- names(subject_colname_substitutions)[i]
  to <- subject_colname_substitutions[[i]]
  colnames(subject_phenotypes) <- gsub(pattern = from, replacement = to,
                                       x = colnames(subject_phenotypes))
  colnames(sample_attributes) <- gsub(pattern = from, replacement = to,
                                      x = colnames(sample_attributes))
  subject_meta[["VARNAME"]] <- gsub(pattern = from, replacement = to,
                                    x = subject_meta[["VARNAME"]])
}

4 Sanity check

Now that I have made some changes to the metadata, let us attempt to make certain that they still match. This primarily refers to the column names of the subject_phenotype file and the VARNAME column from the subject metadata file.

misses <- subject_meta[["VARNAME"]] != colnames(subject_phenotypes)
summary(misses)
##    Mode   FALSE 
## logical      63
if (sum(misses) > 0) {
  stop("There is a mismatch between the metadata and column names.")
}

5 Filter out samples which were not sequenced.

We only want those samples for which we have extant TMRC IDs. Thus, filter the sample_attributes for the samples that have been given tmrc IDs for now.

useful_idx <- !is.na(sample_attributes[["TMRC_ID"]])
sample_attributes <- sample_attributes[useful_idx, ]

6 Create the SubjectConsent_DS.txt

Creating the SubjectConsent_DS.txt should be the easiest, just pull the ID and SEX columns and fill in the rest.

SubjectConsent_DS <- subject_phenotypes[, c("SUBJECT_ID", "SEX")]
SubjectConsent_DS[["CONSENT"]] <- 1
SubjectConsent_DS[["SUBJECT_SOURCE"]] <- "TMRC3"
SubjectConsent_DS[["SUBJECT_SOURCE_ID"]] <- SubjectConsent_DS[["SUBJECT_ID"]]
## And reorder it
column_order <- c("SUBJECT_ID", "CONSENT", "SEX", "SUBJECT_SOURCE", "SUBJECT_SOURCE_ID")
SubjectConsent_DS <- SubjectConsent_DS[, column_order]
readr::write_tsv(x = SubjectConsent_DS,
                 file = glue("outputs/{rundate}/{rundate}-SubjectConsent_DS.txt"))

7 Create the SubjectConsent_DD.xlsx

This file should be unchanged from the template, so we will read it in and immediately write it back out with a blank line in between in case we do in fact need to change something later.

SubjectConsent_DD <- openxlsx::read.xlsx("templates/SubjectConsent_DD.xlsx")


openxlsx::write.xlsx(x = SubjectConsent_DD,
                     file = glue("outputs/{rundate}/{rundate}-SubjectConsent_DD.xlsx"))

8 Create the SSM_DS.txt

The SSM_DS.txt will be more difficult, it requires a merge between sheets…

sample_attributes[["SUBJECT_ID"]] <- gsub(pattern = "^su", replacement = "SU",
                                          x = sample_attributes[["SUBJECT_ID"]])
SSM_DS <- merge(SubjectConsent_DS, sample_attributes, by = "SUBJECT_ID")
wanted_columns <- c("SUBJECT_ID", "SAMPLE_ID")
SSM_DS <- SSM_DS[, wanted_columns]
readr::write_tsv(x = SSM_DS, file = glue("outputs/{rundate}/{rundate}-SSM_DS.txt"))

9 Create the SSM_DD.xlsx

Once again, we will assume that copying this from the template will prove sufficient; but will perform explicit read/write steps in case we need to change anything.

SSM_DD <- openxlsx::read.xlsx("templates/SSM_DD.xlsx")


openxlsx::write.xlsx(x = SSM_DD, file = glue("outputs/{rundate}/{rundate}-SSM_DD.xlsx"))

10 Create the SubjectPhenotypes_DD

Normally, I would move the english-translated columns to the defaults; but we do not have a translated version of this at this time.

## I do not think I need to do anything else to the metadata file at this time.
SubjectPhenotypes_DD <- subject_meta

10.1 Juggle the columns

I cannot run the following block at this time because the current file has not been translated.

## We will need to more VARNAME_ENG to VARNAME
SubjectPhenotypes_DD[["VARNAME"]] <- SubjectPhenotypes_DD[["VARNAME_ENG"]]
SubjectPhenotypes_DD[["VARNAME_ENG"]] <- NULL
## Ditto for VARDESC and TYPE
SubjectPhenotypes_DD[["VARDESC"]] <- SubjectPhenotypes_DD[["VARDESC_ENG"]]
SubjectPhenotypes_DD[["VARDESC_ENG"]] <- NULL
SubjectPhenotypes_DD[["TYPE"]] <- SubjectPhenotypes_DD[["TYPE_ENG"]]
SubjectPhenotypes_DD[["TYPE_ENG"]] <- NULL
## Having done that, we need to set the column names of the SubjectPhenotypes_DS to the new
## VARNAME column.

11 Write out the SubjectPhenotypes_DD

starting_ds_colnames <- SubjectPhenotypes_DD[["VARNAME"]]
openxlsx::write.xlsx(x = SubjectPhenotypes_DD,
                     file = glue("outputs/{rundate}/{rundate}-SubjectPhenotypes_DD.xlsx"))

12 Create the SubjectPhenotypes_DS

The SubjectPhenotypes_DS is just the original subject_phenotypes with some columns blacklisted.

subject_phenotypes_blacklist <- c("EB_LC_FECHA_NACIMIENTO", "SEX")
SubjectPhenotypes_DS <- subject_phenotypes
colnames(SubjectPhenotypes_DS) <- starting_ds_colnames
for (i in subject_phenotypes_blacklist) {
  SubjectPhenotypes_DS[[i]] <- NULL
  meta_keepers <- subject_meta[["VARNAME"]] != i
  subject_meta <- subject_meta[meta_keepers, ]
}
readr::write_tsv(x = SubjectPhenotypes_DS,
                 file = glue("outputs/{rundate}/{rundate}-SubjectPhenotypes_DS.txt"))

13 Create the SampleAttributes_DS

The SampleAttributes_DS will require some more work: 1. Read in the SampleAttributes_DD and keep only the columns defined in it. 2. Recast the data to ensure they are pure text.

Currently that is all we are doing, so it doesn’t really require very much.

I do need to change the column ‘FINAL_OUTCOME’ to ‘CLINICAL_OUTCOME’

colnames(sample_attributes) <- gsub(x = colnames(sample_attributes), pattern = "FINAL_OUTCOME",
                                    replacement = "CLINICAL_OUTCOME")
SampleAttributes_DS <- sample_attributes
kept_columns <- sample_meta[["VARNAME"]]

kept_columns[! kept_columns %in% colnames(SampleAttributes_DS)]
## character(0)
SampleAttributes_DS <- SampleAttributes_DS[, kept_columns]
for (i in 1:ncol(SampleAttributes_DS)) {
  SampleAttributes_DS[[i]] <- as.character(SampleAttributes_DS[[i]])
}
readr::write_tsv(x = SampleAttributes_DS,
                 file = glue("outputs/{rundate}/{rundate}-SampleAttributes_DS.txt"))

14 Write the SampleAttributes_DD

Once again, the DD file is expected to be identical to our template.

SampleAttributes_DD <- sample_meta
openxlsx::write.xlsx(x = SampleAttributes_DD,
                     file = glue("outputs/{rundate}/{rundate}-SampleAttributes_DD.xlsx"))
LS0tCnRpdGxlOiAiQ29udmVydGluZyBtZXRhZGF0YSBmb3IgZGJnYXAhIgphdXRob3I6ICJhdGIgYWJlbGV3QGdtYWlsLmNvbSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBzZWxmX2NvbnRhaW5lZDogdHJ1ZQogICAgdGhlbWU6IHJlYWRhYmxlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBmYWxzZQogICAgICBzbW9vdGhfc2Nyb2xsOiBmYWxzZQogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBkZl9wcmludDogcGFnZWQKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBmaWdfaGVpZ2h0OiA3CiAgICBmaWdfd2lkdGg6IDcKICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgd2lkdGg6IDMwMAogICAga2VlcF9tZDogZmFsc2UKICAgIG1vZGU6IHNlbGZjb250YWluZWQKICAgIHRvY19mbG9hdDogdHJ1ZQogIEJpb2NTdHlsZTo6aHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZmlnX2NhcHRpb246IHRydWUKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogNwogICAgaGlnaGxpZ2h0OiB6ZW5idXJuCiAgICBrZWVwX21kOiBmYWxzZQogICAgbW9kZTogc2VsZmNvbnRhaW5lZAogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keSwgdGQgewogIGZvbnQtc2l6ZTogMTZweDsKfQpjb2RlLnJ7CiAgZm9udC1zaXplOiAxNnB4Owp9CnByZSB7CiBmb250LXNpemU6IDE2cHgKfQpib2R5IC5tYWluLWNvbnRhaW5lciB7CiAgbWF4LXdpZHRoOiAxNjAwcHg7Cn0KPC9zdHlsZT4KCmBgYHtyIG9wdGlvbnMsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoaHBnbHRvb2xzKQpsaWJyYXJ5KGdsdWUpCmxpYnJhcnkocmV0aWN1bGF0ZSkKdHQgPC0gdHJ5KGRldnRvb2xzOjpsb2FkX2FsbCgifi9ocGdsdG9vbHMiKSkKa25pdHI6Om9wdHNfa25pdCRzZXQoCiAgcHJvZ3Jlc3MgPSBUUlVFLCB2ZXJib3NlID0gVFJVRSwgd2lkdGggPSA5MCwgZWNobyA9IFRSVUUpCmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBlcnJvciA9IFRSVUUsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSA4LCBmaWcucmV0aW5hID0gMiwKICBmaWcucG9zID0gInQiLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgZHBpID0gaWYgKGtuaXRyOjppc19sYXRleF9vdXRwdXQoKSkgNzIgZWxzZSAzMDAsCiAgb3V0LndpZHRoID0gIjEwMCUiLCBkZXYgPSAicG5nIiwKICBkZXYuYXJncyA9IGxpc3QocG5nID0gbGlzdCh0eXBlID0gImNhaXJvLXBuZyIpKSkKb2xkX29wdGlvbnMgPC0gb3B0aW9ucyhkaWdpdHMgPSA0LAogICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICBrbml0ci5kdXBsaWNhdGUubGFiZWwgPSAiYWxsb3ciKQpnZ3Bsb3QyOjp0aGVtZV9zZXQoZ2dwbG90Mjo6dGhlbWVfYncoYmFzZV9zaXplID0gMTIpKQp2ZXIgPC0gIjIwMjMwNSIKcHJldmlvdXNfZmlsZSA8LSAiIgp2ZXIgPC0gZm9ybWF0KFN5cy5EYXRlKCksICIlWSVtJWQiKQoKIyN0bXAgPC0gc20obG9hZG1lKGZpbGVuYW1lPXBhc3RlMChnc3ViKHBhdHRlcm49IlxcLlJtZCIsIHJlcGxhY2U9IiIsIHg9cHJldmlvdXNfZmlsZSksICItdiIsIHZlciwgIi5yZGEueHoiKSkpCnJtZF9maWxlIDwtICJpbmRleC5SbWQiCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCk5hamliIGFza2VkIGZvciBhbiBpbml0aWFsIGNvbnZlcnNpb24gb2YgdGhlIGRhdGEgcHJvdmlkZWQgYnkgTWFyaWEgQWRlbGFpZGEKaW50byB0aGUgdmFyaW91cyBmb3JtYXRzIGV4cGVjdGVkIGJ5IGRiZ2FwLiAgSGUgc2VudCBtZSB0d28gc2hlZXRzIHByb3ZpZGVkCmJ5IGhlcjogICIyMDIxMDExNV9FWFBfRVNQRUNJQUxfVE1SQzMueGxzeCIgYW5kCiJDT0RFQk9PS19FWFBfRVNQRUNJQUxfVE1SQzNfMjAyMTAxMTUueGxzeCIuICBUaGUgZmlyc3QgcHJvdmlkZXMgdGhlIGRhdGEgYW5kCnRoZSBzZWNvbmQgZm9yIGZvcm1hdHRpbmcuCgpJbiBhZGRpdGlvbiwgd2Ugd2lsbCByZXF1aXJlIHNvbWUgb2YgdGhlIGRhdGEgaW4gdGhlIHNoYXJlZCBzYW1wbGUgc2hlZXQuCgpXaXRoIHRoYXQgaW4gbWluZCwgaGVyZSBhcmUgb3VyIGlucHV0czoKMS4gIFRoZSBtZXRhZGF0YSBzaGVldCBvZiBwYXRpZW50cy4KMi4gIFRoZSBtZXRhIG1ldGFkYXRhIHNoZWV0LCB0aGUgJ2NvZGVib29rJy4KMy4gIFRoZSB0bXJjMyBzYW1wbGUgc2hlZXQuICBJIGFtIGxpa2VseSB0byBhZGQgYSB3b3Jrc2hlZXQgdG8gaXQgdG8gY29udGFpbgogICAgdGhlIG1ldGEgbWV0YWRhdGEuCgpIZXJlIGFyZSB0aGUgZXhwZWN0ZWQgb3V0cHV0czoKMS4gIFN1YmplY3RDb25zZW50X0RTLnR4dDogIFRhYiBzZXBhcmF0ZWQsIGNvbnRhaW5pbmcgU1VCSkVDVF9JRCwgQ09OU0VOVCwKICAgIFNFWCwgU1VCSkVDVF9TT1VSQ0UsIGFuZCBTVUJKRUNUX1NPVVJDRV9JRAogIEluIG15IGN1cnJlbnQgc2V0dXAsIHRoaXMgaXMgb25seSB0aGUgcGF0aWVudCBJRCwgMSwgc2V4ICgxLDIsTkEgb3IKICB3aGF0ZXZlciksIFRNUkMzLCBhbmQgdGhlIHBhdGllbnQgSUQgcmVwZWF0ZWQuCjIuICBTU01fRFMudHh0OiBUYWIgc2VwYXJhdGVkLCBjb250YWluaW5nIFNVQkpFQ1RfSUQgYW5kIFNBTVBMRV9JRC4gIFRoaXMgaXMKICAgIHRoZSBjcm9zcyByZWZlcmVuY2UgYmV0d2VlbiB0aGUgc2FtcGxlIHNoZWV0IGFuZCBwYXRpZW50IElEcywgc28gaXQgd2lsbAogICAgcmVxdWlyZSBzb21lIHNvcnQgb2YgbWVyZ2UgYmV0d2VlbiB0aGUgdG1yYzMgc2FtcGxlIHNoZWV0IGFuZCB0aGUKICAgIEVYUF9FU1BFQ0lBTCB3b3Jrc2hlZXQuCjMuICBTdWJqZWN0UGhlbm90eXBlc19EUy50eHQ6IFRoaXMgaXMgcHJldHR5IG11Y2ggZXZlcnl0aGluZyBlbHNlIGZyb20gdGhlCiAgICBFWFBfRVNQRUNJQUwgc2hlZXQgd2l0aCBhdCBsZWFzdCB0aGUgZmlyc3QgY29sdW1uIG5lZWRpbmcgdG8gYmUgcmVuYW1lZAogICAgdG8gJ1NVQkpFQ1RfSUQnIGFuZCBwcm9iYWJseSB0aGUgYmlydGhkYXkgcmVtb3ZlZC4KNC4gIFNhbXBsZUF0dHJpYnV0ZXNfRFMudHh0OiBUaGlzIGlzIHByZXR0eSBtdWNoIGV2ZXJ5dGhpbmcgZWxzZSBmcm9tIHRoZQogICAgc2FtcGxlIHNoZWV0LiAgQSBidW5jaCBvZiBjb2x1bW5zIHdpbGwgbmVlZCB0byBiZSByZW1vdmVkLgo1LiAgU3ViamVjdENvbnNlbnRfREQueGxzeDogeGxzeCBmaWxlLCB0aGUgbWV0YS1tZXRhZGF0YSBmb3IgIzEgYWJvdmUuICBJCiAgICB0aGluayB3ZSBjYW4ganVzdCBjcmVhdGUgdGhpcyBkZS1ub3ZvIGFuZCBsZWF2ZSBpdCB1bm1vZGlmaWVkIGJlY2F1c2UgaXQgaXMKICAgIHF1aXRlIHNtYWxsLgo2LiAgU1NNX0RELnhsc3g6IERpdHRvLiAgVGhpcyBqdXN0IGV4cGxhaW5zIHRoZSBjb2x1bW5zIGZyb20gIzIgYWJvdmUgYW5kIGlzCiAgICB0aGVyZWZvcmUgZXZlbiBzbWFsbGVyLgo3LiAgU3ViamVjdFBoZW5vdHlwZXNfREQueGxzeDogVGhpcyBpcyB0aGUgQ09ERUJPT0sgZnJvbSBhYm92ZSBhbmQgd2lsbAogICAgbGlrZWx5IG5lZWQgc29tZSBjaGFuZ2VzLCBidXQgSSB0aGluayB0aGV5IGFyZSBwcmV0dHkgbWlub3IuCjguICBTYW1wbGVBdHRyaWJ1dGVzX0RELnhsc3g6IEkgd2FudCB0byBhZGQgdGhpcyB0byB0aGUgdG1yYzMgc2FtcGxlIHNoZWV0CiAgICBhbmQgdXNlIGl0IHRvIGRlZmluZSB3aGljaCBjb2x1bW5zIHRvIHB1bGwgZnJvbSBpdC4KCiMgUmVhZGluZyBleGlzdGluZyBkYXRhCgpgYGB7cn0KcnVuZGF0ZSA8LSBmb3JtYXQoU3lzLkRhdGUoKSwgZm9ybWF0ID0gIiVZJW0lZCIpCnBoZW5vdHlwZV9maWxlIDwtICJpbnB1dHMvMjAyNDAxLzIwMjIwNzIxX0VYUF9FU1BFQ0lBTF9UTVJDM19WMy54bHN4IgpwaGVub3R5cGVfbWV0YSA8LSAiaW5wdXRzLzIwMjQwMS9DT0RFQk9PS19FWFBfRVNQRUNJQUxfVE1SQzNfVkVSU0lPTl8zXzIwMjIwNTEyLnhsc3giCnNhbXBsZV9maWxlIDwtICJpbnB1dHMvMjAyNDAxL3RtcmMzX3NhbXBsZXNfcHJ1bmVkLnhsc3giCnNhbXBsZV9tZXRhIDwtICJ0ZW1wbGF0ZXMvU2FtcGxlQXR0cmlidXRlc19ERC54bHN4IgoKc3ViamVjdF9waGVub3R5cGVzIDwtIG9wZW54bHN4OjpyZWFkLnhsc3gocGhlbm90eXBlX2ZpbGUpCnN1YmplY3RfbWV0YSA8LSBvcGVueGxzeDo6cmVhZC54bHN4KHBoZW5vdHlwZV9tZXRhKQpzYW1wbGVfYXR0cmlidXRlcyA8LSBvcGVueGxzeDo6cmVhZC54bHN4KHNhbXBsZV9maWxlKQpzYW1wbGVfbWV0YSA8LSBvcGVueGxzeDo6cmVhZC54bHN4KHNhbXBsZV9tZXRhKQoKY3JlYXRlZCA8LSBkaXIuY3JlYXRlKGdsdWUoIm91dHB1dHMve3J1bmRhdGV9IikpCmBgYAoKIyBTYW5pdGl6ZSBjb2x1bW4gbmFtZXMgc2xpZ2h0bHkuCgpUaGVyZSBhcmUgYSBmZXcgdGhpbmdzIHdoaWNoIGFyZSByZXF1aXJlZCBieSBkYkdhcCB3aGljaCBJIGNhbiB0cml2aWFsbHkgY2hhbmdlIGhlcmUuCgpgYGB7cn0KIyMgU2FuaXRpemUgdGhlIGNvbHVtbiBuYW1lcyBhIGxpdHRsZS4KY29sbmFtZXMoc3ViamVjdF9waGVub3R5cGVzKSA8LSB0b3VwcGVyKGNvbG5hbWVzKHN1YmplY3RfcGhlbm90eXBlcykpCgpzdWJqZWN0X21ldGFbWyJWQVJOQU1FIl1dIDwtIHRvdXBwZXIoc3ViamVjdF9tZXRhW1siVkFSTkFNRSJdXSkKCmNvbG5hbWVzKHNhbXBsZV9hdHRyaWJ1dGVzKSA8LSB0b3VwcGVyKGNvbG5hbWVzKHNhbXBsZV9hdHRyaWJ1dGVzKSkKY29sbmFtZXMoc2FtcGxlX2F0dHJpYnV0ZXMpIDwtIGdzdWIocGF0dGVybiA9ICJcXC4rIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBjb2xuYW1lcyhzYW1wbGVfYXR0cmlidXRlcykpCmNvbG5hbWVzKHNhbXBsZV9hdHRyaWJ1dGVzKSA8LSBnc3ViKHBhdHRlcm4gPSAiXFwpfFxcKHxcXFt8XFxdfCx8LXwvfCV8J3zCtHxcXHMrIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjZW1lbnQgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGNvbG5hbWVzKHNhbXBsZV9hdHRyaWJ1dGVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVybCA9IFRSVUUpCnN1YmplY3RfY29sbmFtZV9zdWJzdGl0dXRpb25zIDwtIGxpc3QoCiAgICAiQ09ESUdPX1BBQ0lFTlRFIiA9ICJTVUJKRUNUX0lEIiwKICAgICJFQl9MQ19TRVhPIiA9ICJTRVgiLAogICAgIlRVQkVfTEFCRUxfT1JJR0lOIiA9ICJTVUJKRUNUX0lEIiwKICAgICJTQU1QTEVfTkFNRSIgPSAiU0FNUExFX0lEIiwKICAgICJUTVJDX0lERU5USUZJRVIiID0gIlRNUkNfSUQiCikKZm9yIChpIGluIDE6bGVuZ3RoKHN1YmplY3RfY29sbmFtZV9zdWJzdGl0dXRpb25zKSkgewogIGZyb20gPC0gbmFtZXMoc3ViamVjdF9jb2xuYW1lX3N1YnN0aXR1dGlvbnMpW2ldCiAgdG8gPC0gc3ViamVjdF9jb2xuYW1lX3N1YnN0aXR1dGlvbnNbW2ldXQogIGNvbG5hbWVzKHN1YmplY3RfcGhlbm90eXBlcykgPC0gZ3N1YihwYXR0ZXJuID0gZnJvbSwgcmVwbGFjZW1lbnQgPSB0bywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGNvbG5hbWVzKHN1YmplY3RfcGhlbm90eXBlcykpCiAgY29sbmFtZXMoc2FtcGxlX2F0dHJpYnV0ZXMpIDwtIGdzdWIocGF0dGVybiA9IGZyb20sIHJlcGxhY2VtZW50ID0gdG8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGNvbG5hbWVzKHNhbXBsZV9hdHRyaWJ1dGVzKSkKICBzdWJqZWN0X21ldGFbWyJWQVJOQU1FIl1dIDwtIGdzdWIocGF0dGVybiA9IGZyb20sIHJlcGxhY2VtZW50ID0gdG8sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBzdWJqZWN0X21ldGFbWyJWQVJOQU1FIl1dKQp9CmBgYAoKIyBTYW5pdHkgY2hlY2sKCk5vdyB0aGF0IEkgaGF2ZSBtYWRlIHNvbWUgY2hhbmdlcyB0byB0aGUgbWV0YWRhdGEsIGxldCB1cyBhdHRlbXB0IHRvCm1ha2UgY2VydGFpbiB0aGF0IHRoZXkgc3RpbGwgbWF0Y2guICBUaGlzIHByaW1hcmlseSByZWZlcnMgdG8gdGhlCmNvbHVtbiBuYW1lcyBvZiB0aGUgc3ViamVjdF9waGVub3R5cGUgZmlsZSBhbmQgdGhlIFZBUk5BTUUgY29sdW1uIGZyb20KdGhlIHN1YmplY3QgbWV0YWRhdGEgZmlsZS4KCmBgYHtyfQptaXNzZXMgPC0gc3ViamVjdF9tZXRhW1siVkFSTkFNRSJdXSAhPSBjb2xuYW1lcyhzdWJqZWN0X3BoZW5vdHlwZXMpCnN1bW1hcnkobWlzc2VzKQppZiAoc3VtKG1pc3NlcykgPiAwKSB7CiAgc3RvcCgiVGhlcmUgaXMgYSBtaXNtYXRjaCBiZXR3ZWVuIHRoZSBtZXRhZGF0YSBhbmQgY29sdW1uIG5hbWVzLiIpCn0KYGBgCgojIEZpbHRlciBvdXQgc2FtcGxlcyB3aGljaCB3ZXJlIG5vdCBzZXF1ZW5jZWQuCgpXZSBvbmx5IHdhbnQgdGhvc2Ugc2FtcGxlcyBmb3Igd2hpY2ggd2UgaGF2ZSBleHRhbnQgVE1SQyBJRHMuICBUaHVzLApmaWx0ZXIgdGhlIHNhbXBsZV9hdHRyaWJ1dGVzIGZvciB0aGUgc2FtcGxlcyB0aGF0IGhhdmUgYmVlbiBnaXZlbiB0bXJjCklEcyBmb3Igbm93LgoKYGBge3J9CnVzZWZ1bF9pZHggPC0gIWlzLm5hKHNhbXBsZV9hdHRyaWJ1dGVzW1siVE1SQ19JRCJdXSkKc2FtcGxlX2F0dHJpYnV0ZXMgPC0gc2FtcGxlX2F0dHJpYnV0ZXNbdXNlZnVsX2lkeCwgXQpgYGAKCiMgQ3JlYXRlIHRoZSBTdWJqZWN0Q29uc2VudF9EUy50eHQKCkNyZWF0aW5nIHRoZSBTdWJqZWN0Q29uc2VudF9EUy50eHQgc2hvdWxkIGJlIHRoZSBlYXNpZXN0LCBqdXN0IHB1bGwgdGhlIElECmFuZCBTRVggY29sdW1ucyBhbmQgZmlsbCBpbiB0aGUgcmVzdC4KCmBgYHtyfQpTdWJqZWN0Q29uc2VudF9EUyA8LSBzdWJqZWN0X3BoZW5vdHlwZXNbLCBjKCJTVUJKRUNUX0lEIiwgIlNFWCIpXQpTdWJqZWN0Q29uc2VudF9EU1tbIkNPTlNFTlQiXV0gPC0gMQpTdWJqZWN0Q29uc2VudF9EU1tbIlNVQkpFQ1RfU09VUkNFIl1dIDwtICJUTVJDMyIKU3ViamVjdENvbnNlbnRfRFNbWyJTVUJKRUNUX1NPVVJDRV9JRCJdXSA8LSBTdWJqZWN0Q29uc2VudF9EU1tbIlNVQkpFQ1RfSUQiXV0KIyMgQW5kIHJlb3JkZXIgaXQKY29sdW1uX29yZGVyIDwtIGMoIlNVQkpFQ1RfSUQiLCAiQ09OU0VOVCIsICJTRVgiLCAiU1VCSkVDVF9TT1VSQ0UiLCAiU1VCSkVDVF9TT1VSQ0VfSUQiKQpTdWJqZWN0Q29uc2VudF9EUyA8LSBTdWJqZWN0Q29uc2VudF9EU1ssIGNvbHVtbl9vcmRlcl0KcmVhZHI6OndyaXRlX3Rzdih4ID0gU3ViamVjdENvbnNlbnRfRFMsCiAgICAgICAgICAgICAgICAgZmlsZSA9IGdsdWUoIm91dHB1dHMve3J1bmRhdGV9L3tydW5kYXRlfS1TdWJqZWN0Q29uc2VudF9EUy50eHQiKSkKYGBgCgojIENyZWF0ZSB0aGUgU3ViamVjdENvbnNlbnRfREQueGxzeAoKVGhpcyBmaWxlIHNob3VsZCBiZSB1bmNoYW5nZWQgZnJvbSB0aGUgdGVtcGxhdGUsIHNvIHdlIHdpbGwgcmVhZCBpdCBpbgphbmQgaW1tZWRpYXRlbHkgd3JpdGUgaXQgYmFjayBvdXQgd2l0aCBhIGJsYW5rIGxpbmUgaW4gYmV0d2VlbiBpbiBjYXNlCndlIGRvIGluIGZhY3QgbmVlZCB0byBjaGFuZ2Ugc29tZXRoaW5nIGxhdGVyLgoKYGBge3J9ClN1YmplY3RDb25zZW50X0REIDwtIG9wZW54bHN4OjpyZWFkLnhsc3goInRlbXBsYXRlcy9TdWJqZWN0Q29uc2VudF9ERC54bHN4IikKCgpvcGVueGxzeDo6d3JpdGUueGxzeCh4ID0gU3ViamVjdENvbnNlbnRfREQsCiAgICAgICAgICAgICAgICAgICAgIGZpbGUgPSBnbHVlKCJvdXRwdXRzL3tydW5kYXRlfS97cnVuZGF0ZX0tU3ViamVjdENvbnNlbnRfREQueGxzeCIpKQpgYGAKCiMgQ3JlYXRlIHRoZSBTU01fRFMudHh0CgpUaGUgU1NNX0RTLnR4dCB3aWxsIGJlIG1vcmUgZGlmZmljdWx0LCBpdCByZXF1aXJlcyBhIG1lcmdlIGJldHdlZW4gc2hlZXRzLi4uCgpgYGB7cn0Kc2FtcGxlX2F0dHJpYnV0ZXNbWyJTVUJKRUNUX0lEIl1dIDwtIGdzdWIocGF0dGVybiA9ICJec3UiLCByZXBsYWNlbWVudCA9ICJTVSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBzYW1wbGVfYXR0cmlidXRlc1tbIlNVQkpFQ1RfSUQiXV0pClNTTV9EUyA8LSBtZXJnZShTdWJqZWN0Q29uc2VudF9EUywgc2FtcGxlX2F0dHJpYnV0ZXMsIGJ5ID0gIlNVQkpFQ1RfSUQiKQp3YW50ZWRfY29sdW1ucyA8LSBjKCJTVUJKRUNUX0lEIiwgIlNBTVBMRV9JRCIpClNTTV9EUyA8LSBTU01fRFNbLCB3YW50ZWRfY29sdW1uc10KcmVhZHI6OndyaXRlX3Rzdih4ID0gU1NNX0RTLCBmaWxlID0gZ2x1ZSgib3V0cHV0cy97cnVuZGF0ZX0ve3J1bmRhdGV9LVNTTV9EUy50eHQiKSkKYGBgCgojIENyZWF0ZSB0aGUgU1NNX0RELnhsc3gKCk9uY2UgYWdhaW4sIHdlIHdpbGwgYXNzdW1lIHRoYXQgY29weWluZyB0aGlzIGZyb20gdGhlIHRlbXBsYXRlIHdpbGwKcHJvdmUgc3VmZmljaWVudDsgYnV0IHdpbGwgcGVyZm9ybSBleHBsaWNpdCByZWFkL3dyaXRlIHN0ZXBzIGluIGNhc2UKd2UgbmVlZCB0byBjaGFuZ2UgYW55dGhpbmcuCgpgYGB7cn0KU1NNX0REIDwtIG9wZW54bHN4OjpyZWFkLnhsc3goInRlbXBsYXRlcy9TU01fREQueGxzeCIpCgoKb3Blbnhsc3g6OndyaXRlLnhsc3goeCA9IFNTTV9ERCwgZmlsZSA9IGdsdWUoIm91dHB1dHMve3J1bmRhdGV9L3tydW5kYXRlfS1TU01fREQueGxzeCIpKQpgYGAKCiMgQ3JlYXRlIHRoZSBTdWJqZWN0UGhlbm90eXBlc19ERAoKTm9ybWFsbHksIEkgd291bGQgbW92ZSB0aGUgZW5nbGlzaC10cmFuc2xhdGVkIGNvbHVtbnMgdG8gdGhlIGRlZmF1bHRzOwpidXQgd2UgZG8gbm90IGhhdmUgYSB0cmFuc2xhdGVkIHZlcnNpb24gb2YgdGhpcyBhdCB0aGlzIHRpbWUuCgpgYGB7cn0KIyMgSSBkbyBub3QgdGhpbmsgSSBuZWVkIHRvIGRvIGFueXRoaW5nIGVsc2UgdG8gdGhlIG1ldGFkYXRhIGZpbGUgYXQgdGhpcyB0aW1lLgpTdWJqZWN0UGhlbm90eXBlc19ERCA8LSBzdWJqZWN0X21ldGEKYGBgCgojIyBKdWdnbGUgdGhlIGNvbHVtbnMKCkkgY2Fubm90IHJ1biB0aGUgZm9sbG93aW5nIGJsb2NrIGF0IHRoaXMgdGltZSBiZWNhdXNlIHRoZSBjdXJyZW50IGZpbGUKaGFzIG5vdCBiZWVuIHRyYW5zbGF0ZWQuCgpgYGB7ciBqdWdnbGUsIGV2YWw9RkFMU0V9CiMjIFdlIHdpbGwgbmVlZCB0byBtb3JlIFZBUk5BTUVfRU5HIHRvIFZBUk5BTUUKU3ViamVjdFBoZW5vdHlwZXNfRERbWyJWQVJOQU1FIl1dIDwtIFN1YmplY3RQaGVub3R5cGVzX0REW1siVkFSTkFNRV9FTkciXV0KU3ViamVjdFBoZW5vdHlwZXNfRERbWyJWQVJOQU1FX0VORyJdXSA8LSBOVUxMCiMjIERpdHRvIGZvciBWQVJERVNDIGFuZCBUWVBFClN1YmplY3RQaGVub3R5cGVzX0REW1siVkFSREVTQyJdXSA8LSBTdWJqZWN0UGhlbm90eXBlc19ERFtbIlZBUkRFU0NfRU5HIl1dClN1YmplY3RQaGVub3R5cGVzX0REW1siVkFSREVTQ19FTkciXV0gPC0gTlVMTApTdWJqZWN0UGhlbm90eXBlc19ERFtbIlRZUEUiXV0gPC0gU3ViamVjdFBoZW5vdHlwZXNfRERbWyJUWVBFX0VORyJdXQpTdWJqZWN0UGhlbm90eXBlc19ERFtbIlRZUEVfRU5HIl1dIDwtIE5VTEwKIyMgSGF2aW5nIGRvbmUgdGhhdCwgd2UgbmVlZCB0byBzZXQgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgU3ViamVjdFBoZW5vdHlwZXNfRFMgdG8gdGhlIG5ldwojIyBWQVJOQU1FIGNvbHVtbi4KYGBgCgojIFdyaXRlIG91dCB0aGUgU3ViamVjdFBoZW5vdHlwZXNfREQKCmBgYHtyfQpzdGFydGluZ19kc19jb2xuYW1lcyA8LSBTdWJqZWN0UGhlbm90eXBlc19ERFtbIlZBUk5BTUUiXV0Kb3Blbnhsc3g6OndyaXRlLnhsc3goeCA9IFN1YmplY3RQaGVub3R5cGVzX0RELAogICAgICAgICAgICAgICAgICAgICBmaWxlID0gZ2x1ZSgib3V0cHV0cy97cnVuZGF0ZX0ve3J1bmRhdGV9LVN1YmplY3RQaGVub3R5cGVzX0RELnhsc3giKSkKYGBgCgojIENyZWF0ZSB0aGUgU3ViamVjdFBoZW5vdHlwZXNfRFMKClRoZSBTdWJqZWN0UGhlbm90eXBlc19EUyBpcyBqdXN0IHRoZSBvcmlnaW5hbCBzdWJqZWN0X3BoZW5vdHlwZXMgd2l0aApzb21lIGNvbHVtbnMgYmxhY2tsaXN0ZWQuCgpgYGB7cn0Kc3ViamVjdF9waGVub3R5cGVzX2JsYWNrbGlzdCA8LSBjKCJFQl9MQ19GRUNIQV9OQUNJTUlFTlRPIiwgIlNFWCIpClN1YmplY3RQaGVub3R5cGVzX0RTIDwtIHN1YmplY3RfcGhlbm90eXBlcwpjb2xuYW1lcyhTdWJqZWN0UGhlbm90eXBlc19EUykgPC0gc3RhcnRpbmdfZHNfY29sbmFtZXMKZm9yIChpIGluIHN1YmplY3RfcGhlbm90eXBlc19ibGFja2xpc3QpIHsKICBTdWJqZWN0UGhlbm90eXBlc19EU1tbaV1dIDwtIE5VTEwKICBtZXRhX2tlZXBlcnMgPC0gc3ViamVjdF9tZXRhW1siVkFSTkFNRSJdXSAhPSBpCiAgc3ViamVjdF9tZXRhIDwtIHN1YmplY3RfbWV0YVttZXRhX2tlZXBlcnMsIF0KfQpyZWFkcjo6d3JpdGVfdHN2KHggPSBTdWJqZWN0UGhlbm90eXBlc19EUywKICAgICAgICAgICAgICAgICBmaWxlID0gZ2x1ZSgib3V0cHV0cy97cnVuZGF0ZX0ve3J1bmRhdGV9LVN1YmplY3RQaGVub3R5cGVzX0RTLnR4dCIpKQpgYGAKCiMgQ3JlYXRlIHRoZSBTYW1wbGVBdHRyaWJ1dGVzX0RTCgpUaGUgU2FtcGxlQXR0cmlidXRlc19EUyB3aWxsIHJlcXVpcmUgc29tZSBtb3JlIHdvcms6CjEuICBSZWFkIGluIHRoZSBTYW1wbGVBdHRyaWJ1dGVzX0REIGFuZCBrZWVwIG9ubHkgdGhlIGNvbHVtbnMgZGVmaW5lZAogICAgaW4gaXQuCjIuICBSZWNhc3QgdGhlIGRhdGEgdG8gZW5zdXJlIHRoZXkgYXJlIHB1cmUgdGV4dC4KCkN1cnJlbnRseSB0aGF0IGlzIGFsbCB3ZSBhcmUgZG9pbmcsIHNvIGl0IGRvZXNuJ3QgcmVhbGx5IHJlcXVpcmUgdmVyeQptdWNoLgoKSSBkbyBuZWVkIHRvIGNoYW5nZSB0aGUgY29sdW1uICdGSU5BTF9PVVRDT01FJyB0byAnQ0xJTklDQUxfT1VUQ09NRScKCmBgYHtyfQpjb2xuYW1lcyhzYW1wbGVfYXR0cmlidXRlcykgPC0gZ3N1Yih4ID0gY29sbmFtZXMoc2FtcGxlX2F0dHJpYnV0ZXMpLCBwYXR0ZXJuID0gIkZJTkFMX09VVENPTUUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudCA9ICJDTElOSUNBTF9PVVRDT01FIikKU2FtcGxlQXR0cmlidXRlc19EUyA8LSBzYW1wbGVfYXR0cmlidXRlcwprZXB0X2NvbHVtbnMgPC0gc2FtcGxlX21ldGFbWyJWQVJOQU1FIl1dCgprZXB0X2NvbHVtbnNbISBrZXB0X2NvbHVtbnMgJWluJSBjb2xuYW1lcyhTYW1wbGVBdHRyaWJ1dGVzX0RTKV0KClNhbXBsZUF0dHJpYnV0ZXNfRFMgPC0gU2FtcGxlQXR0cmlidXRlc19EU1ssIGtlcHRfY29sdW1uc10KZm9yIChpIGluIDE6bmNvbChTYW1wbGVBdHRyaWJ1dGVzX0RTKSkgewogIFNhbXBsZUF0dHJpYnV0ZXNfRFNbW2ldXSA8LSBhcy5jaGFyYWN0ZXIoU2FtcGxlQXR0cmlidXRlc19EU1tbaV1dKQp9CnJlYWRyOjp3cml0ZV90c3YoeCA9IFNhbXBsZUF0dHJpYnV0ZXNfRFMsCiAgICAgICAgICAgICAgICAgZmlsZSA9IGdsdWUoIm91dHB1dHMve3J1bmRhdGV9L3tydW5kYXRlfS1TYW1wbGVBdHRyaWJ1dGVzX0RTLnR4dCIpKQpgYGAKCiMgV3JpdGUgdGhlIFNhbXBsZUF0dHJpYnV0ZXNfREQKCk9uY2UgYWdhaW4sIHRoZSBERCBmaWxlIGlzIGV4cGVjdGVkIHRvIGJlIGlkZW50aWNhbCB0byBvdXIgdGVtcGxhdGUuCgpgYGB7cn0KU2FtcGxlQXR0cmlidXRlc19ERCA8LSBzYW1wbGVfbWV0YQpvcGVueGxzeDo6d3JpdGUueGxzeCh4ID0gU2FtcGxlQXR0cmlidXRlc19ERCwKICAgICAgICAgICAgICAgICAgICAgZmlsZSA9IGdsdWUoIm91dHB1dHMve3J1bmRhdGV9L3tydW5kYXRlfS1TYW1wbGVBdHRyaWJ1dGVzX0RELnhsc3giKSkKYGBgCg==