Purpose

There are many specimens in the full dataset that are repeated samples from the same participants. This workbook is a quick summary of those samples, so that we can better select a unique set of specimens for downstream analysis. This is particularly important for analyses such as mutation co-occurence, where repeated sampling will inflate apparent co-ocurrence measures.

# load libraries
library(dplyr)
Registered S3 method overwritten by 'dplyr':
  method           from
  print.rowwise_df     

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
# source sample selection funciton
source("independent-samples.R")

set.seed(201910)
root_dir <- rprojroot::find_root(rprojroot::has_dir(".git"))
sample_df <- readr::read_tsv(file.path(root_dir, "data", "pbta-histologies.tsv"))
Parsed with column specification:
cols(
  .default = col_character(),
  age_at_diagnosis_days = col_double(),
  OS_days = col_double()
)
See spec(...) for full column specifications.

WGS and WXS samples

Sort to just WGS samples of tumors

seq_samples <- sample_df %>%
  filter(experimental_strategy %in% c("WGS", "WXS"), 
         sample_type == "Tumor")

How many WGS, how many WXS, summarize #s of samples and participants by

seq_samples %>%
  group_by(experimental_strategy, composition, tumor_descriptor) %>%
  summarise(samples = length(unique(Kids_First_Biospecimen_ID)),
            participants = length(unique(Kids_First_Participant_ID)), 
  ) 

What if we reduce to the earliest sample for each participant?

early_samples <- seq_samples %>%
  group_by(Kids_First_Participant_ID) %>%
  summarize(age_at_diagnosis_days = min(age_at_diagnosis_days)) %>%
  left_join(seq_samples) %>%
  pull(Kids_First_Biospecimen_ID) %>%
  unique()
Joining, by = c("Kids_First_Participant_ID", "age_at_diagnosis_days")
seq_samples %>% 
  filter(Kids_First_Biospecimen_ID %in% early_samples) %>%
  group_by(experimental_strategy, composition, tumor_descriptor) %>%
  summarise(samples = length(unique(Kids_First_Biospecimen_ID)),
            participants = length(unique(Kids_First_Participant_ID)), 
  )

There are still many samples for which there are more samples than participants, indicating duplicates, even among the earliest samples.

Derived cell lines

Are there any samples for which we only have derived cell lines?

derived_participants <- seq_samples %>%
  filter(composition == "Derived Cell Line") %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

non_derived_participants <- seq_samples %>% 
  filter(composition != "Derived Cell Line") %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

notissue_participants  <- derived_participants[!(derived_participants %in% non_derived_participants)]
length(notissue_participants)
[1] 1

Looks like there is a cell line sample with no corresponding tumor.

seq_samples %>%
  filter(Kids_First_Participant_ID %in% notissue_participants)

Since this is only one sample it seems like the best move will be to remove all derived cell lines.

Unlabeled composition samples

9 samples are listed as “Not Reported” for composition. What are these, and do we have correponding samples for which composition is known?

notreported_participants <- seq_samples %>%
  filter(composition == "Not Reported") %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

seq_samples %>% 
  filter(Kids_First_Participant_ID %in% notreported_participants)

In one case (PT_N8W26H19), we do have Solid Tissue samples, but not for the others. Should these samples be included in further analysis?

WGS vs WXS

WXS_participants <- seq_samples %>%
  filter(experimental_strategy == "WXS") %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

WGS_participants <- seq_samples %>%
  filter(experimental_strategy == "WXS") %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

No_WGS <- WXS_participants[!(WXS_participants %in% WGS_participants)]

length(No_WGS)
[1] 0

There are no WXS samples for which we do not have WGS, so we should be able to simply remove WXS samples.

Just to check, is this still true when we restrict to early samples?

WXS_participants <- seq_samples %>%
  filter(experimental_strategy == "WXS", 
         Kids_First_Biospecimen_ID %in% early_samples) %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

WGS_participants <- seq_samples %>%
  filter(experimental_strategy == "WXS", 
         Kids_First_Biospecimen_ID %in% early_samples) %>%
  pull(Kids_First_Participant_ID) %>%
  unique() 

No_WGS <- WXS_participants[!(WXS_participants %in% WGS_participants)]

