Analysis
library(tidyverse)
library(openxlsx)
library(DBI)
config <- config::get()
con <- dbConnect(
drv = RPostgreSQL::PostgreSQL(),
dbname = config$dbname,
host = config$host,
port = config$port,
user = config$user,
password = config$password,
option = config$option
)
selected_schema <- str_remove(config$option, pattern = "-c search_path=")
T cell epitope table
The table below is the original downloaded table:
f <- "/v/projects/geno2pheno-kpapp/dnieuw/MHCI-Spike-epitopes.tsv"
epitope_raw_ref <- read_tsv(f, show_col_types = FALSE)[, c(1,5,6)]
names(epitope_raw_ref) <- c("id", "references", "assays")
epitope_raw_ref$id <- as.character(epitope_raw_ref$id)
f <- "/v/projects/geno2pheno-kpapp/dnieuw/MHCI-Spike-epitopes_metadata.tsv"
epitope_raw <- read_tsv(f, show_col_types = FALSE)[1:17]
names(epitope_raw) <- c("iedb_iri", "object_type", "name", "modified_residue",
"modifications", "start", "end", "iri", "synonyms",
"source_molecule", "source_molecule_iri", "molecule_parent",
"molecule_parent_iri", "source_organism",
"source_organism_iri", "species", "species_iri")
epitope_raw <- epitope_raw %>%
separate(col = iedb_iri, into = c(NA, NA, NA, NA, "id"),
sep = "/", remove = FALSE) %>%
inner_join(epitope_raw_ref, by = "id") %>%
select(id, name, start, end, references, assays, everything())
epitope_raw
- Table above shows that there are 653 epitopes in the raw epitope table
Need to concentrate only those epitopes those were present in more than 1 presentation
epitope <- epitope_raw %>%
filter(references>1)
epitope
- There are 190 out of 653 epitopes those were published more than a single paper
epitope%>%
filter(is.na(start))
- There is a single epitope (
1593848
) where the start and end position is missing. Based on David’s alignment (“/v/projects/geno2pheno-kpapp/dnieuw/MHCI-Spike-epitopes_aligned.faa”), the position of this epitope is similar to https://www.iedb.org/epitope/1309147 epitope. There difference is a single mutation at amino acide level. So this epitope does not derive from reference sequence but the start-end position will be included the table:
epitope[epitope$id=="1593848", "start"] <- 269
epitope[epitope$id=="1593848", "end"] <- 277
Need to check if epitope sequences derived from the reference sequence.
The reference sequence is here: https://www.ncbi.nlm.nih.gov/nuccore/MN908947 Spike protein (QHD43416.1) is coded in the reference sequence position: 21563-25384
Database will be searched for the reference Spike protein sequence:
select * from datahub_0.translated_aa_ref_sequence(21563, 25384)
- Note: the “datahub_0.barcodes_with_mutation_positions({start}, {end})” SQL function translate the reference nucleotide sequence into protein sequence (the parameters are the start and end positions of reference nucleotide sequence)
spike_seq <- str_c(spike_seq$aa_symbol, collapse = "")
spike_seq
[1] "MFVFLVLLPLVSSQCVNLTTRTQLPPAYTNSFTRGVYYPDKVFRSSVLHSTQDLFLPFFSNVTWFHAIHVSGTNGTKRFDNPVLPFNDGVYFASTEKSNIIRGWIFGTTLDSKTQSLLIVNNATNVVIKVCEFQFCNDPFLGVYYHKNNKSWMESEFRVYSSANNCTFEYVSQPFLMDLEGKQGNFKNLREFVFKNIDGYFKIYSKHTPINLVRDLPQGFSALEPLVDLPIGINITRFQTLLALHRSYLTPGDSSSGWTAGAAAYYVGYLQPRTFLLKYNENGTITDAVDCALDPLSETKCTLKSFTVEKGIYQTSNFRVQPTESIVRFPNITNLCPFGEVFNATRFASVYAWNRKRISNCVADYSVLYNSASFSTFKCYGVSPTKLNDLCFTNVYADSFVIRGDEVRQIAPGQTGKIADYNYKLPDDFTGCVIAWNSNNLDSKVGGNYNYLYRLFRKSNLKPFERDISTEIYQAGSTPCNGVEGFNCYFPLQSYGFQPTNGVGYQPYRVVVLSFELLHAPATVCGPKKSTNLVKNKCVNFNFNGLTGTGVLTESNKKFLPFQQFGRDIADTTDAVRDPQTLEILDITPCSFGGVSVITPGTNTSNQVAVLYQDVNCTEVPVAIHADQLTPTWRVYSTGSNVFQTRAGCLIGAEHVNNSYECDIPIGAGICASYQTQTNSPRRARSVASQSIIAYTMSLGAENSVAYSNNSIAIPTNFTISVTTEILPVSMTKTSVDCTMYICGDSTECSNLLLQYGSFCTQLNRALTGIAVEQDKNTQEVFAQVKQIYKTPPIKDFGGFNFSQILPDPSKPSKRSFIEDLLFNKVTLADAGFIKQYGDCLGDIAARDLICAQKFNGLTVLPPLLTDEMIAQYTSALLAGTITSGWTFGAGAALQIPFAMQMAYRFNGIGVTQNVLYENQKLIANQFNSAIGKIQDSLSSTASALGKLQDVVNQNAQALNTLVKQLSSNFGAISSVLNDILSRLDKVEAEVQIDRLITGRLQSLQTYVTQQLIRAAEIRASANLAATKMSECVLGQSKRVDFCGKGYHLMSFPQSAPHGVVFLHVTYVPAQEKNFTTAPAICHDGKAHFPREGVFVSNGTHWFVTQRNFYEPQIITTDNTFVSGNCDVVIGIVNNTVYDPLQPELDSFKEELDKYFKNHTSPDVDLGDISGINASVVNIQKEIDRLNEVAKNLNESLIDLQELGKYEQYIKWPWYIWLGFIAGLIAIVMVTIMLCCMTSCCSCLKGCCSCGSCCKFDEDDSEPVLKGVKLHYT"
Check if the name
column of the epitope table contains really the reference sequence.
for (i in 1:nrow(epitope)) {
ref <- str_sub(spike_seq,
start = epitope[i, 'start'],
end = epitope[i, 'end'])
if (epitope[i, 'name'] == ref) epitope[i, 'match_with_ref'] <- TRUE else epitope[i, 'match_with_ref'] <- FALSE
}
Lets see the result of the comparison
table(epitope$match_with_ref)
FALSE TRUE
6 184
There are 6 epitopes where the name
column does not match with the reference.
These are the epitopes those do not match with reference sequence:
epitope[epitope$match_with_ref==FALSE,]
- These epitopes above are important one, but now for simplicity I will exclude them, later these epitopes will be checked.
Table of T cell epitopes included the analysis
The table below contains the epitopes those derived from reference sequence:
epitope_selected <- epitope %>%
filter(match_with_ref)
epitope_selected
Position of epitopes on reference spike sequence
x <- epitope_selected %>%
select(id, start, end) %>%
mutate(start = as.numeric(start),
end = as.numeric(end)) %>%
arrange(start)
x$y <- 1:nrow(x)
x <- x %>%
pivot_longer(cols = start:end, values_to = "x")
ggplot(data = x, aes(x = x, y = y, fill = id)) +
geom_line(alpha = 0.4) +
labs(title = "Distribution of T-cell epitopes on spike protein",
x = "Amino acide position",
y = "") +
theme_minimal() +
theme(aspect.ratio=2/6)
- Each short horizontal line represent a T cell epitope
Barcode table
Barcode was assigned to each samples. A given barcode is basically a mutation pattern (amino acide level) of S protein.
How many different S protein exist now in the database?
SELECT count(*)
FROM barcode
- There are 346976 barcodes in the database.
I will use only those barcodes that were assigned to more than 100 samples:
WITH barcode_dist AS (
SELECT barcode_id, count(*) AS sample_number_in_bacode
FROM aa_mutation_new AS a
GROUP BY a.barcode_id
)
SELECT sample_number_in_bacode, count(*) AS number_of_barcode
FROM barcode_dist
GROUP BY sample_number_in_bacode
ORDER BY number_of_barcode DESC
barcode_hist %>%
filter(sample_number_in_bacode > 100) %>%
summarise(`Number of barcodes` = sum(number_of_barcode))
There are 1972 barcodes that appeared more than 100 samples in the database.
barcode_hist %>%
filter(sample_number_in_bacode > 100) %>%
summarise(`Number of samples belongs to the selected barcodes` = sum(sample_number_in_bacode))
- Around 1.8 million samples will be included into this analysis
List of barcodes that will be included this analysis:
WITH barcode_dist AS (
SELECT barcode_id, count(*) AS sample_number_in_bacode
FROM aa_mutation_new AS a
GROUP BY a.barcode_id
)
SELECT barcode_id, sample_number_in_bacode, aa_mutation
FROM barcode_dist
LEFT JOIN barcode ON barcode.id=barcode_dist.barcode_id
WHERE barcode_dist.sample_number_in_bacode > 100
Head of the table:
head(barcodes)
Match barcodes with T cell epitopes
mutation <- barcodes %>%
mutate(mut = aa_mutation) %>%
mutate(mut = str_remove(mut, pattern = "\\{"),
mut = str_remove(mut, pattern = "\\}"),
mut = str_remove_all(mut, pattern = "p\\.")) %>%
mutate(n = (str_count(mut, pattern = ",") + 1))
max_mut <- max(mutation$n)
ids <- str_c("x", 1:max_mut)
mutation<- mutation%>%
separate(mut, into = ids, sep = ",", fill = "right") %>%
pivot_longer(cols = all_of(ids), values_to = "mut",
names_to = "mut_pos_in_barcode") %>%
drop_na(mut) %>%
mutate(pos = as.numeric(str_sub(mut, start = 4, end = -4)))
head(mutation)
- Table above shows the positions of mutations in the selected barcodes.
Select those epitopes where start-end position includes any of the mutation positions above.
mutation_all <- tibble(barcode_id = integer(),
sample_number_in_bacode = numeric(),
aa_mutation = character(),
n = numeric(),
mut_pos_in_barcode = character(),
mut = character(),
pos = numeric(),
start = numeric(),
end = numeric(),
iedb_id= character())
for (i in row.names(epitope_selected)) {
x <- mutation %>%
mutate(start = as.numeric(epitope_selected[i, "start"]),
end = as.numeric(epitope_selected[i, "end"]),
iedb_id = as.character(epitope_selected[i, "id"])) %>%
mutate(is_mutation_present = ifelse((pos >= start & pos <= end), TRUE, FALSE)) %>%
filter(is_mutation_present) %>%
select(-is_mutation_present)
mutation_all <- mutation_all %>%
add_row(x)
}
mutation_fin <- mutation_all_mod %>%
select(barcode_id, iedb_id, mut) %>%
group_by(barcode_id, iedb_id) %>%
summarize(mutation_list = str_c(mut, collapse = ",")) %>%
inner_join(x, by = c("barcode_id", "iedb_id")) %>%
mutate(mutation_list = str_replace_all(mutation_list, pattern = 'Ala', replacement = 'A'),
mutation_list = str_replace_all(mutation_list, pattern = 'Arg', replacement = 'R'),
mutation_list = str_replace_all(mutation_list, pattern = 'Asn', replacement = 'N'),
mutation_list = str_replace_all(mutation_list, pattern = 'Asp', replacement = 'D'),
mutation_list = str_replace_all(mutation_list, pattern = 'Cys', replacement = 'C'),
mutation_list = str_replace_all(mutation_list, pattern = 'Gln', replacement = 'Q'),
mutation_list = str_replace_all(mutation_list, pattern = 'Glu', replacement = 'E'),
mutation_list = str_replace_all(mutation_list, pattern = 'His', replacement = 'H'),
mutation_list = str_replace_all(mutation_list, pattern = 'Ile', replacement = 'I'),
mutation_list = str_replace_all(mutation_list, pattern = 'Leu', replacement = 'L'),
mutation_list = str_replace_all(mutation_list, pattern = 'Lys', replacement = 'K'),
mutation_list = str_replace_all(mutation_list, pattern = 'Met', replacement = 'M'),
mutation_list = str_replace_all(mutation_list, pattern = 'Phe', replacement = 'F'),
mutation_list = str_replace_all(mutation_list, pattern = 'Pro', replacement = 'P'),
mutation_list = str_replace_all(mutation_list, pattern = 'Ser', replacement = 'S'),
mutation_list = str_replace_all(mutation_list, pattern = 'Thr', replacement = 'T'),
mutation_list = str_replace_all(mutation_list, pattern = 'Trp', replacement = 'W'),
mutation_list = str_replace_all(mutation_list, pattern = 'Tyr', replacement = 'Y'),
mutation_list = str_replace_all(mutation_list, pattern = 'Val', replacement = 'V'),
mutation_list = str_replace_all(mutation_list, pattern = 'Gly', replacement = 'G')
)
`summarise()` has grouped output by 'barcode_id'. You can override using the `.groups` argument.
There are 38155 mutated epitopes in the table above, but many of them appear in more than one times (if the same mutated epitope appears in more than one barcode, then it it will be present more than one row)
Mutated epitope table
mutated_epitope_table <- mutation_fin %>%
ungroup() %>%
select(iedb_id, ref_epitope, mut_epitope) %>%
distinct()
mutated_epitope_table
- The table above contains 935 mutated epitopes. This is the final list, these need to be checked if their binding affinity changes as a result of mutation
write_tsv(mutated_epitope_table, file = "/v/projects/geno2pheno-kpapp/kpapp/t_cell_epitop/data/mutated_epitopes.tsv")
write_tsv(mutation_fin, file = "/v/projects/geno2pheno-kpapp/kpapp/t_cell_epitop/data/mutated_epitopes_barcodes.tsv")
write_tsv(mutation_all, file = "/v/projects/geno2pheno-kpapp/kpapp/t_cell_epitop/data/mutated_epitopes_barcodes_all.tsv")
LS0tCnRpdGxlOiAiTUhDSSBULWNlbGwgZXBpdG9wZXMgZnJvbSBTcGlrZSBwcm90ZWluIgphdXRob3I6ICJLcmlzenRpYW4gUGFwcCIKZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgY29sbGFwc2VkOiBmYWxzZQogICAgZmlnX2NhcHRpb246IHllcwogICAgZmlnX2hlaWdodDogNAogICAgZmlnX3dpZHRoOiA1CiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMKICAgIHRoZW1lOiBzYW5kc3RvbmUKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDUKICAgIHRvY19mbG9hdDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCgojIEFpbXMKCiogQ3JlYXRlIGEgbGlzdCBvZiBtdXRhdGVkIE1IQ0kgVC1jZWxsIGVwaXRvcGVzIHRob3NlIGFwcGVhciBpbiB0aGUgQ09WRU8gZGF0YWJhc2UKCiMgU3VtbWFyeQoKKiBUaGVyZSBhcmUgNjUzIGVwaXRvcGVzIGluIHRoZSBvcmlnaW5hbCBkb3dubG9hZGVkIGVwaXRvcGUgbGlzdC4gVGhlcmUgYXJlIDE5MCBvdXQgb2YgNjUzIGVwaXRvcGVzIHRob3NlIHdlcmUgcHVibGlzaGVkIG1vcmUgdGhhbiBhIHNpbmdsZSBwYXBlci4gMTg0IG91dCBvZiAxOTAgZXBpdG9wZXMgZGVyaXZlZCBmcm9tIHJlZmVyZW5jZSBzZXF1ZW5jZSAodGhpcyB3YXMgdXNlZCBpbiB0aGlzIHJlcG9ydCwgbGF0ZXIgdGhlIGV4dHJhIDYgbXV0YXRlZCBlcGl0b3BlcyB3aWxsIGJlIGluY2x1ZGVkIHRvbykgW3NlZV0oI2ExKQoqIFBvc2l0aW9uIG9mIGFuYWx5emVkIFQgY2VsbCBlcGl0b3BlcyBvbiBTcGlrZSByZWZlcmVuY2Ugc2VxdWVuY2UgW3NlZV0oI2EyKQoqIEJhcmNvZGUgd2FzIGFzc2lnbmVkIHRvIGVhY2ggc2FtcGxlcy4gQSBnaXZlbiBiYXJjb2RlIGlzIGJhc2ljYWxseSBhIG11dGF0aW9uIHBhdHRlcm4gKGFtaW5vIGFjaWRlIGxldmVsKSBvZiBTIHByb3RlaW4uIFRoZXJlIGFyZSAzNDY5NzYgYmFyY29kZXMgaW4gdGhlIGRhdGFiYXNlLiBUaGVyZSBhcmUgb25seSAxOTcyIGJhcmNvZGVzIHRoYXQgYXBwZWFyZWQgbW9yZSB0aGFuIDEwMCBzYW1wbGVzIGluIHRoZSBkYXRhYmFzZSAob25seSB0aGVzZSBhcmUgaW5jbHVkZWQgdGhlIGFuYWx5c2lzKS4gMTc4NDEwOCBzYW1wbGVzIGFyZSBhc3NpZ25lZCB0byB0aGVzZSBkaWZmZXJlbnQgMTk3MiBiYXJjb2Rlcy4KKiBUaGUgc2VsZWN0ZWQgZXBpdG9wZXMgYW5kIGJhcmNvZGVzIHdlcmUgbWF0Y2hlZC4gQSB0YWJsZSB3YXMgY3JlYXRlZCB0aGF0IGNvbnRhaW5zIHRoZSA5MzUgbXV0YXRlZCBUIGNlbGwgZXBpdG9wZXMgW3NlZV0oI2EzKS4gRG93bmxvYWRhYmxlIHRhYmxlOiAiL3YvcHJvamVjdHMvZ2VubzJwaGVuby1rcGFwcC9rcGFwcC90X2NlbGxfZXBpdG9wL2RhdGEvbXV0YXRlZF9lcGl0b3Blcy50c3YiCgojIEJhY2tncm91bmQKCiogVCBjZWxsIGVwaXRvcGVzIHdlcmUgZG93bmxvYWRlZCAoaHR0cHM6Ly93d3cuaWVkYi5vcmcvKSBieSBEYXZpZDogICgiL3YvcHJvamVjdHMvZ2VubzJwaGVuby1rcGFwcC9kbmlldXcvTUhDSS1TcGlrZS1lcGl0b3Blc19tZXRhZGF0YS50c3YiKQoKKiBUaGUgQ09WRU8gZGF0YWJhc2Ugd2lsbCBiZSBzZWFyY2hlZCBmb3IgdGhlc2UgZXBpdG9wZXMgYW5kIGNoZWNrIHRoZSBwcmVzZW5jZXMgb2YgdGhlaXIgbXV0YXRlZCB2ZXJzaW9uCgojIEFuYWx5c2lzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkoREJJKQpgYGAKCmBgYHtyfQpjb25maWcgPC0gY29uZmlnOjpnZXQoKQpjb24gPC0gZGJDb25uZWN0KAogIGRydiA9IFJQb3N0Z3JlU1FMOjpQb3N0Z3JlU1FMKCksCiAgZGJuYW1lID0gY29uZmlnJGRibmFtZSwKICBob3N0ID0gY29uZmlnJGhvc3QsCiAgcG9ydCA9IGNvbmZpZyRwb3J0LAogIHVzZXIgPSBjb25maWckdXNlciwKICBwYXNzd29yZCA9IGNvbmZpZyRwYXNzd29yZCwKICBvcHRpb24gPSBjb25maWckb3B0aW9uCikKc2VsZWN0ZWRfc2NoZW1hIDwtIHN0cl9yZW1vdmUoY29uZmlnJG9wdGlvbiwgcGF0dGVybiA9ICItYyBzZWFyY2hfcGF0aD0iKQpgYGAKCiMjIFQgY2VsbCBlcGl0b3BlIHRhYmxlCgpUaGUgdGFibGUgYmVsb3cgaXMgdGhlIG9yaWdpbmFsIGRvd25sb2FkZWQgdGFibGU6CgpgYGB7cn0KZiA8LSAiL3YvcHJvamVjdHMvZ2VubzJwaGVuby1rcGFwcC9kbmlldXcvTUhDSS1TcGlrZS1lcGl0b3Blcy50c3YiCmVwaXRvcGVfcmF3X3JlZiA8LSByZWFkX3RzdihmLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFKVssIGMoMSw1LDYpXQpuYW1lcyhlcGl0b3BlX3Jhd19yZWYpIDwtIGMoImlkIiwgInJlZmVyZW5jZXMiLCAiYXNzYXlzIikKZXBpdG9wZV9yYXdfcmVmJGlkIDwtIGFzLmNoYXJhY3RlcihlcGl0b3BlX3Jhd19yZWYkaWQpCgpmIDwtICIvdi9wcm9qZWN0cy9nZW5vMnBoZW5vLWtwYXBwL2RuaWV1dy9NSENJLVNwaWtlLWVwaXRvcGVzX21ldGFkYXRhLnRzdiIKZXBpdG9wZV9yYXcgPC0gcmVhZF90c3YoZiwgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSlbMToxN10KbmFtZXMoZXBpdG9wZV9yYXcpIDwtIGMoImllZGJfaXJpIiwgIm9iamVjdF90eXBlIiwgIm5hbWUiLCAibW9kaWZpZWRfcmVzaWR1ZSIsIAogICAgICAgICAgICAgICAgICAgICJtb2RpZmljYXRpb25zIiwgInN0YXJ0IiwgImVuZCIsICJpcmkiLCAic3lub255bXMiLAogICAgICAgICAgICAgICAgICAgICJzb3VyY2VfbW9sZWN1bGUiLCAic291cmNlX21vbGVjdWxlX2lyaSIsICJtb2xlY3VsZV9wYXJlbnQiLAogICAgICAgICAgICAgICAgICAgICJtb2xlY3VsZV9wYXJlbnRfaXJpIiwgInNvdXJjZV9vcmdhbmlzbSIsCiAgICAgICAgICAgICAgICAgICAgInNvdXJjZV9vcmdhbmlzbV9pcmkiLCAic3BlY2llcyIsICJzcGVjaWVzX2lyaSIpCmVwaXRvcGVfcmF3IDwtIGVwaXRvcGVfcmF3ICU+JQogIHNlcGFyYXRlKGNvbCA9IGllZGJfaXJpLCBpbnRvID0gYyhOQSwgTkEsIE5BLCBOQSwgImlkIiksCiAgICAgICAgICAgc2VwID0gIi8iLCByZW1vdmUgPSBGQUxTRSkgJT4lCiAgaW5uZXJfam9pbihlcGl0b3BlX3Jhd19yZWYsIGJ5ID0gImlkIikgJT4lCiAgc2VsZWN0KGlkLCBuYW1lLCBzdGFydCwgZW5kLCByZWZlcmVuY2VzLCBhc3NheXMsIGV2ZXJ5dGhpbmcoKSkKICAKZXBpdG9wZV9yYXcKYGBgCiogVGFibGUgYWJvdmUgc2hvd3MgdGhhdCB0aGVyZSBhcmUgNjUzIGVwaXRvcGVzIGluIHRoZSByYXcgZXBpdG9wZSB0YWJsZQoKTmVlZCB0byBjb25jZW50cmF0ZSBvbmx5IHRob3NlIGVwaXRvcGVzIHRob3NlIHdlcmUgcHJlc2VudCBpbiBtb3JlIHRoYW4gMSBwcmVzZW50YXRpb24KCmBgYHtyfQplcGl0b3BlIDwtIGVwaXRvcGVfcmF3ICU+JQogIGZpbHRlcihyZWZlcmVuY2VzPjEpCmVwaXRvcGUKYGBgCiogVGhlcmUgYXJlIDE5MCBvdXQgb2YgNjUzIGVwaXRvcGVzIHRob3NlIHdlcmUgcHVibGlzaGVkIG1vcmUgdGhhbiBhIHNpbmdsZSBwYXBlcgoKYGBge3J9CmVwaXRvcGUlPiUKICBmaWx0ZXIoaXMubmEoc3RhcnQpKQpgYGAKKiBUaGVyZSBpcyBhIHNpbmdsZSBlcGl0b3BlIChgMTU5Mzg0OGApIHdoZXJlIHRoZSBzdGFydCBhbmQgZW5kIHBvc2l0aW9uIGlzIG1pc3NpbmcuIEJhc2VkIG9uIERhdmlkJ3MgYWxpZ25tZW50ICgiL3YvcHJvamVjdHMvZ2VubzJwaGVuby1rcGFwcC9kbmlldXcvTUhDSS1TcGlrZS1lcGl0b3Blc19hbGlnbmVkLmZhYSIpLCB0aGUgcG9zaXRpb24gb2YgdGhpcyBlcGl0b3BlIGlzIHNpbWlsYXIgdG8gaHR0cHM6Ly93d3cuaWVkYi5vcmcvZXBpdG9wZS8xMzA5MTQ3IGVwaXRvcGUuIFRoZXJlIGRpZmZlcmVuY2UgaXMgYSBzaW5nbGUgbXV0YXRpb24gYXQgYW1pbm8gYWNpZGUgbGV2ZWwuIFNvIHRoaXMgZXBpdG9wZSBkb2VzIG5vdCBkZXJpdmUgZnJvbSByZWZlcmVuY2Ugc2VxdWVuY2UgYnV0IHRoZSBzdGFydC1lbmQgcG9zaXRpb24gd2lsbCBiZSBpbmNsdWRlZCB0aGUgdGFibGU6CgoKYGBge3J9CmVwaXRvcGVbZXBpdG9wZSRpZD09IjE1OTM4NDgiLCAic3RhcnQiXSA8LSAyNjkKZXBpdG9wZVtlcGl0b3BlJGlkPT0iMTU5Mzg0OCIsICJlbmQiXSA8LSAyNzcKYGBgCgpOZWVkIHRvIGNoZWNrIGlmIGVwaXRvcGUgc2VxdWVuY2VzIGRlcml2ZWQgZnJvbSB0aGUgcmVmZXJlbmNlIHNlcXVlbmNlLgoKVGhlIHJlZmVyZW5jZSBzZXF1ZW5jZSBpcyBoZXJlOiBodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L251Y2NvcmUvTU45MDg5NDcKU3Bpa2UgcHJvdGVpbiAoUUhENDM0MTYuMSkgaXMgY29kZWQgaW4gdGhlIHJlZmVyZW5jZSBzZXF1ZW5jZSBwb3NpdGlvbjogMjE1NjMtMjUzODQKCkRhdGFiYXNlIHdpbGwgYmUgc2VhcmNoZWQgZm9yIHRoZSByZWZlcmVuY2UgU3Bpa2UgcHJvdGVpbiBzZXF1ZW5jZToKCmBgYHtzcWwgY29ubmVjdGlvbj1jb24sIG91dHB1dC52YXI9c3Bpa2Vfc2VxfQpzZWxlY3QgKiBmcm9tIGRhdGFodWJfMC50cmFuc2xhdGVkX2FhX3JlZl9zZXF1ZW5jZSgyMTU2MywgMjUzODQpCmBgYAoKKiBOb3RlOiB0aGUgImRhdGFodWJfMC5iYXJjb2Rlc193aXRoX211dGF0aW9uX3Bvc2l0aW9ucyh7c3RhcnR9LCB7ZW5kfSkiIFNRTCBmdW5jdGlvbiB0cmFuc2xhdGUgdGhlIHJlZmVyZW5jZSBudWNsZW90aWRlIHNlcXVlbmNlIGludG8gcHJvdGVpbiBzZXF1ZW5jZSAodGhlIHBhcmFtZXRlcnMgYXJlIHRoZSBzdGFydCBhbmQgZW5kIHBvc2l0aW9ucyBvZiByZWZlcmVuY2UgbnVjbGVvdGlkZSBzZXF1ZW5jZSkKCmBgYHtyfQpzcGlrZV9zZXEgPC0gc3RyX2Moc3Bpa2Vfc2VxJGFhX3N5bWJvbCwgY29sbGFwc2UgPSAiIikKc3Bpa2Vfc2VxCmBgYAoKQ2hlY2sgaWYgdGhlIGBuYW1lYCBjb2x1bW4gb2YgdGhlIGVwaXRvcGUgdGFibGUgY29udGFpbnMgcmVhbGx5IHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UuIAoKYGBge3J9CmZvciAoaSBpbiAxOm5yb3coZXBpdG9wZSkpIHsKICByZWYgPC0gc3RyX3N1YihzcGlrZV9zZXEsCiAgICAgICAgICAgICAgICAgc3RhcnQgPSBlcGl0b3BlW2ksICdzdGFydCddLAogICAgICAgICAgICAgICAgIGVuZCA9IGVwaXRvcGVbaSwgJ2VuZCddKQogIGlmIChlcGl0b3BlW2ksICduYW1lJ10gPT0gcmVmKSBlcGl0b3BlW2ksICdtYXRjaF93aXRoX3JlZiddIDwtIFRSVUUgZWxzZSBlcGl0b3BlW2ksICdtYXRjaF93aXRoX3JlZiddIDwtIEZBTFNFCn0KYGBgCgpMZXRzIHNlZSB0aGUgcmVzdWx0IG9mIHRoZSBjb21wYXJpc29uCgpgYGB7cn0KdGFibGUoZXBpdG9wZSRtYXRjaF93aXRoX3JlZikKYGBgClRoZXJlIGFyZSA2IGVwaXRvcGVzIHdoZXJlIHRoZSBgbmFtZWAgY29sdW1uIGRvZXMgbm90IG1hdGNoIHdpdGggdGhlIHJlZmVyZW5jZS4gCgpUaGVzZSBhcmUgdGhlIGVwaXRvcGVzIHRob3NlIGRvIG5vdCBtYXRjaCB3aXRoIHJlZmVyZW5jZSBzZXF1ZW5jZToKCmBgYHtyfQplcGl0b3BlW2VwaXRvcGUkbWF0Y2hfd2l0aF9yZWY9PUZBTFNFLF0KYGBgCgoqIFRoZXNlIGVwaXRvcGVzIGFib3ZlIGFyZSBpbXBvcnRhbnQgb25lLCBidXQgbm93IGZvciBzaW1wbGljaXR5IEkgd2lsbCBleGNsdWRlIHRoZW0sIGxhdGVyIHRoZXNlIGVwaXRvcGVzIHdpbGwgYmUgY2hlY2tlZC4KCiMjIyBUYWJsZSBvZiBUIGNlbGwgZXBpdG9wZXMgaW5jbHVkZWQgdGhlIGFuYWx5c2lzIHsjYTF9CgpUaGUgdGFibGUgYmVsb3cgY29udGFpbnMgdGhlIGVwaXRvcGVzIHRob3NlIGRlcml2ZWQgZnJvbSByZWZlcmVuY2Ugc2VxdWVuY2U6CgpgYGB7cn0KZXBpdG9wZV9zZWxlY3RlZCA8LSBlcGl0b3BlICU+JQogIGZpbHRlcihtYXRjaF93aXRoX3JlZikgCmVwaXRvcGVfc2VsZWN0ZWQKYGBgCiMjIyBQb3NpdGlvbiBvZiBlcGl0b3BlcyBvbiByZWZlcmVuY2Ugc3Bpa2Ugc2VxdWVuY2UgeyNhMn0KCgpgYGB7cn0KeCA8LSBlcGl0b3BlX3NlbGVjdGVkICU+JQogIHNlbGVjdChpZCwgc3RhcnQsIGVuZCkgJT4lCiAgbXV0YXRlKHN0YXJ0ID0gYXMubnVtZXJpYyhzdGFydCksCiAgICAgICAgIGVuZCA9IGFzLm51bWVyaWMoZW5kKSkgJT4lCiAgYXJyYW5nZShzdGFydCkKeCR5IDwtIDE6bnJvdyh4KQp4IDwtIHggJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydDplbmQsIHZhbHVlc190byA9ICJ4IikKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSB4LCBhZXMoeCA9IHgsIHkgPSB5LCBmaWxsID0gaWQpKSArCiAgZ2VvbV9saW5lKGFscGhhID0gMC40KSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgVC1jZWxsIGVwaXRvcGVzIG9uIHNwaWtlIHByb3RlaW4iLAogICAgICAgeCA9ICJBbWlubyBhY2lkZSBwb3NpdGlvbiIsCiAgICAgICB5ID0gIiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGFzcGVjdC5yYXRpbz0yLzYpCmBgYAoqIEVhY2ggc2hvcnQgaG9yaXpvbnRhbCBsaW5lIHJlcHJlc2VudCBhIFQgY2VsbCBlcGl0b3BlCgojIyBCYXJjb2RlIHRhYmxlCgpCYXJjb2RlIHdhcyBhc3NpZ25lZCB0byBlYWNoIHNhbXBsZXMuIEEgZ2l2ZW4gYmFyY29kZSBpcyBiYXNpY2FsbHkgYSBtdXRhdGlvbiBwYXR0ZXJuIChhbWlubyBhY2lkZSBsZXZlbCkgb2YgUyBwcm90ZWluLgoKSG93IG1hbnkgZGlmZmVyZW50IFMgcHJvdGVpbiBleGlzdCBub3cgaW4gdGhlIGRhdGFiYXNlPwoKYGBge3NxbCBjb25uZWN0aW9uPWNvbn0KU0VMRUNUIGNvdW50KCopCkZST00gYmFyY29kZQpgYGAKCgoqIFRoZXJlIGFyZSAzNDY5NzYgYmFyY29kZXMgaW4gdGhlIGRhdGFiYXNlLgoKSSB3aWxsIHVzZSBvbmx5IHRob3NlIGJhcmNvZGVzIHRoYXQgd2VyZSBhc3NpZ25lZCB0byBtb3JlIHRoYW4gMTAwIHNhbXBsZXM6CgpgYGB7c3FsIGNvbm5lY3Rpb249Y29uLCBvdXRwdXQudmFyID0gImJhcmNvZGVfaGlzdCJ9CldJVEggYmFyY29kZV9kaXN0IEFTICgKICAgICBTRUxFQ1QgYmFyY29kZV9pZCwgY291bnQoKikgQVMgc2FtcGxlX251bWJlcl9pbl9iYWNvZGUKICAgICAgICAgIEZST00gYWFfbXV0YXRpb25fbmV3IEFTIGEKICAgICAgICAgIEdST1VQIEJZIGEuYmFyY29kZV9pZAopCgpTRUxFQ1Qgc2FtcGxlX251bWJlcl9pbl9iYWNvZGUsIGNvdW50KCopIEFTIG51bWJlcl9vZl9iYXJjb2RlCiAgICAgRlJPTSBiYXJjb2RlX2Rpc3QKICAgICBHUk9VUCBCWSBzYW1wbGVfbnVtYmVyX2luX2JhY29kZQogICAgIE9SREVSIEJZIG51bWJlcl9vZl9iYXJjb2RlIERFU0MKYGBgCgpgYGB7cn0KYmFyY29kZV9oaXN0ICU+JQogIGZpbHRlcihzYW1wbGVfbnVtYmVyX2luX2JhY29kZSA+IDEwMCkgJT4lCiAgc3VtbWFyaXNlKGBOdW1iZXIgb2YgYmFyY29kZXNgID0gc3VtKG51bWJlcl9vZl9iYXJjb2RlKSkKYGBgCgpUaGVyZSBhcmUgMTk3MiBiYXJjb2RlcyB0aGF0IGFwcGVhcmVkIG1vcmUgdGhhbiAxMDAgc2FtcGxlcyBpbiB0aGUgZGF0YWJhc2UuIAoKCmBgYHtyfQpiYXJjb2RlX2hpc3QgJT4lCiAgZmlsdGVyKHNhbXBsZV9udW1iZXJfaW5fYmFjb2RlID4gMTAwKSAlPiUKICBzdW1tYXJpc2UoYE51bWJlciBvZiBzYW1wbGVzIGJlbG9uZ3MgdG8gdGhlIHNlbGVjdGVkIGJhcmNvZGVzYCA9IHN1bShzYW1wbGVfbnVtYmVyX2luX2JhY29kZSkpCmBgYAoqIEFyb3VuZCAxLjggbWlsbGlvbiBzYW1wbGVzIHdpbGwgYmUgaW5jbHVkZWQgaW50byB0aGlzIGFuYWx5c2lzCgpMaXN0IG9mIGJhcmNvZGVzIHRoYXQgd2lsbCBiZSBpbmNsdWRlZCB0aGlzIGFuYWx5c2lzOgoKYGBge3NxbCBjb25uZWN0aW9uPWNvbiwgb3V0cHV0LnZhciA9ICJiYXJjb2RlcyJ9CldJVEggYmFyY29kZV9kaXN0IEFTICgKICAgICBTRUxFQ1QgYmFyY29kZV9pZCwgY291bnQoKikgQVMgc2FtcGxlX251bWJlcl9pbl9iYWNvZGUKICAgICAgICAgIEZST00gYWFfbXV0YXRpb25fbmV3IEFTIGEKICAgICAgICAgIEdST1VQIEJZIGEuYmFyY29kZV9pZAopCgpTRUxFQ1QgYmFyY29kZV9pZCwgc2FtcGxlX251bWJlcl9pbl9iYWNvZGUsIGFhX211dGF0aW9uCiAgICAgRlJPTSBiYXJjb2RlX2Rpc3QKICAgICBMRUZUIEpPSU4gYmFyY29kZSBPTiBiYXJjb2RlLmlkPWJhcmNvZGVfZGlzdC5iYXJjb2RlX2lkCiAgICAgV0hFUkUgYmFyY29kZV9kaXN0LnNhbXBsZV9udW1iZXJfaW5fYmFjb2RlID4gMTAwCmBgYAoKSGVhZCBvZiB0aGUgdGFibGU6CmBgYHtyfQpoZWFkKGJhcmNvZGVzKQpgYGAKCiMjIE1hdGNoIGJhcmNvZGVzIHdpdGggVCBjZWxsIGVwaXRvcGVzCgpgYGB7cn0KbXV0YXRpb24gPC0gYmFyY29kZXMgJT4lCiAgbXV0YXRlKG11dCA9IGFhX211dGF0aW9uKSAlPiUKICBtdXRhdGUobXV0ID0gc3RyX3JlbW92ZShtdXQsIHBhdHRlcm4gPSAiXFx7IiksCiAgICAgICAgIG11dCA9IHN0cl9yZW1vdmUobXV0LCBwYXR0ZXJuID0gIlxcfSIpLAogICAgICAgICBtdXQgPSBzdHJfcmVtb3ZlX2FsbChtdXQsIHBhdHRlcm4gPSAicFxcLiIpKSAlPiUKICBtdXRhdGUobiA9IChzdHJfY291bnQobXV0LCBwYXR0ZXJuID0gIiwiKSArIDEpKQoKbWF4X211dCA8LSBtYXgobXV0YXRpb24kbikKaWRzIDwtIHN0cl9jKCJ4IiwgMTptYXhfbXV0KQoKbXV0YXRpb248LSBtdXRhdGlvbiU+JQogIHNlcGFyYXRlKG11dCwgaW50byA9IGlkcywgc2VwID0gIiwiLCBmaWxsICA9ICJyaWdodCIpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYWxsX29mKGlkcyksIHZhbHVlc190byA9ICJtdXQiLAogICAgICAgICAgICAgICBuYW1lc190byA9ICJtdXRfcG9zX2luX2JhcmNvZGUiKSAlPiUKICBkcm9wX25hKG11dCkgJT4lCiAgbXV0YXRlKHBvcyA9IGFzLm51bWVyaWMoc3RyX3N1YihtdXQsIHN0YXJ0ID0gNCwgZW5kID0gLTQpKSkKCmhlYWQobXV0YXRpb24pCmBgYAoKKiBUYWJsZSBhYm92ZSBzaG93cyB0aGUgcG9zaXRpb25zIG9mIG11dGF0aW9ucyBpbiB0aGUgc2VsZWN0ZWQgYmFyY29kZXMuCgpTZWxlY3QgdGhvc2UgZXBpdG9wZXMgd2hlcmUgc3RhcnQtZW5kIHBvc2l0aW9uIGluY2x1ZGVzIGFueSBvZiB0aGUgbXV0YXRpb24gcG9zaXRpb25zIGFib3ZlLgoKYGBge3J9Cm11dGF0aW9uX2FsbCA8LSB0aWJibGUoYmFyY29kZV9pZCA9IGludGVnZXIoKSwKICAgICAgICAgICAgICAgICAgICAgICBzYW1wbGVfbnVtYmVyX2luX2JhY29kZSA9IG51bWVyaWMoKSwKICAgICAgICAgICAgICAgICAgICAgICBhYV9tdXRhdGlvbiA9IGNoYXJhY3RlcigpLAogICAgICAgICAgICAgICAgICAgICAgIG4gPSBudW1lcmljKCksCiAgICAgICAgICAgICAgICAgICAgICAgbXV0X3Bvc19pbl9iYXJjb2RlID0gY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgbXV0ID0gY2hhcmFjdGVyKCksCiAgICAgICAgICAgICAgICAgICAgICAgcG9zID0gbnVtZXJpYygpLAogICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gbnVtZXJpYygpLAogICAgICAgICAgICAgICAgICAgICAgIGVuZCA9IG51bWVyaWMoKSwKICAgICAgICAgICAgICAgICAgICAgICBpZWRiX2lkPSBjaGFyYWN0ZXIoKSkKCmZvciAoaSBpbiByb3cubmFtZXMoZXBpdG9wZV9zZWxlY3RlZCkpIHsKICB4IDwtIG11dGF0aW9uICU+JQogICAgbXV0YXRlKHN0YXJ0ID0gYXMubnVtZXJpYyhlcGl0b3BlX3NlbGVjdGVkW2ksICJzdGFydCJdKSwKICAgICAgICAgICBlbmQgPSBhcy5udW1lcmljKGVwaXRvcGVfc2VsZWN0ZWRbaSwgImVuZCJdKSwKICAgICAgICAgICBpZWRiX2lkID0gYXMuY2hhcmFjdGVyKGVwaXRvcGVfc2VsZWN0ZWRbaSwgImlkIl0pKSAlPiUKICAgIG11dGF0ZShpc19tdXRhdGlvbl9wcmVzZW50ID0gaWZlbHNlKChwb3MgPj0gc3RhcnQgJiBwb3MgPD0gZW5kKSwgVFJVRSwgRkFMU0UpKSAlPiUKICAgIGZpbHRlcihpc19tdXRhdGlvbl9wcmVzZW50KSAlPiUKICAgIHNlbGVjdCgtaXNfbXV0YXRpb25fcHJlc2VudCkKICBtdXRhdGlvbl9hbGwgPC0gbXV0YXRpb25fYWxsICU+JQogICAgYWRkX3Jvdyh4KQp9CmBgYAoKYGBge3J9Cm11dGF0aW9uX2FsbF9tb2QgPC0gbXV0YXRpb25fYWxsICU+JQogIG11dGF0ZShyZWZfZXBpdG9wZSA9IHN0cl9zdWIoc3Bpa2Vfc2VxLCBzdGFydCA9IHN0YXJ0LCBlbmQgPSBlbmQpKSAlPiUKICBzZWxlY3QoLW11dF9wb3NfaW5fYmFyY29kZSkgJT4lCiAgc2VsZWN0KC1wb3MpCgp4IDwtIG11dGF0aW9uX2FsbF9tb2QgJT4lCiAgc2VsZWN0KC1tdXQpICU+JQogIHNlbGVjdCgtbikgJT4lCiAgZGlzdGluY3QoKQoKbXV0YXRpb25fZmluIDwtIG11dGF0aW9uX2FsbF9tb2QgJT4lCiAgc2VsZWN0KGJhcmNvZGVfaWQsIGllZGJfaWQsIG11dCkgJT4lCiAgZ3JvdXBfYnkoYmFyY29kZV9pZCwgaWVkYl9pZCkgJT4lCiAgc3VtbWFyaXplKG11dGF0aW9uX2xpc3QgPSBzdHJfYyhtdXQsIGNvbGxhcHNlID0gIiwiKSkgJT4lCiAgaW5uZXJfam9pbih4LCBieSA9IGMoImJhcmNvZGVfaWQiLCAiaWVkYl9pZCIpKSAlPiUKICBtdXRhdGUobXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0FsYScsIHJlcGxhY2VtZW50ID0gJ0EnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0FyZycsIHJlcGxhY2VtZW50ID0gJ1InKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0FzbicsIHJlcGxhY2VtZW50ID0gJ04nKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0FzcCcsIHJlcGxhY2VtZW50ID0gJ0QnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0N5cycsIHJlcGxhY2VtZW50ID0gJ0MnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0dsbicsIHJlcGxhY2VtZW50ID0gJ1EnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0dsdScsIHJlcGxhY2VtZW50ID0gJ0UnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0hpcycsIHJlcGxhY2VtZW50ID0gJ0gnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0lsZScsIHJlcGxhY2VtZW50ID0gJ0knKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0xldScsIHJlcGxhY2VtZW50ID0gJ0wnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0x5cycsIHJlcGxhY2VtZW50ID0gJ0snKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ01ldCcsIHJlcGxhY2VtZW50ID0gJ00nKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1BoZScsIHJlcGxhY2VtZW50ID0gJ0YnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1BybycsIHJlcGxhY2VtZW50ID0gJ1AnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1NlcicsIHJlcGxhY2VtZW50ID0gJ1MnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1RocicsIHJlcGxhY2VtZW50ID0gJ1QnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1RycCcsIHJlcGxhY2VtZW50ID0gJ1cnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1R5cicsIHJlcGxhY2VtZW50ID0gJ1knKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ1ZhbCcsIHJlcGxhY2VtZW50ID0gJ1YnKSwKICAgICAgICAgbXV0YXRpb25fbGlzdCA9IHN0cl9yZXBsYWNlX2FsbChtdXRhdGlvbl9saXN0LCBwYXR0ZXJuID0gJ0dseScsIHJlcGxhY2VtZW50ID0gJ0cnKQogICAgICAgICApCmBgYAoKCmBgYHtyfQpmb3IgKGkgaW4gcm93Lm5hbWVzKG11dGF0aW9uX2ZpbikpewogIG11dF9saXN0IDwtIHVubGlzdChzdHJzcGxpdChhcy5jaGFyYWN0ZXIobXV0YXRpb25fZmluW2ksICJtdXRhdGlvbl9saXN0Il0pLCAiLCIpKQogIG11dGF0aW9uX2ZpbltpLCAibXV0X2VwaXRvcGUiXSA8LSBtdXRhdGlvbl9maW5baSwgInJlZl9lcGl0b3BlIl0KICBmb3IgKGogaW4gbXV0X2xpc3QpIHsKICAgIHJlcGxhY2VtZW50X3BvcyA8LSBhcy5udW1lcmljKHN0cl9zdWIoaiwgc3RhcnQgPSAyLCBlbmQgPSAtMikpIC0gYXMubnVtZXJpYyhtdXRhdGlvbl9maW5baSwgInN0YXJ0Il0pICsxCiAgICByZXBsYWNlbWVudF9BQSA8LSBzdHJfc3ViKGosIHN0YXJ0ID0gLTEsIGVuZCA9IC0xKQogICAgbXV0YXRpb25fZmluW2ksICJtdXRfZXBpdG9wZSJdIDwtIHN0cl9jKHN0cl9zdWIobXV0YXRpb25fZmluW2ksICJtdXRfZXBpdG9wZSJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnQgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kID0gKHJlcGxhY2VtZW50X3BvcyAtIDEpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlbWVudF9BQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfc3ViKG11dGF0aW9uX2ZpbltpLCAibXV0X2VwaXRvcGUiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YXJ0ID0gKHJlcGxhY2VtZW50X3BvcyArIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kID0gLTEpKQogIH0KfQpoZWFkKG11dGF0aW9uX2ZpbikKYGBgCgpUaGVyZSBhcmUgMzgxNTUgbXV0YXRlZCBlcGl0b3BlcyBpbiB0aGUgdGFibGUgYWJvdmUsIGJ1dCBtYW55IG9mIHRoZW0gYXBwZWFyIGluIG1vcmUgdGhhbiBvbmUgdGltZXMgKGlmIHRoZSBzYW1lIG11dGF0ZWQgZXBpdG9wZSBhcHBlYXJzIGluIG1vcmUgdGhhbiBvbmUgYmFyY29kZSwgdGhlbiBpdCBpdCB3aWxsIGJlIHByZXNlbnQgbW9yZSB0aGFuIG9uZSByb3cpIAoKIyMjIE11dGF0ZWQgZXBpdG9wZSB0YWJsZSB7I2EzfQoKYGBge3J9Cm11dGF0ZWRfZXBpdG9wZV90YWJsZSA8LSBtdXRhdGlvbl9maW4gJT4lCiAgdW5ncm91cCgpICU+JQogIHNlbGVjdChpZWRiX2lkLCByZWZfZXBpdG9wZSwgbXV0X2VwaXRvcGUpICU+JQogIGRpc3RpbmN0KCkKbXV0YXRlZF9lcGl0b3BlX3RhYmxlICAKYGBgCiogVGhlIHRhYmxlIGFib3ZlIGNvbnRhaW5zIDkzNSBtdXRhdGVkIGVwaXRvcGVzLiBUaGlzIGlzIHRoZSBmaW5hbCBsaXN0LCB0aGVzZSBuZWVkIHRvIGJlIGNoZWNrZWQgaWYgdGhlaXIgYmluZGluZyBhZmZpbml0eSBjaGFuZ2VzIGFzIGEgcmVzdWx0IG9mIG11dGF0aW9uCgoKYGBge3J9CndyaXRlX3RzdihtdXRhdGVkX2VwaXRvcGVfdGFibGUsIGZpbGUgPSAiL3YvcHJvamVjdHMvZ2VubzJwaGVuby1rcGFwcC9rcGFwcC90X2NlbGxfZXBpdG9wL2RhdGEvbXV0YXRlZF9lcGl0b3Blcy50c3YiKQpgYGAKCmBgYHtyfQp3cml0ZV90c3YobXV0YXRpb25fZmluLCBmaWxlID0gIi92L3Byb2plY3RzL2dlbm8ycGhlbm8ta3BhcHAva3BhcHAvdF9jZWxsX2VwaXRvcC9kYXRhL211dGF0ZWRfZXBpdG9wZXNfYmFyY29kZXMudHN2IikKYGBgCgpgYGB7cn0Kd3JpdGVfdHN2KG11dGF0aW9uX2FsbCwgZmlsZSA9ICIvdi9wcm9qZWN0cy9nZW5vMnBoZW5vLWtwYXBwL2twYXBwL3RfY2VsbF9lcGl0b3AvZGF0YS9tdXRhdGVkX2VwaXRvcGVzX2JhcmNvZGVzX2FsbC50c3YiKQpgYGAKCgoKCgoKCg==