1 Aims

  • Check number of samples in time where a given T-cell epitope altered

2 Background

  • Already determined T cell epitopes were downloaded (https://www.iedb.org/) (“/v/projects/geno2pheno-kpapp/small_data/Tcell”)

  • The COVEO database will be searched for these epitopes

3 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=")

3.1 T cell epitope table

The table below is the original download table:

f <- "/v/projects/geno2pheno-kpapp/small_data/Tcell/CD4_MHC_II_epitope_table_export_1693249691.xlsx"
epitope_raw <- read_excel(f, skip = 1)[,1:17]
New names:
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

There are a few rows, where the start and end positions are not filled, these will be removed.

epitope <- epitope_raw %>%
  drop_na(name, start, end)
epitope

Based on the table it looks that these sequences derived from reference sequences, but need to check it.

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"
str_length(spike_seq)
[1] 1273

The Spike protein contains 1273 amino acides

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 
   22   563 

There are 22 epitopes where the name column does not match with the reference.

These are the epitopes those do not match:

epitope[epitope$match_with_ref==FALSE,]

Later needs to focus on these too, but in the first round I will remove them from the list.

The table below contains the epitopes those derived from reference sequence:

epitope_selected <- epitope %>%
  filter(match_with_ref) %>%
  separate(col = iedb_iri, into = c(NA, NA, NA, NA, "iedb_id"),
           sep = "/", remove = FALSE)
epitope_selected

In the next step those barcodes (and samples) will be selected that contains mutation between the S protein epitope defined start-end position. These samples contain altered T-cell target sequence that maybe not recognized anymore by the given T-cell clone.

d <- tibble(collection_date = Date())

for (i in 1:nrow(epitope_selected)){
#for (i in 1:2){
  start <- epitope_selected[i, 'start']
  end <- epitope_selected[i, 'end']
  id <- str_c("id_", as.character(epitope_selected[i, 'iedb_id']))
  
  sql_query1 <- glue_sql("
    SELECT collection_date, count(*)
    FROM (SELECT distinct(barcode_id) FROM datahub_0.barcodes_with_mutation_positions({start}, {end})) as f
    JOIN datahub_0.aa_mutation_new amn
      ON amn.barcode_id = f.barcode_id
    GROUP BY collection_date
  ", .con = con)
  x <- dbGetQuery(con, sql_query1) %>%
    drop_na()
  names(x) <- c("collection_date", id)
  
  d <- d %>%
    full_join(x, by = "collection_date")
}
save(d, file = "/v/projects/geno2pheno-kpapp/kpapp/t_cell_epitop/d_CD4_MHC_II_epitope_table_export_1693249691.Rdata")

Summarize the counts for weeks:

  • Above is the head of the table that contains how many samples were collected on a given week (‘date’ column is the date of the first day of a given week). These samples contains altered T-cell epitope region. The iedb_id column is the id of a given epitope (eg. “id_100428”) that can be used to get further information (eg. https://www.iedb.org/epitope/100428)

How many sample was collected on a given week? (This will be the background)

SELECT collection_date, count(*)
FROM aa_mutation_new
GROUP BY collection_date
names(all_samples) <- c("collection_date", "all")
all_samples <- all_samples %>%
  mutate(iso_year=isoyear(collection_date),
         iso_week= isoweek(collection_date)) %>%
  filter(iso_year > 2020,
         iso_year < 2024) %>%
  mutate(iso_week = ifelse(iso_week < 10, str_c("0", as.character(iso_week)),
                           as.character(iso_week))) %>%
  mutate(date = ISOweek2date(paste(iso_year, "-W", iso_week, "-1", sep = ""))) %>%
  select(!c("iso_year", "iso_week", "collection_date")) %>%
  mutate(date = as.character(date)) %>%
  group_by(date) %>%
  summarise_at(c("all"), sum)
all_samples$date <- ymd(all_samples$date)
ggplot(data = all_samples, aes(x = date, y = all)) +
  geom_line(show.legend = FALSE) +
  labs(title = "All collected samples",
       x = "Collection date (weekly)",
       y = "Number of sample") +
  theme_minimal()

Visualize the number of samples in time those maybe escaped from a given T-cell clone:

ggplot(data = d, aes(x = date, y = count, color = iedb_id)) +
  geom_line(show.legend = FALSE) +
  labs(title = "Potentially escaped samples",
       x = "Collection date (weekly)",
       y = "Number of sample") +
  theme_minimal()

  • Each line above represent a given T-cell clone

It is also important to know how many samples were collected on a given week

ggplot(data = d, aes(x = date, y = count, color = iedb_id)) +
  geom_line(show.legend = FALSE) +
  geom_line(data = all_samples, aes(x = date, y = all), show.legend = FALSE,
            color = "black", size = 2) +
  labs(title = "Potentially escaped samples",
       subtitle = "(Black represent all sample on a givek week)",
       x = "Collection date (weekly)",
       y = "Number of sample") +
  theme_minimal()

Percentage of the potentially escaped samples:

x <- d %>%
  pivot_wider(names_from = "iedb_id", values_from = "count") %>%
  inner_join(all_samples, by = "date")

for (i in 2:(ncol(x)-1)){
  x[,i] <- x[,i]/x[,"all"]*100
}
x <- x %>%
  select(-all) %>%
  pivot_longer(cols = starts_with("id_"), names_to = "iedb_id",
               values_to = "count")
x$date <- ymd(x$date)


ggplot(data = x, aes(x = date, y = count, color = iedb_id)) +
  geom_line(show.legend = FALSE) +
  labs(title = "Potentially escaped samples",
       x = "Collection date (weekly)",
       y = "Percentage of samples") +
  theme_minimal()

There are 563 epitopes in the epitope table. Which region of the spike protein is covered by these epitopes?

x <- epitope_selected %>%
  select(iedb_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 = iedb_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 an epitope from the epitope table
  • Surprisingly almost the whole molecule is covered
LS0tCnRpdGxlOiAiVCBjZWxsIGVwaXRvcGUiCmF1dGhvcjogIktyaXN6dGlhbiBQYXBwIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBmaWdfY2FwdGlvbjogeWVzCiAgICBmaWdfaGVpZ2h0OiA0CiAgICBmaWdfd2lkdGg6IDUKICAgIHNtb290aF9zY3JvbGw6IHllcwogICAgdGhlbWU6IHNhbmRzdG9uZQogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNQogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCiMgQWltcwoKKiBDaGVjayBudW1iZXIgb2Ygc2FtcGxlcyBpbiB0aW1lIHdoZXJlIGEgZ2l2ZW4gVC1jZWxsIGVwaXRvcGUgYWx0ZXJlZAoKIyBCYWNrZ3JvdW5kCgoqIEFscmVhZHkgZGV0ZXJtaW5lZCBUIGNlbGwgZXBpdG9wZXMgd2VyZSBkb3dubG9hZGVkIChodHRwczovL3d3dy5pZWRiLm9yZy8pICgiL3YvcHJvamVjdHMvZ2VubzJwaGVuby1rcGFwcC9zbWFsbF9kYXRhL1RjZWxsIikKCiogVGhlIENPVkVPIGRhdGFiYXNlIHdpbGwgYmUgc2VhcmNoZWQgZm9yIHRoZXNlIGVwaXRvcGVzCgojIEFuYWx5c2lzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkoREJJKQpgYGAKCmBgYHtyfQpjb25maWcgPC0gY29uZmlnOjpnZXQoKQpjb24gPC0gZGJDb25uZWN0KAogIGRydiA9IFJQb3N0Z3JlU1FMOjpQb3N0Z3JlU1FMKCksCiAgZGJuYW1lID0gY29uZmlnJGRibmFtZSwKICBob3N0ID0gY29uZmlnJGhvc3QsCiAgcG9ydCA9IGNvbmZpZyRwb3J0LAogIHVzZXIgPSBjb25maWckdXNlciwKICBwYXNzd29yZCA9IGNvbmZpZyRwYXNzd29yZCwKICBvcHRpb24gPSBjb25maWckb3B0aW9uCikKc2VsZWN0ZWRfc2NoZW1hIDwtIHN0cl9yZW1vdmUoY29uZmlnJG9wdGlvbiwgcGF0dGVybiA9ICItYyBzZWFyY2hfcGF0aD0iKQpgYGAKCiMjIFQgY2VsbCBlcGl0b3BlIHRhYmxlCgpUaGUgdGFibGUgYmVsb3cgaXMgdGhlIG9yaWdpbmFsIGRvd25sb2FkIHRhYmxlOgoKYGBge3J9CmYgPC0gIi92L3Byb2plY3RzL2dlbm8ycGhlbm8ta3BhcHAvc21hbGxfZGF0YS9UY2VsbC9DRDRfTUhDX0lJX2VwaXRvcGVfdGFibGVfZXhwb3J0XzE2OTMyNDk2OTEueGxzeCIKZXBpdG9wZV9yYXcgPC0gcmVhZF9leGNlbChmLCBza2lwID0gMSlbLDE6MTddCm5hbWVzKGVwaXRvcGVfcmF3KSA8LSBjKCJpZWRiX2lyaSIsICJvYmplY3RfdHlwZSIsICJuYW1lIiwgIm1vZGlmaWVkX3Jlc2lkdWUiLCAKICAgICAgICAgICAgICAgICAgICAibW9kaWZpY2F0aW9ucyIsICJzdGFydCIsICJlbmQiLCAiaXJpIiwgInN5bm9ueW1zIiwKICAgICAgICAgICAgICAgICAgICAic291cmNlX21vbGVjdWxlIiwgInNvdXJjZV9tb2xlY3VsZV9pcmkiLCAibW9sZWN1bGVfcGFyZW50IiwKICAgICAgICAgICAgICAgICAgICAibW9sZWN1bGVfcGFyZW50X2lyaSIsICJzb3VyY2Vfb3JnYW5pc20iLAogICAgICAgICAgICAgICAgICAgICJzb3VyY2Vfb3JnYW5pc21faXJpIiwgInNwZWNpZXMiLCAic3BlY2llc19pcmkiKQplcGl0b3BlX3JhdwpgYGAKVGhlcmUgYXJlIGEgZmV3IHJvd3MsIHdoZXJlIHRoZSBgc3RhcnRgIGFuZCBgZW5kYCBwb3NpdGlvbnMgYXJlIG5vdCBmaWxsZWQsIHRoZXNlIHdpbGwgYmUgcmVtb3ZlZC4KCmBgYHtyfQplcGl0b3BlIDwtIGVwaXRvcGVfcmF3ICU+JQogIGRyb3BfbmEobmFtZSwgc3RhcnQsIGVuZCkKZXBpdG9wZQpgYGAKQmFzZWQgb24gdGhlIHRhYmxlIGl0IGxvb2tzIHRoYXQgdGhlc2Ugc2VxdWVuY2VzIGRlcml2ZWQgZnJvbSByZWZlcmVuY2Ugc2VxdWVuY2VzLCBidXQgbmVlZCB0byBjaGVjayBpdC4gCgpUaGUgcmVmZXJlbmNlIHNlcXVlbmNlIGlzIGhlcmU6IGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvbnVjY29yZS9NTjkwODk0NwpTcGlrZSBwcm90ZWluIChRSEQ0MzQxNi4xKSBpcyBjb2RlZCBpbiB0aGUgcmVmZXJlbmNlIHNlcXVlbmNlIHBvc2l0aW9uOiAyMTU2My0yNTM4NAoKRGF0YWJhc2Ugd2lsbCBiZSBzZWFyY2hlZCBmb3IgdGhlIHJlZmVyZW5jZSBTcGlrZSBwcm90ZWluIHNlcXVlbmNlOgoKYGBge3NxbCBjb25uZWN0aW9uPWNvbiwgb3V0cHV0LnZhcj1zcGlrZV9zZXF9CnNlbGVjdCAqIGZyb20gZGF0YWh1Yl8wLnRyYW5zbGF0ZWRfYWFfcmVmX3NlcXVlbmNlKDIxNTYzLCAyNTM4NCkKYGBgCgoqIE5vdGU6IHRoZSAiZGF0YWh1Yl8wLmJhcmNvZGVzX3dpdGhfbXV0YXRpb25fcG9zaXRpb25zKHtzdGFydH0sIHtlbmR9KSIgU1FMIGZ1bmN0aW9uIHRyYW5zbGF0ZSB0aGUgcmVmZXJlbmNlIG51Y2xlb3RpZGUgc2VxdWVuY2UgaW50byBwcm90ZWluIHNlcXVlbmNlICh0aGUgcGFyYW1ldGVycyBhcmUgdGhlIHN0YXJ0IGFuZCBlbmQgcG9zaXRpb25zIG9mIHJlZmVyZW5jZSBudWNsZW90aWRlIHNlcXVlbmNlKQoKYGBge3J9CnNwaWtlX3NlcSA8LSBzdHJfYyhzcGlrZV9zZXEkYWFfc3ltYm9sLCBjb2xsYXBzZSA9ICIiKQpzcGlrZV9zZXEKYGBgCgpgYGB7cn0Kc3RyX2xlbmd0aChzcGlrZV9zZXEpCmBgYApUaGUgU3Bpa2UgcHJvdGVpbiBjb250YWlucyAxMjczIGFtaW5vIGFjaWRlcwoKQ2hlY2sgaWYgdGhlIGBuYW1lYCBjb2x1bW4gb2YgdGhlIGVwaXRvcGUgdGFibGUgY29udGFpbnMgcmVhbGx5IHRoZSByZWZlcmVuY2Ugc2VxdWVuY2UuIAoKYGBge3J9CmZvciAoaSBpbiAxOm5yb3coZXBpdG9wZSkpIHsKICByZWYgPC0gc3RyX3N1YihzcGlrZV9zZXEsCiAgICAgICAgICAgICAgICAgc3RhcnQgPSBlcGl0b3BlW2ksICdzdGFydCddLAogICAgICAgICAgICAgICAgIGVuZCA9IGVwaXRvcGVbaSwgJ2VuZCddKQogIGlmIChlcGl0b3BlW2ksICduYW1lJ10gPT0gcmVmKSBlcGl0b3BlW2ksICdtYXRjaF93aXRoX3JlZiddIDwtIFRSVUUgZWxzZSBlcGl0b3BlW2ksICdtYXRjaF93aXRoX3JlZiddIDwtIEZBTFNFCn0KYGBgCgpMZXRzIHNlZSB0aGUgcmVzdWx0IG9mIHRoZSBjb21wYXJpc29uCgpgYGB7cn0KdGFibGUoZXBpdG9wZSRtYXRjaF93aXRoX3JlZikKYGBgClRoZXJlIGFyZSAyMiBlcGl0b3BlcyB3aGVyZSB0aGUgYG5hbWVgIGNvbHVtbiBkb2VzIG5vdCBtYXRjaCB3aXRoIHRoZSByZWZlcmVuY2UuIAoKVGhlc2UgYXJlIHRoZSBlcGl0b3BlcyB0aG9zZSBkbyBub3QgbWF0Y2g6CgpgYGB7cn0KZXBpdG9wZVtlcGl0b3BlJG1hdGNoX3dpdGhfcmVmPT1GQUxTRSxdCmBgYApMYXRlciBuZWVkcyB0byBmb2N1cyBvbiB0aGVzZSB0b28sIGJ1dCBpbiB0aGUgZmlyc3Qgcm91bmQgSSB3aWxsIHJlbW92ZSB0aGVtIGZyb20gdGhlIGxpc3QuCgpUaGUgdGFibGUgYmVsb3cgY29udGFpbnMgdGhlIGVwaXRvcGVzIHRob3NlIGRlcml2ZWQgZnJvbSByZWZlcmVuY2Ugc2VxdWVuY2U6CgpgYGB7cn0KZXBpdG9wZV9zZWxlY3RlZCA8LSBlcGl0b3BlICU+JQogIGZpbHRlcihtYXRjaF93aXRoX3JlZikgJT4lCiAgc2VwYXJhdGUoY29sID0gaWVkYl9pcmksIGludG8gPSBjKE5BLCBOQSwgTkEsIE5BLCAiaWVkYl9pZCIpLAogICAgICAgICAgIHNlcCA9ICIvIiwgcmVtb3ZlID0gRkFMU0UpCmVwaXRvcGVfc2VsZWN0ZWQKYGBgCgpJbiB0aGUgbmV4dCBzdGVwIHRob3NlIGJhcmNvZGVzIChhbmQgc2FtcGxlcykgd2lsbCBiZSBzZWxlY3RlZCB0aGF0IGNvbnRhaW5zIG11dGF0aW9uIGJldHdlZW4gdGhlIFMgcHJvdGVpbiBlcGl0b3BlIGRlZmluZWQgc3RhcnQtZW5kIHBvc2l0aW9uLiBUaGVzZSBzYW1wbGVzIGNvbnRhaW4gYWx0ZXJlZCBULWNlbGwgdGFyZ2V0IHNlcXVlbmNlIHRoYXQgbWF5YmUgbm90IHJlY29nbml6ZWQgYW55bW9yZSBieSB0aGUgZ2l2ZW4gVC1jZWxsIGNsb25lLgoKYGBge3J9CmQgPC0gdGliYmxlKGNvbGxlY3Rpb25fZGF0ZSA9IERhdGUoKSkKCmZvciAoaSBpbiAxOm5yb3coZXBpdG9wZV9zZWxlY3RlZCkpewojZm9yIChpIGluIDE6Mil7CiAgc3RhcnQgPC0gZXBpdG9wZV9zZWxlY3RlZFtpLCAnc3RhcnQnXQogIGVuZCA8LSBlcGl0b3BlX3NlbGVjdGVkW2ksICdlbmQnXQogIGlkIDwtIHN0cl9jKCJpZF8iLCBhcy5jaGFyYWN0ZXIoZXBpdG9wZV9zZWxlY3RlZFtpLCAnaWVkYl9pZCddKSkKICAKICBzcWxfcXVlcnkxIDwtIGdsdWVfc3FsKCIKICAgIFNFTEVDVCBjb2xsZWN0aW9uX2RhdGUsIGNvdW50KCopCiAgICBGUk9NIChTRUxFQ1QgZGlzdGluY3QoYmFyY29kZV9pZCkgRlJPTSBkYXRhaHViXzAuYmFyY29kZXNfd2l0aF9tdXRhdGlvbl9wb3NpdGlvbnMoe3N0YXJ0fSwge2VuZH0pKSBhcyBmCiAgICBKT0lOIGRhdGFodWJfMC5hYV9tdXRhdGlvbl9uZXcgYW1uCiAgICAgIE9OIGFtbi5iYXJjb2RlX2lkID0gZi5iYXJjb2RlX2lkCiAgICBHUk9VUCBCWSBjb2xsZWN0aW9uX2RhdGUKICAiLCAuY29uID0gY29uKQogIHggPC0gZGJHZXRRdWVyeShjb24sIHNxbF9xdWVyeTEpICU+JQogICAgZHJvcF9uYSgpCiAgbmFtZXMoeCkgPC0gYygiY29sbGVjdGlvbl9kYXRlIiwgaWQpCiAgCiAgZCA8LSBkICU+JQogICAgZnVsbF9qb2luKHgsIGJ5ID0gImNvbGxlY3Rpb25fZGF0ZSIpCn0KYGBgCgoKYGBge3J9CnNhdmUoZCwgZmlsZSA9ICIvdi9wcm9qZWN0cy9nZW5vMnBoZW5vLWtwYXBwL2twYXBwL3RfY2VsbF9lcGl0b3AvZF9DRDRfTUhDX0lJX2VwaXRvcGVfdGFibGVfZXhwb3J0XzE2OTMyNDk2OTEuUmRhdGEiKQpgYGAKCgpTdW1tYXJpemUgdGhlIGNvdW50cyBmb3Igd2Vla3M6CgpgYGB7cn0KZFtpcy5uYShkKV0gPC0gMApkIDwtIGQgJT4lCiAgbXV0YXRlKGlzb195ZWFyPWlzb3llYXIoY29sbGVjdGlvbl9kYXRlKSwKICAgICAgICAgaXNvX3dlZWs9IGlzb3dlZWsoY29sbGVjdGlvbl9kYXRlKSkgJT4lCiAgZmlsdGVyKGlzb195ZWFyID4gMjAyMCwKICAgICAgICAgaXNvX3llYXIgPCAyMDI0KSAlPiUKICBtdXRhdGUoaXNvX3dlZWsgPSBpZmVsc2UoaXNvX3dlZWsgPCAxMCwgc3RyX2MoIjAiLCBhcy5jaGFyYWN0ZXIoaXNvX3dlZWspKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKGlzb193ZWVrKSkpICU+JQogIG11dGF0ZShkYXRlID0gSVNPd2VlazJkYXRlKHBhc3RlKGlzb195ZWFyLCAiLVciLCBpc29fd2VlaywgIi0xIiwgc2VwID0gIiIpKSkgJT4lCiAgc2VsZWN0KCFjKCJpc29feWVhciIsICJpc29fd2VlayIsICJjb2xsZWN0aW9uX2RhdGUiKSkgJT4lCiAgbXV0YXRlKGRhdGUgPSBhcy5jaGFyYWN0ZXIoZGF0ZSkpICU+JQogIGdyb3VwX2J5KGRhdGUpICU+JQogIHN1bW1hcmlzZV9hbGwoc3VtKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJpZF8iKSwgbmFtZXNfdG8gPSAiaWVkYl9pZCIsCiAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiY291bnQiKQpkJGRhdGUgPC0geW1kKGQkZGF0ZSkKaGVhZChkKQpgYGAKCiogQWJvdmUgaXMgdGhlIGhlYWQgb2YgdGhlIHRhYmxlIHRoYXQgY29udGFpbnMgaG93IG1hbnkgc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZCBvbiBhIGdpdmVuIHdlZWsgKCdkYXRlJyBjb2x1bW4gaXMgdGhlIGRhdGUgb2YgdGhlIGZpcnN0IGRheSBvZiBhIGdpdmVuIHdlZWspLiBUaGVzZSBzYW1wbGVzIGNvbnRhaW5zIGFsdGVyZWQgVC1jZWxsIGVwaXRvcGUgcmVnaW9uLiBUaGUgYGllZGJfaWRgIGNvbHVtbiBpcyB0aGUgaWQgb2YgYSBnaXZlbiBlcGl0b3BlIChlZy4gImlkXzEwMDQyOCIpIHRoYXQgY2FuIGJlIHVzZWQgdG8gZ2V0IGZ1cnRoZXIgaW5mb3JtYXRpb24gKGVnLiBodHRwczovL3d3dy5pZWRiLm9yZy9lcGl0b3BlLzEwMDQyOCkKCkhvdyBtYW55IHNhbXBsZSB3YXMgY29sbGVjdGVkIG9uIGEgZ2l2ZW4gd2Vlaz8gKFRoaXMgd2lsbCBiZSB0aGUgYmFja2dyb3VuZCkKCmBgYHtzcWwgY29ubmVjdGlvbj1jb24sIG91dHB1dC52YXI9YWxsX3NhbXBsZXN9ClNFTEVDVCBjb2xsZWN0aW9uX2RhdGUsIGNvdW50KCopCkZST00gYWFfbXV0YXRpb25fbmV3CkdST1VQIEJZIGNvbGxlY3Rpb25fZGF0ZQpgYGAKCgpgYGB7cn0KbmFtZXMoYWxsX3NhbXBsZXMpIDwtIGMoImNvbGxlY3Rpb25fZGF0ZSIsICJhbGwiKQphbGxfc2FtcGxlcyA8LSBhbGxfc2FtcGxlcyAlPiUKICBtdXRhdGUoaXNvX3llYXI9aXNveWVhcihjb2xsZWN0aW9uX2RhdGUpLAogICAgICAgICBpc29fd2Vlaz0gaXNvd2Vlayhjb2xsZWN0aW9uX2RhdGUpKSAlPiUKICBmaWx0ZXIoaXNvX3llYXIgPiAyMDIwLAogICAgICAgICBpc29feWVhciA8IDIwMjQpICU+JQogIG11dGF0ZShpc29fd2VlayA9IGlmZWxzZShpc29fd2VlayA8IDEwLCBzdHJfYygiMCIsIGFzLmNoYXJhY3Rlcihpc29fd2VlaykpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoaXNvX3dlZWspKSkgJT4lCiAgbXV0YXRlKGRhdGUgPSBJU093ZWVrMmRhdGUocGFzdGUoaXNvX3llYXIsICItVyIsIGlzb193ZWVrLCAiLTEiLCBzZXAgPSAiIikpKSAlPiUKICBzZWxlY3QoIWMoImlzb195ZWFyIiwgImlzb193ZWVrIiwgImNvbGxlY3Rpb25fZGF0ZSIpKSAlPiUKICBtdXRhdGUoZGF0ZSA9IGFzLmNoYXJhY3RlcihkYXRlKSkgJT4lCiAgZ3JvdXBfYnkoZGF0ZSkgJT4lCiAgc3VtbWFyaXNlX2F0KGMoImFsbCIpLCBzdW0pCmFsbF9zYW1wbGVzJGRhdGUgPC0geW1kKGFsbF9zYW1wbGVzJGRhdGUpCmBgYAoKYGBge3J9CmdncGxvdChkYXRhID0gYWxsX3NhbXBsZXMsIGFlcyh4ID0gZGF0ZSwgeSA9IGFsbCkpICsKICBnZW9tX2xpbmUoc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGxhYnModGl0bGUgPSAiQWxsIGNvbGxlY3RlZCBzYW1wbGVzIiwKICAgICAgIHggPSAiQ29sbGVjdGlvbiBkYXRlICh3ZWVrbHkpIiwKICAgICAgIHkgPSAiTnVtYmVyIG9mIHNhbXBsZSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKVmlzdWFsaXplIHRoZSBudW1iZXIgb2Ygc2FtcGxlcyBpbiB0aW1lIHRob3NlIG1heWJlIGVzY2FwZWQgZnJvbSBhIGdpdmVuIFQtY2VsbCBjbG9uZToKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGQsIGFlcyh4ID0gZGF0ZSwgeSA9IGNvdW50LCBjb2xvciA9IGllZGJfaWQpKSArCiAgZ2VvbV9saW5lKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBsYWJzKHRpdGxlID0gIlBvdGVudGlhbGx5IGVzY2FwZWQgc2FtcGxlcyIsCiAgICAgICB4ID0gIkNvbGxlY3Rpb24gZGF0ZSAod2Vla2x5KSIsCiAgICAgICB5ID0gIk51bWJlciBvZiBzYW1wbGUiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoqIEVhY2ggbGluZSBhYm92ZSByZXByZXNlbnQgYSBnaXZlbiBULWNlbGwgY2xvbmUKCkl0IGlzIGFsc28gaW1wb3J0YW50IHRvIGtub3cgaG93IG1hbnkgc2FtcGxlcyB3ZXJlIGNvbGxlY3RlZCBvbiBhIGdpdmVuIHdlZWsKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGQsIGFlcyh4ID0gZGF0ZSwgeSA9IGNvdW50LCBjb2xvciA9IGllZGJfaWQpKSArCiAgZ2VvbV9saW5lKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBnZW9tX2xpbmUoZGF0YSA9IGFsbF9zYW1wbGVzLCBhZXMoeCA9IGRhdGUsIHkgPSBhbGwpLCBzaG93LmxlZ2VuZCA9IEZBTFNFLAogICAgICAgICAgICBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSArCiAgbGFicyh0aXRsZSA9ICJQb3RlbnRpYWxseSBlc2NhcGVkIHNhbXBsZXMiLAogICAgICAgc3VidGl0bGUgPSAiKEJsYWNrIHJlcHJlc2VudCBhbGwgc2FtcGxlIG9uIGEgZ2l2ZWsgd2VlaykiLAogICAgICAgeCA9ICJDb2xsZWN0aW9uIGRhdGUgKHdlZWtseSkiLAogICAgICAgeSA9ICJOdW1iZXIgb2Ygc2FtcGxlIikgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKUGVyY2VudGFnZSBvZiB0aGUgcG90ZW50aWFsbHkgZXNjYXBlZCBzYW1wbGVzOgoKYGBge3J9CnggPC0gZCAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gImllZGJfaWQiLCB2YWx1ZXNfZnJvbSA9ICJjb3VudCIpICU+JQogIGlubmVyX2pvaW4oYWxsX3NhbXBsZXMsIGJ5ID0gImRhdGUiKQoKZm9yIChpIGluIDI6KG5jb2woeCktMSkpewogIHhbLGldIDwtIHhbLGldL3hbLCJhbGwiXSoxMDAKfQp4IDwtIHggJT4lCiAgc2VsZWN0KC1hbGwpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gc3RhcnRzX3dpdGgoImlkXyIpLCBuYW1lc190byA9ICJpZWRiX2lkIiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImNvdW50IikKeCRkYXRlIDwtIHltZCh4JGRhdGUpCgoKZ2dwbG90KGRhdGEgPSB4LCBhZXMoeCA9IGRhdGUsIHkgPSBjb3VudCwgY29sb3IgPSBpZWRiX2lkKSkgKwogIGdlb21fbGluZShzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh0aXRsZSA9ICJQb3RlbnRpYWxseSBlc2NhcGVkIHNhbXBsZXMiLAogICAgICAgeCA9ICJDb2xsZWN0aW9uIGRhdGUgKHdlZWtseSkiLAogICAgICAgeSA9ICJQZXJjZW50YWdlIG9mIHNhbXBsZXMiKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKVGhlcmUgYXJlIDU2MyBlcGl0b3BlcyBpbiB0aGUgZXBpdG9wZSB0YWJsZS4gV2hpY2ggcmVnaW9uIG9mIHRoZSBzcGlrZSBwcm90ZWluIGlzIGNvdmVyZWQgYnkgdGhlc2UgZXBpdG9wZXM/CgpgYGB7cn0KeCA8LSBlcGl0b3BlX3NlbGVjdGVkICU+JQogIHNlbGVjdChpZWRiX2lkLCBzdGFydCwgZW5kKSAlPiUKICBtdXRhdGUoc3RhcnQgPSBhcy5udW1lcmljKHN0YXJ0KSwKICAgICAgICAgZW5kID0gYXMubnVtZXJpYyhlbmQpKSAlPiUKICBhcnJhbmdlKHN0YXJ0KQp4JHkgPC0gMTpucm93KHgpCnggPC0geCAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0OmVuZCwgdmFsdWVzX3RvID0gIngiKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IHgsIGFlcyh4ID0geCwgeSA9IHksIGZpbGwgPSBpZWRiX2lkKSkgKwogIGdlb21fbGluZShhbHBoYSA9IDAuNCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIFQtY2VsbCBlcGl0b3BlcyBvbiBzcGlrZSBwcm90ZWluIiwKICAgICAgIHggPSAiQW1pbm8gYWNpZGUgcG9zaXRpb24iLAogICAgICAgeSA9ICIiKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShhc3BlY3QucmF0aW89Mi82KQpgYGAKKiBFYWNoIHNob3J0IGhvcml6b250YWwgbGluZSByZXByZXNlbnQgYW4gZXBpdG9wZSBmcm9tIHRoZSBlcGl0b3BlIHRhYmxlCiogU3VycHJpc2luZ2x5IGFsbW9zdCB0aGUgd2hvbGUgbW9sZWN1bGUgaXMgY292ZXJlZAoK