length(No_WGS)
[1] 0

Yes, good.

Summarize repeated samples

repeat_samples <- seq_samples %>% 
  dplyr::filter(Kids_First_Biospecimen_ID %in% early_samples, 
                experimental_strategy == "WGS") %>%
  dplyr::filter(composition != "Derived Cell Line") %>%
  dplyr::filter(Kids_First_Participant_ID %in% 
                  Kids_First_Participant_ID[duplicated(Kids_First_Participant_ID)])

repeat_samples %>% 
  group_by(Kids_First_Participant_ID) %>%
  summarise(samples = paste(Kids_First_Biospecimen_ID, collapse = ", "), 
         descriptors = paste(sort(unique(tumor_descriptor)),  collapse = ", "),
         age_diagnosis = paste(sort(unique(age_at_diagnosis_days)), collapse = ", "),
         experimental_stragegy = paste(sort(unique(experimental_strategy)), collapse = ", ")
  )

Select independent samples

Using the information described above, I created a function independent_samples() to select the earliest biospecimen from each participant, or in the case of more than one with the same date, randomly select among the available samples.

An example of this function is below, applied to the WGS Solid Tissue tumor samples. Note that in this case, only tumors designated as primary will be included, though this can be adjusted with tumor_types = "prefer_primary" or tumor_types = "any" if we want to be less presecriptive.

In the accompanying script, I generate both primary_only and primary_plus sets using the "primary" and "prefer_primary" resepectively.

independents <- sample_df %>%
  filter(experimental_strategy == "WGS",
         sample_type == "Tumor", 
         composition == "Solid Tissue"
         ) %>%
  independent_samples(tumor_types = "primary")

length(independents)
[1] 641

Just for reference, lets see what tumor types we end up with in this set:

sample_df %>% 
  filter(Kids_First_Biospecimen_ID %in% independents) %>%
  group_by(tumor_descriptor) %>%
  summarize(n = n())

Session info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] dplyr_0.8.3

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       rprojroot_1.3-2  crayon_1.3.4     assertthat_0.2.1 R6_2.4.0         backports_1.1.4 
 [7] magrittr_1.5     pillar_1.4.2     rlang_0.4.0      rstudioapi_0.10  tools_3.6.0      readr_1.3.1     
[13] glue_1.3.1       purrr_0.3.2      hms_0.4.2        xfun_0.8         compiler_3.6.0   pkgconfig_2.0.2 
[19] knitr_1.23       tidyselect_0.2.5 tibble_2.1.3    
LS0tCnRpdGxlOiAiUmVwZWF0ZWQgc2FtcGxlIGFuYWx5c2lzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KCiMjIFB1cnBvc2UKClRoZXJlIGFyZSBtYW55IHNwZWNpbWVucyBpbiB0aGUgZnVsbCBkYXRhc2V0IHRoYXQgYXJlIHJlcGVhdGVkIHNhbXBsZXMgZnJvbSB0aGUgc2FtZSBwYXJ0aWNpcGFudHMuClRoaXMgd29ya2Jvb2sgaXMgYSBxdWljayBzdW1tYXJ5IG9mIHRob3NlIHNhbXBsZXMsIHNvIHRoYXQgd2UgY2FuIGJldHRlciBzZWxlY3QgYSB1bmlxdWUgc2V0IG9mIHNwZWNpbWVucyBmb3IgZG93bnN0cmVhbSBhbmFseXNpcy4gClRoaXMgaXMgcGFydGljdWxhcmx5IGltcG9ydGFudCBmb3IgYW5hbHlzZXMgc3VjaCBhcyBtdXRhdGlvbiBjby1vY2N1cmVuY2UsIHdoZXJlIHJlcGVhdGVkIHNhbXBsaW5nIHdpbGwgaW5mbGF0ZSBhcHBhcmVudCBjby1vY3VycmVuY2UgbWVhc3VyZXMuCgoKYGBge3Igc2V0dXB9CiMgbG9hZCBsaWJyYXJpZXMKbGlicmFyeShkcGx5cikKCiMgc291cmNlIHNhbXBsZSBzZWxlY3Rpb24gZnVuY2l0b24Kc291cmNlKCJpbmRlcGVuZGVudC1zYW1wbGVzLlIiKQoKc2V0LnNlZWQoMjAxOTEwKQpgYGAKCgpgYGB7ciBsb2FkIGZpbGVzfQpyb290X2RpciA8LSBycHJvanJvb3Q6OmZpbmRfcm9vdChycHJvanJvb3Q6Omhhc19kaXIoIi5naXQiKSkKc2FtcGxlX2RmIDwtIHJlYWRyOjpyZWFkX3RzdihmaWxlLnBhdGgocm9vdF9kaXIsICJkYXRhIiwgInBidGEtaGlzdG9sb2dpZXMudHN2IikpCmBgYAoKIyMgV0dTIGFuZCBXWFMgc2FtcGxlcwpTb3J0IHRvIGp1c3QgV0dTIHNhbXBsZXMgb2YgdHVtb3JzCmBgYHtyfQpzZXFfc2FtcGxlcyA8LSBzYW1wbGVfZGYgJT4lCiAgZmlsdGVyKGV4cGVyaW1lbnRhbF9zdHJhdGVneSAlaW4lIGMoIldHUyIsICJXWFMiKSwgCiAgICAgICAgIHNhbXBsZV90eXBlID09ICJUdW1vciIpCmBgYAoKSG93IG1hbnkgV0dTLCBob3cgbWFueSBXWFMsIHN1bW1hcml6ZSAjcyBvZiBzYW1wbGVzIGFuZCBwYXJ0aWNpcGFudHMgYnkgCmBgYHtyfQpzZXFfc2FtcGxlcyAlPiUKICBncm91cF9ieShleHBlcmltZW50YWxfc3RyYXRlZ3ksIGNvbXBvc2l0aW9uLCB0dW1vcl9kZXNjcmlwdG9yKSAlPiUKICBzdW1tYXJpc2Uoc2FtcGxlcyA9IGxlbmd0aCh1bmlxdWUoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCkpLAogICAgICAgICAgICBwYXJ0aWNpcGFudHMgPSBsZW5ndGgodW5pcXVlKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpKSwgCiAgKSAKYGBgCgpXaGF0IGlmIHdlIHJlZHVjZSB0byB0aGUgZWFybGllc3Qgc2FtcGxlIGZvciBlYWNoIHBhcnRpY2lwYW50PwoKYGBge3J9CmVhcmx5X3NhbXBsZXMgPC0gc2VxX3NhbXBsZXMgJT4lCiAgZ3JvdXBfYnkoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lCiAgc3VtbWFyaXplKGFnZV9hdF9kaWFnbm9zaXNfZGF5cyA9IG1pbihhZ2VfYXRfZGlhZ25vc2lzX2RheXMpKSAlPiUKICBsZWZ0X2pvaW4oc2VxX3NhbXBsZXMpICU+JQogIHB1bGwoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCkgJT4lCiAgdW5pcXVlKCkKCnNlcV9zYW1wbGVzICU+JSAKICBmaWx0ZXIoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCAlaW4lIGVhcmx5X3NhbXBsZXMpICU+JQogIGdyb3VwX2J5KGV4cGVyaW1lbnRhbF9zdHJhdGVneSwgY29tcG9zaXRpb24sIHR1bW9yX2Rlc2NyaXB0b3IpICU+JQogIHN1bW1hcmlzZShzYW1wbGVzID0gbGVuZ3RoKHVuaXF1ZShLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEKSksCiAgICAgICAgICAgIHBhcnRpY2lwYW50cyA9IGxlbmd0aCh1bmlxdWUoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkpLCAKICApCmBgYAoKVGhlcmUgYXJlIHN0aWxsIG1hbnkgc2FtcGxlcyBmb3Igd2hpY2ggdGhlcmUgYXJlIG1vcmUgc2FtcGxlcyB0aGFuIHBhcnRpY2lwYW50cywgaW5kaWNhdGluZyBkdXBsaWNhdGVzLCBldmVuIGFtb25nIHRoZSBlYXJsaWVzdCBzYW1wbGVzLgoKCiMjIERlcml2ZWQgY2VsbCBsaW5lcwoKQXJlIHRoZXJlIGFueSBzYW1wbGVzIGZvciB3aGljaCB3ZSBvbmx5IGhhdmUgZGVyaXZlZCBjZWxsIGxpbmVzPwoKYGBge3J9CmRlcml2ZWRfcGFydGljaXBhbnRzIDwtIHNlcV9zYW1wbGVzICU+JQogIGZpbHRlcihjb21wb3NpdGlvbiA9PSAiRGVyaXZlZCBDZWxsIExpbmUiKSAlPiUKICBwdWxsKEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpICU+JQogIHVuaXF1ZSgpIAoKbm9uX2Rlcml2ZWRfcGFydGljaXBhbnRzIDwtIHNlcV9zYW1wbGVzICU+JSAKICBmaWx0ZXIoY29tcG9zaXRpb24gIT0gIkRlcml2ZWQgQ2VsbCBMaW5lIikgJT4lCiAgcHVsbChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUKICB1bmlxdWUoKSAKCm5vdGlzc3VlX3BhcnRpY2lwYW50cyAgPC0gZGVyaXZlZF9wYXJ0aWNpcGFudHNbIShkZXJpdmVkX3BhcnRpY2lwYW50cyAlaW4lIG5vbl9kZXJpdmVkX3BhcnRpY2lwYW50cyldCmxlbmd0aChub3Rpc3N1ZV9wYXJ0aWNpcGFudHMpCgpgYGAKCkxvb2tzIGxpa2UgdGhlcmUgaXMgYSBjZWxsIGxpbmUgc2FtcGxlIHdpdGggbm8gY29ycmVzcG9uZGluZyB0dW1vci4KCmBgYHtyfQpzZXFfc2FtcGxlcyAlPiUKICBmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCAlaW4lIG5vdGlzc3VlX3BhcnRpY2lwYW50cykKYGBgCgpTaW5jZSB0aGlzIGlzIG9ubHkgb25lIHNhbXBsZSBpdCBzZWVtcyBsaWtlIHRoZSBiZXN0IG1vdmUgd2lsbCBiZSB0byByZW1vdmUgYWxsIGRlcml2ZWQgY2VsbCBsaW5lcy4KCiMjIFVubGFiZWxlZCBjb21wb3NpdGlvbiBzYW1wbGVzCgo5IHNhbXBsZXMgYXJlIGxpc3RlZCBhcyAiTm90IFJlcG9ydGVkIiBmb3IgYGNvbXBvc2l0aW9uYC4gCldoYXQgYXJlIHRoZXNlLCBhbmQgZG8gd2UgaGF2ZSBjb3JyZXBvbmRpbmcgc2FtcGxlcyBmb3Igd2hpY2ggY29tcG9zaXRpb24gaXMga25vd24/CgpgYGB7cn0Kbm90cmVwb3J0ZWRfcGFydGljaXBhbnRzIDwtIHNlcV9zYW1wbGVzICU+JQogIGZpbHRlcihjb21wb3NpdGlvbiA9PSAiTm90IFJlcG9ydGVkIikgJT4lCiAgcHVsbChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUKICB1bmlxdWUoKSAKCnNlcV9zYW1wbGVzICU+JSAKICBmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCAlaW4lIG5vdHJlcG9ydGVkX3BhcnRpY2lwYW50cykKYGBgCgpJbiBvbmUgY2FzZSAoYFBUX044VzI2SDE5YCksIHdlIGRvIGhhdmUgU29saWQgVGlzc3VlIHNhbXBsZXMsIGJ1dCBub3QgZm9yIHRoZSBvdGhlcnMuClNob3VsZCB0aGVzZSBzYW1wbGVzIGJlIGluY2x1ZGVkIGluIGZ1cnRoZXIgYW5hbHlzaXM/CgojIyBXR1MgdnMgV1hTCmBgYHtyfQpXWFNfcGFydGljaXBhbnRzIDwtIHNlcV9zYW1wbGVzICU+JQogIGZpbHRlcihleHBlcmltZW50YWxfc3RyYXRlZ3kgPT0gIldYUyIpICU+JQogIHB1bGwoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lCiAgdW5pcXVlKCkgCgpXR1NfcGFydGljaXBhbnRzIDwtIHNlcV9zYW1wbGVzICU+JQogIGZpbHRlcihleHBlcmltZW50YWxfc3RyYXRlZ3kgPT0gIldYUyIpICU+JQogIHB1bGwoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lCiAgdW5pcXVlKCkgCgpOb19XR1MgPC0gV1hTX3BhcnRpY2lwYW50c1shKFdYU19wYXJ0aWNpcGFudHMgJWluJSBXR1NfcGFydGljaXBhbnRzKV0KCmxlbmd0aChOb19XR1MpCmBgYAoKVGhlcmUgYXJlIG5vIFdYUyBzYW1wbGVzIGZvciB3aGljaCB3ZSBkbyBub3QgaGF2ZSBXR1MsIHNvIHdlIHNob3VsZCBiZSBhYmxlIHRvIHNpbXBseSByZW1vdmUgV1hTIHNhbXBsZXMuCgpKdXN0IHRvIGNoZWNrLCBpcyB0aGlzIHN0aWxsIHRydWUgd2hlbiB3ZSByZXN0cmljdCB0byBlYXJseSBzYW1wbGVzPwpgYGB7cn0KV1hTX3BhcnRpY2lwYW50cyA8LSBzZXFfc2FtcGxlcyAlPiUKICBmaWx0ZXIoZXhwZXJpbWVudGFsX3N0cmF0ZWd5ID09ICJXWFMiLCAKICAgICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCAlaW4lIGVhcmx5X3NhbXBsZXMpICU+JQogIHB1bGwoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCkgJT4lCiAgdW5pcXVlKCkgCgpXR1NfcGFydGljaXBhbnRzIDwtIHNlcV9zYW1wbGVzICU+JQogIGZpbHRlcihleHBlcmltZW50YWxfc3RyYXRlZ3kgPT0gIldYUyIsIAogICAgICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEICVpbiUgZWFybHlfc2FtcGxlcykgJT4lCiAgcHVsbChLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEKSAlPiUKICB1bmlxdWUoKSAKCk5vX1dHUyA8LSBXWFNfcGFydGljaXBhbnRzWyEoV1hTX3BhcnRpY2lwYW50cyAlaW4lIFdHU19wYXJ0aWNpcGFudHMpXQoKbGVuZ3RoKE5vX1dHUykKYGBgCgpZZXMsIGdvb2QuCgoKCiMjIFN1bW1hcml6ZSByZXBlYXRlZCBzYW1wbGVzCgpgYGB7cn0KcmVwZWF0X3NhbXBsZXMgPC0gc2VxX3NhbXBsZXMgJT4lIAogIGRwbHlyOjpmaWx0ZXIoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCAlaW4lIGVhcmx5X3NhbXBsZXMsIAogICAgICAgICAgICAgICAgZXhwZXJpbWVudGFsX3N0cmF0ZWd5ID09ICJXR1MiKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGNvbXBvc2l0aW9uICE9ICJEZXJpdmVkIENlbGwgTGluZSIpICU+JQogIGRwbHlyOjpmaWx0ZXIoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCAlaW4lIAogICAgICAgICAgICAgICAgICBLaWRzX0ZpcnN0X1BhcnRpY2lwYW50X0lEW2R1cGxpY2F0ZWQoS2lkc19GaXJzdF9QYXJ0aWNpcGFudF9JRCldKQoKcmVwZWF0X3NhbXBsZXMgJT4lIAogIGdyb3VwX2J5KEtpZHNfRmlyc3RfUGFydGljaXBhbnRfSUQpICU+JQogIHN1bW1hcmlzZShzYW1wbGVzID0gcGFzdGUoS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCwgY29sbGFwc2UgPSAiLCAiKSwgCiAgICAgICAgIGRlc2NyaXB0b3JzID0gcGFzdGUoc29ydCh1bmlxdWUodHVtb3JfZGVzY3JpcHRvcikpLCAgY29sbGFwc2UgPSAiLCAiKSwKICAgICAgICAgYWdlX2RpYWdub3NpcyA9IHBhc3RlKHNvcnQodW5pcXVlKGFnZV9hdF9kaWFnbm9zaXNfZGF5cykpLCBjb2xsYXBzZSA9ICIsICIpLAogICAgICAgICBleHBlcmltZW50YWxfc3RyYWdlZ3kgPSBwYXN0ZShzb3J0KHVuaXF1ZShleHBlcmltZW50YWxfc3RyYXRlZ3kpKSwgY29sbGFwc2UgPSAiLCAiKQogICkKYGBgCgoKIyMgU2VsZWN0IGluZGVwZW5kZW50IHNhbXBsZXMKClVzaW5nIHRoZSBpbmZvcm1hdGlvbiBkZXNjcmliZWQgYWJvdmUsIEkgY3JlYXRlZCBhIGZ1bmN0aW9uIGBpbmRlcGVuZGVudF9zYW1wbGVzKClgCnRvIHNlbGVjdCB0aGUgZWFybGllc3QgYmlvc3BlY2ltZW4gZnJvbSBlYWNoIHBhcnRpY2lwYW50LCBvciBpbiB0aGUgY2FzZSBvZiBtb3JlIAp0aGFuIG9uZSB3aXRoIHRoZSBzYW1lIGRhdGUsIHJhbmRvbWx5IHNlbGVjdCBhbW9uZyB0aGUgYXZhaWxhYmxlIHNhbXBsZXMuCgpBbiBleGFtcGxlIG9mIHRoaXMgZnVuY3Rpb24gaXMgYmVsb3csIGFwcGxpZWQgdG8gdGhlIFdHUyBTb2xpZCBUaXNzdWUgdHVtb3Igc2FtcGxlcy4KTm90ZSB0aGF0IGluIHRoaXMgY2FzZSwgb25seSB0dW1vcnMgZGVzaWduYXRlZCBhcyBwcmltYXJ5IHdpbGwgYmUgaW5jbHVkZWQsIAp0aG91Z2ggdGhpcyBjYW4gYmUgYWRqdXN0ZWQgd2l0aCBgdHVtb3JfdHlwZXMgPSAicHJlZmVyX3ByaW1hcnkiYCBvciAKYHR1bW9yX3R5cGVzID0gImFueSJgIGlmIHdlIHdhbnQgdG8gYmUgbGVzcyBwcmVzZWNyaXB0aXZlLiAKCkluIHRoZSBhY2NvbXBhbnlpbmcgc2NyaXB0LCBJIGdlbmVyYXRlIGJvdGggYHByaW1hcnlfb25seWAgYW5kIGBwcmltYXJ5X3BsdXNgIApzZXRzIHVzaW5nIHRoZSBgInByaW1hcnkiYCBhbmQgYCJwcmVmZXJfcHJpbWFyeSJgIHJlc2VwZWN0aXZlbHkuCgoKCmBgYHtyfQppbmRlcGVuZGVudHMgPC0gc2FtcGxlX2RmICU+JQogIGZpbHRlcihleHBlcmltZW50YWxfc3RyYXRlZ3kgPT0gIldHUyIsCiAgICAgICAgIHNhbXBsZV90eXBlID09ICJUdW1vciIsIAogICAgICAgICBjb21wb3NpdGlvbiA9PSAiU29saWQgVGlzc3VlIgogICAgICAgICApICU+JQogIGluZGVwZW5kZW50X3NhbXBsZXModHVtb3JfdHlwZXMgPSAicHJpbWFyeSIpCgpsZW5ndGgoaW5kZXBlbmRlbnRzKQpgYGAKCkp1c3QgZm9yIHJlZmVyZW5jZSwgbGV0cyBzZWUgd2hhdCB0dW1vciB0eXBlcyB3ZSBlbmQgdXAgd2l0aCBpbiB0aGlzIHNldDoKCmBgYHtyfQpzYW1wbGVfZGYgJT4lIAogIGZpbHRlcihLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEICVpbiUgaW5kZXBlbmRlbnRzKSAlPiUKICBncm91cF9ieSh0dW1vcl9kZXNjcmlwdG9yKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkKYGBgCgoKCiMjIyBTZXNzaW9uIGluZm8KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAo=