Intro

This script streamlines and standardizes our handling of the steps taken to reach each dataset from an original sampleset. You will export some standardized csv and tsv tables for easy importing and manipulation with our other workflows.

Files Needed

To start, you should have four categories of csv files (I download the first three from working google spreadsheets, and the fourth category is a series of files automatically generated by each MinION sequencing run). Those files and their list of column headers to be used are as follows (note: it is fine to have extra columns, but you must at least have these to run the script as is):

  1. Libraries
    • SequenceID
    • Pipeline
    • LibraryTube
    • LibraryBarcode
    • ExtractID
    • Final_Library_Concentration
    • Volume.Added.to.Pool.(uL)
    • Seq_ID
    • Run_ID
    • LibraryTubeID
  2. Extracts
    • ExtractID
    • ExtractDate
    • ExtractedBy
    • ExtractType
    • ExtractKit
    • SampleID
    • ExtractConcentration
    • ExtractBox
    • ExtractNotes
  3. Samples
    • SampleID
    • SampleSubject
    • SampleDate
    • SampleCollectedBy
    • SampleNotes
  4. Barcode Alignments (1 file per Run_ID)
    • barcode
    • alias
    • type
    • target_unclassified
    • qcquisition_run_id
    • protocol_group_id
    • sample_id
    • flow_cell_id
    • started

Other Configuration Settings

Sampleset in Params

You can use the sampleset setting under params in the header of this script to select which sampleset you will be working with. So long as the same name is used consistently, this should automatically filter for that name (e.g., loris or marmoset).

Sequencing Run Lists

The code in the chunk above also generated a list of formatted codes for each available sequencing run to date, separated by taxa/samplesets (currently just for loris and marmoset). Make sure the end number matches the highest integer we have for that sampleset to date.

Script

Barcode Alignments

barcodes <- imap(seqruns, ~ {
  map(.x, ~ read_table(barcode_alignments[[.x]]) %>% mutate(seqrun = .x)) 
}) %>%
    bind_rows() %>%
                        as_tibble() %>%
                        filter(barcode     != "unclassified") %>%
                        mutate(SeqDateTime  = as_datetime(started)) %>%
                        mutate(SeqDate      = floor_date(SeqDateTime, unit = "day")) %>%
                        mutate(SeqRunID     = str_replace_all(sample_id, "pool1", "PL001")) %>%
                      mutate(LibraryCode    = str_squish(str_trim(seqrun      , "both")),
                             FlowCellSerial = str_squish(str_trim(flow_cell_id, "both"))
                             ) %>%
                      mutate(LibraryBarcode  = as.numeric(str_remove_all(barcode, "16S|barcode0|barcode"))) %>%
                        select(LibraryCode,
                               LibraryBarcode,
                               reads_unclassified = target_unclassified,
                               FlowCellSerial,
                               protocol_group_id,
                               flow_cell_id,
                               SeqRunID,
                               SeqDate,
                               SeqDateTime)

write.table(barcodes, barcode_alignments$compilations[[paste0(params$sampleset)]],
            row.names = F,
            sep = "\t")

Sequencing Runs

seqrun.tbl <- read_csv(path$inventories$seqruns) %>% 
  rename_with(~str_replace_all(., "\\s", "_")) %>%
  mutate(SampleSet       = case_when(
    str_detect(Pooled_Library_Code, "CM")  ~ "marmoset", 
    str_detect(Pooled_Library_Code, "PL")  ~ "loris",
    str_detect(Pooled_Library_Code, "NWR") ~ "bats", .default = "unknown"),
         LibraryCode     = str_to_lower(Pooled_Library_Code),
         LibPrepWorkflow = case_when(
           str_detect(Kit, "LSK") & Pipeline == "16S" ~ "lsk16s",
           Pipeline == "Host mtDNA"                   ~ "lskadaptive",
           str_detect(Kit, "SQK-16S") & Pipeline == "16S" ~ "rapid16s"),
         LibPrepDate     = mdy(Run_Date),
         SeqRunDate      = ymd(str_remove_all(str_trim(Run_ID, "both"), "MIN_16_|MIN_16-|MIN_MT_"))) %>%
  mutate(LibraryCode     = str_replace_all(LibraryCode, "pl00|pl0", "hdz"),
         strands         = 2,
         fragment_type   = if_else(Pipeline == "16S", 3, 1),
         Length          = if_else(Pipeline == "16S", 1500, 10000),
         InputMassStart  = if_else(Pipeline == "16S", 10, 1000),
         TemplateVolPrep = if_else(LibPrepWorkflow == "rapid16s", 15, 47),
         PoolSamples     = if_else(Pipeline == "16S", "yes", "no"),
         InputMassFinal  = 50
         ) %>%
  filter(SampleSet == params$sampleset) %>%
  select(
         SampleSet,
         LibraryCode,
         LibPrepDate,
         LibPrepWorkflow,
         LibPrepKit      = Kit,
         FlowCellSerial  = Flow_Cell_ID,
         FlowCellType    = Flow_Cell_Type,
         FlongleAdapter  = Flongle_Adapter,
         SeqDevice       = Sequencer,
         strands,
         fragment_type,
         Length,
         InputMassStart,
         TemplateVolPrep,
         PoolSamples,
         InputMassFinal)

Sample Records

samples     <- read_csv(path$inventories$collection) %>% 
  rename_with(~str_replace_all(., "\\s", "_")) %>%
                      filter(str_starts(SampleID, "\\w+")) %>% 
                      select(-SampleBox)  %>%
                      mutate(SampleID = str_squish(str_trim(SampleID, "both"))) %>% distinct() %>%
                      mutate(CollectionDate     = mdy(SampleDate),
                             Subject            = str_squish(str_trim(SampleSubject)),
                             .keep = "unused") %>% distinct() %>%
                      mutate(Subj_Certainty = if_else(Subject %in% subject_list, "yes", "no")) %>%
                      mutate(Subject        = str_remove_all(Subject, "\\?"))

DNA Extract Records

We will also join the previous sample records to this table at the end of the chunk.

extracts <- read_csv(path$inventories$extraction) %>% 
  rename_with(~str_replace_all(., "\\s", "_")) %>%
  filter(str_starts(SampleID, "\\w+")) %>%
  mutate(SampleID        = if_else(str_detect(SampleID, "#N/A") | is.na(SampleID), "ExtractControl", SampleID)) %>%
  mutate(SampleID = str_squish(str_trim(SampleID, "both")),
         ExtractID= str_squish(str_trim(ExtractID, "both")),
         ExtractDate       = mdy(ExtractDate)) %>%
  mutate(ExtractConc       = str_remove_all(ExtractConcentration, ">"), .keep = "unused") %>%
  mutate(ExtractConc = if_else(str_detect(ExtractConc, "Higher"), "100", ExtractConc),
         ExtractConc = if_else(str_detect(ExtractConc, "HIGHER"), "100", ExtractConc),
         ExtractConc = if_else(ExtractConc == "LOW", "0", ExtractConc),
         ExtractConc = if_else(ExtractConc == "", NA, ExtractConc)) %>%
  mutate(ExtractConc = round(as.numeric(ExtractConc), 1))  %>% filter(ExtractType == "DNA" | ExtractType == "dna") %>%
  select(-ExtractType) %>%
  right_join(samples) %>% distinct()

Libraries and Combining all Records

compilation <- read_csv(path$inventories$libraries, show_col_types = FALSE, col_types = list(LibraryBarcode = col_character()))  %>% 
  rename_with(~str_replace_all(., "\\s", "_")) %>%
  rename_with(~str_remove_all(., "\\(|\\)")) %>%
  filter(str_starts(SequenceID, "\\w+") & Seq_ID != "#N/A") %>%
  mutate(LibraryCode     = str_to_lower(Seq_ID)) %>%
  mutate(LibraryCode     = str_replace_all(LibraryCode, "pl00|pl0" , "hdz"),
         SampVolPool     = round(as.numeric(Volume_Added_to_Pool_uL), 0),
         LibraryBarcode  = as.numeric(str_remove_all(LibraryBarcode, "16S|barcode0|barcode"))) %>%
  mutate(TotalPoolVol    = sum(SampVolPool), .by = LibraryCode) %>%
  mutate(BeadVol         = TotalPoolVol * 0.6) %>%
  select(SequenceID,
         LibraryCode,
         LibraryTube,
         LibraryBarcode,
         ExtractID,
         SampVolPool,
         TotalPoolVol,
         BeadVol,
         Conc_QC2    = Final_Library_Concentration) %>%
  full_join(extracts, by = join_by(ExtractID)) %>% 
  mutate(SampleID = if_else(is.na(SampleID) & !is.na(ExtractID), "NTC", SampleID)) %>%
  full_join(barcodes, by = join_by(LibraryCode, LibraryBarcode)) %>%
  select(-FlowCellSerial) %>%
  left_join(seqrun.tbl, by = join_by(LibraryCode)) %>% distinct() %>%
  mutate(steps_remaining = case_when(
    is.na(ExtractID) ~ "sample not extracted",
    is.na(SequenceID) ~ "extract not sequenced",
    !is.na(ExtractID) & !is.na(SequenceID) & !is.na(SampleID) ~ "sample extracted and sequenced"
  )) %>%
  relocate(SampleID, ExtractID, SequenceID, steps_remaining) %>%
  arrange(CollectionDate, Subject)

Exporting a Spreadsheet with Records

We will use this spreadsheet for building the metadata table but also for calling up sample info in our protocol apps.

write.table(compilation,
            path$inventories$all_stages,
            row.names = F,
            sep = "\t")

Counting Replicates

count.extracts    <- extracts %>% select(ExtractID, SampleID) %>% distinct() %>% 
  group_by(SampleID)  %>% 
  mutate(n_dna_extracts = n_distinct(ExtractID)) %>% ungroup() %>% select(-ExtractID)

count.libraries <- compilation %>% select(SequenceID, ExtractID, SampleID) %>% distinct() %>% 
  group_by(ExtractID) %>% mutate(n_16s_extract = n_distinct(SequenceID)) %>% ungroup() %>%
  group_by(SampleID)  %>% mutate(n_16s_sample  = n_distinct(SequenceID)) %>% ungroup() %>% select(-SequenceID)

Exporting SampleSheets formatted for Dorado

samplesheet <- compilation %>%
  filter(steps_remaining == "sample extracted and sequenced") %>%
                      mutate(barcode = if_else(LibraryBarcode < 10, 
                                               as.character(str_glue("barcode0", "{LibraryBarcode}")),
                                               as.character(str_glue("barcode" , "{LibraryBarcode}")))) %>%
                      select(flow_cell_id  = FlowCellSerial,
                             experiment_id = protocol_group_id,
                             kit           = LibPrepKit,
                             barcode,
                             alias         = SequenceID,
                             seqrun        = LibraryCode)

write.table(samplesheet, 
          sample_sheets$compilations[[paste0(params$sampleset)]],
          row.names = F,
          quote     = F,
          sep       = ",")

Splitting Samplesheet to Individual Files for Each Run

samplesheet.nested <- samplesheet %>% nest(.by = seqrun) %>%
  deframe()

Next Step

Now you should proceed to the Read Processing workflow to begin basecalling the sequencing run.

LS0tCnRpdGxlOiAiU2FtcGxlIEludmVudG9yeSIKYXV0aG9yOiAiQWxpY2lhIFJpY2giCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZToKICAgICAgYnNsaWI6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGRmX3ByaW50OiBwYWdlZAogICAgY3NzOiBqb3VybmFsLmNzcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQpwYXJhbXM6CiAgc2FtcGxlc2V0OiAibG9yaXMiCiAgc2VxcnVuOiAiaGR6MTgiCiAgICAgICAgICAgICAgICAgICAgIAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlID0gRn0KCmxpYnJhcnkoYnNsaWIpCmxpYnJhcnkoY29uZmxpY3RlZCkKbGlicmFyeShodG1sdG9vbHMpCmxpYnJhcnkoaHRtbHdpZGdldHMpCmxpYnJhcnkodGlkeXZlcnNlKQoKc291cmNlKCJzZXR1cC9jb25mbGljdGVkLlIiKQpzb3VyY2UoInNldHVwL2tuaXRfZW5naW5lc19zaW1wbGUuUiIpCnNvdXJjZSgic2V0dXAvaW5wdXRzX3JlYWRfcHJvY2Vzc2luZy5SIikKCgprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgbWVzc2FnZSAgICAgICA9IEZBTFNFLAogIHdhcm5pbmcgICAgICAgPSBGQUxTRSwKICBlY2hvICAgICAgICAgID0gVFJVRSwKICBpbmNsdWRlICAgICAgID0gVFJVRSwKICBldmFsICAgICAgICAgID0gVFJVRSwKICBjb21tZW50ICAgICAgID0gIiIgICwKICBkZl9wcmludCAgICAgID0gImthYmxlIiwKICBza2ltcl9kaWdpdHMgID0gMiAgICAgICAgKQoKa25pdHI6Om9wdHNfa25pdCRzZXQoCiAgbWVzc2FnZSAgICAgICA9IEZBTFNFLAogIHdhcm5pbmcgICAgICAgPSBGQUxTRSwKICBlY2hvICAgICAgICAgID0gVFJVRSwKICBpbmNsdWRlICAgICAgID0gVFJVRSwKICBldmFsICAgICAgICAgID0gVFJVRSwKICBjb21tZW50ICAgICAgID0gIiIgICwKICBkZl9wcmludCAgICAgID0gImthYmxlIiwKICBza2ltcl9kaWdpdHMgID0gMiAgICAgICAgCiAgKQoKc2VxcnVucyA8LSBzZXFydW5zICU+JSBrZWVwX2F0KHBhcmFtcyRzYW1wbGVzZXQpICAlPiUgbGlzdF9mbGF0dGVuKG5hbWVfc3BlYyA9ICIiKQoKZ2xvYmFsICAgICAgICAgICAgPC0gY29uZmlnOjpnZXQoY29uZmlnID0gImRlZmF1bHQiKQpsb3JpcyAgICAgICAgICAgICA8LSBjb25maWc6OmdldChjb25maWcgPSAibG9yaXMiKQpiYXJjb2RlX2FsaWdubWVudHM8LSBjb25maWc6OmdldChjb25maWcgPSAiYmFyY29kZV9hbGlnbm1lbnRzIikKc2FtcGxlX3NoZWV0cyAgICAgPC0gY29uZmlnOjpnZXQoY29uZmlnID0gInNhbXBsZV9zaGVldHMiKQpwYXRoICAgICAgICAgICAgICA8LSBjb25maWc6OmdldChjb25maWcgPSBwYXN0ZTAocGFyYW1zJHNhbXBsZXNldCkpCgpzdWJqZWN0X2xpc3QgPC0ga2VlcF9hdChzdWJqZWN0cywgcGFzdGUwKHBhcmFtcyRzYW1wbGVzZXQpKSAlPiUgbGlzdF9mbGF0dGVuKG5hbWVfc3BlYyA9ICJ7aW5uZXJ9IikKCmBgYAoKCiMgSW50cm8KClRoaXMgc2NyaXB0IHN0cmVhbWxpbmVzIGFuZCBzdGFuZGFyZGl6ZXMgb3VyIGhhbmRsaW5nIG9mIHRoZSBzdGVwcyB0YWtlbiB0byByZWFjaCBlYWNoIGRhdGFzZXQgZnJvbSBhbiBvcmlnaW5hbCBzYW1wbGVzZXQuIFlvdSB3aWxsIGV4cG9ydCBzb21lIHN0YW5kYXJkaXplZCBjc3YgYW5kIHRzdiB0YWJsZXMgZm9yIGVhc3kgaW1wb3J0aW5nIGFuZCBtYW5pcHVsYXRpb24gd2l0aCBvdXIgb3RoZXIgd29ya2Zsb3dzLgoKIyMgRmlsZXMgTmVlZGVkCgpUbyBzdGFydCwgeW91IHNob3VsZCBoYXZlIGZvdXIgY2F0ZWdvcmllcyBvZiBjc3YgZmlsZXMgKEkgZG93bmxvYWQgdGhlIGZpcnN0IHRocmVlIGZyb20gd29ya2luZyBnb29nbGUgc3ByZWFkc2hlZXRzLCBhbmQgdGhlIGZvdXJ0aCBjYXRlZ29yeSBpcyBhIHNlcmllcyBvZiBmaWxlcyBhdXRvbWF0aWNhbGx5IGdlbmVyYXRlZCBieSBlYWNoIE1pbklPTiBzZXF1ZW5jaW5nIHJ1bikuIFRob3NlIGZpbGVzIGFuZCB0aGVpciBsaXN0IG9mIGNvbHVtbiBoZWFkZXJzIHRvIGJlIHVzZWQgYXJlIGFzIGZvbGxvd3MgKG5vdGU6IGl0IGlzIGZpbmUgdG8gaGF2ZSBleHRyYSBjb2x1bW5zLCBidXQgeW91IG11c3QgYXQgbGVhc3QgaGF2ZSB0aGVzZSB0byBydW4gdGhlIHNjcmlwdCBhcyBpcyk6ICAKCmBgYHtyLCBlY2hvID0gRkFMU0V9CnBhZ2VfZmx1aWQoCiAgICBhY2NvcmRpb24oCiAgICAgIG9wZW4gPSBGQUxTRSwKICAgICAgYWNjb3JkaW9uX3BhbmVsKAogICAgICAgICJTaG93L0hpZGUgRmlsZSBMaXN0IiwKICAgICAgICB0YWdMaXN0KHRhZ0xpc3QoCiAgICB3aXRoVGFncygKICAgICAgb2woCiAgICAgICAgbGkoIkxpYnJhcmllcyIpLAogICAgICAgICAgdWwoCiAgICAgICAgICAgIGxpKCJTZXF1ZW5jZUlEIiksCiAgICAgICAgICAgIGxpKCJQaXBlbGluZSIpLAogICAgICAgICAgICBsaSgiTGlicmFyeVR1YmUiKSwKICAgICAgICAgICAgbGkoIkxpYnJhcnlCYXJjb2RlIiksCiAgICAgICAgICAgIGxpKCJFeHRyYWN0SUQiKSwKICAgICAgICAgICAgbGkoIkZpbmFsX0xpYnJhcnlfQ29uY2VudHJhdGlvbiIpLAogICAgICAgICAgICBsaSgiVm9sdW1lLkFkZGVkLnRvLlBvb2wuKHVMKSIpLAogICAgICAgICAgICBsaSgiU2VxX0lEIiksCiAgICAgICAgICAgIGxpKCJSdW5fSUQiKSwKICAgICAgICAgICAgbGkoIkxpYnJhcnlUdWJlSUQiKQogICAgICAgICAgKSwKICAgICAgICBsaSgiRXh0cmFjdHMiKSwKICAgICAgICAgIHVsKAogICAgICAgICAgICBsaSgiRXh0cmFjdElEIiksCiAgICAgICAgICAgIGxpKCJFeHRyYWN0RGF0ZSIpLAogICAgICAgICAgICBsaSgiRXh0cmFjdGVkQnkiKSwKICAgICAgICAgICAgbGkoIkV4dHJhY3RUeXBlIiksCiAgICAgICAgICAgIGxpKCJFeHRyYWN0S2l0IiksCiAgICAgICAgICAgIGxpKCJTYW1wbGVJRCIpLAogICAgICAgICAgICBsaSgiRXh0cmFjdENvbmNlbnRyYXRpb24iKSwKICAgICAgICAgICAgbGkoIkV4dHJhY3RCb3giKSwKICAgICAgICAgICAgbGkoIkV4dHJhY3ROb3RlcyIpCiAgICAgICAgICAgICksCiAgICAgICAgbGkoIlNhbXBsZXMiKSwKICAgICAgICAgIHVsKCAgCiAgICAgICAgICAgIGxpKCJTYW1wbGVJRCIpLAogICAgICAgICAgICBsaSgiU2FtcGxlU3ViamVjdCIpLAogICAgICAgICAgICBsaSgiU2FtcGxlRGF0ZSIpLAogICAgICAgICAgICBsaSgiU2FtcGxlQ29sbGVjdGVkQnkiKSwKICAgICAgICAgICAgbGkoIlNhbXBsZU5vdGVzIikKICAgICAgICAgICAgKSwKICAgICAgICBsaSgiQmFyY29kZSBBbGlnbm1lbnRzICgxIGZpbGUgcGVyIFJ1bl9JRCkiKSwKICAgICAgICAgIHVsKAogICAgICAgICAgICBsaSgiYmFyY29kZSIpLAogICAgICAgICAgICBsaSgiYWxpYXMiKSwKICAgICAgICAgICAgbGkoInR5cGUiKSwKICAgICAgICAgICAgbGkoInRhcmdldF91bmNsYXNzaWZpZWQiKSwKICAgICAgICAgICAgbGkoInFjcXVpc2l0aW9uX3J1bl9pZCIpLAogICAgICAgICAgICBsaSgicHJvdG9jb2xfZ3JvdXBfaWQiKSwKICAgICAgICAgICAgbGkoInNhbXBsZV9pZCIpLAogICAgICAgICAgICBsaSgiZmxvd19jZWxsX2lkIiksCiAgICAgICAgICAgIGxpKCJzdGFydGVkIikKICAgICAgICAgICAgKQogICAgICAgICkKICAgICAgKQogICAgKQogICAgKQogICAgKQogICAgKQogICAgKQoKYGBgCgoKIyMgT3RoZXIgQ29uZmlndXJhdGlvbiBTZXR0aW5ncwoKIyMjIFNhbXBsZXNldCBpbiBQYXJhbXMKCllvdSBjYW4gdXNlIHRoZSBzYW1wbGVzZXQgc2V0dGluZyB1bmRlciBwYXJhbXMgaW4gdGhlIGhlYWRlciBvZiB0aGlzIHNjcmlwdCB0byBzZWxlY3Qgd2hpY2ggc2FtcGxlc2V0IHlvdSB3aWxsIGJlIHdvcmtpbmcgd2l0aC4gU28gbG9uZyBhcyB0aGUgc2FtZSBuYW1lIGlzIHVzZWQgY29uc2lzdGVudGx5LCB0aGlzIHNob3VsZCBhdXRvbWF0aWNhbGx5IGZpbHRlciBmb3IgdGhhdCBuYW1lIChlLmcuLCBsb3JpcyBvciBtYXJtb3NldCkuIAoKIyMjIFNlcXVlbmNpbmcgUnVuIExpc3RzCgpUaGUgY29kZSBpbiB0aGUgY2h1bmsgYWJvdmUgYWxzbyBnZW5lcmF0ZWQgYSBsaXN0IG9mIGZvcm1hdHRlZCBjb2RlcyBmb3IgZWFjaCBhdmFpbGFibGUgc2VxdWVuY2luZyBydW4gdG8gZGF0ZSwgc2VwYXJhdGVkIGJ5IHRheGEvc2FtcGxlc2V0cyAoY3VycmVudGx5IGp1c3QgZm9yIGxvcmlzIGFuZCBtYXJtb3NldCkuIE1ha2Ugc3VyZSB0aGUgZW5kIG51bWJlciBtYXRjaGVzIHRoZSBoaWdoZXN0IGludGVnZXIgd2UgaGF2ZSBmb3IgdGhhdCBzYW1wbGVzZXQgdG8gZGF0ZS4KCiMgU2NyaXB0CgojIyBCYXJjb2RlIEFsaWdubWVudHMKCmBgYHtyfQpiYXJjb2RlcyA8LSBpbWFwKHNlcXJ1bnMsIH4gewogIG1hcCgueCwgfiByZWFkX3RhYmxlKGJhcmNvZGVfYWxpZ25tZW50c1tbLnhdXSkgJT4lIG11dGF0ZShzZXFydW4gPSAueCkpIAp9KSAlPiUKICAgIGJpbmRfcm93cygpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBhc190aWJibGUoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKGJhcmNvZGUgICAgICE9ICJ1bmNsYXNzaWZpZWQiKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKFNlcURhdGVUaW1lICA9IGFzX2RhdGV0aW1lKHN0YXJ0ZWQpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKFNlcURhdGUgICAgICA9IGZsb29yX2RhdGUoU2VxRGF0ZVRpbWUsIHVuaXQgPSAiZGF5IikpICU+JQogICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoU2VxUnVuSUQgICAgID0gc3RyX3JlcGxhY2VfYWxsKHNhbXBsZV9pZCwgInBvb2wxIiwgIlBMMDAxIikpICU+JQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKExpYnJhcnlDb2RlICAgID0gc3RyX3NxdWlzaChzdHJfdHJpbShzZXFydW4gICAgICAsICJib3RoIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZsb3dDZWxsU2VyaWFsID0gc3RyX3NxdWlzaChzdHJfdHJpbShmbG93X2NlbGxfaWQsICJib3RoIikpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShMaWJyYXJ5QmFyY29kZSAgPSBhcy5udW1lcmljKHN0cl9yZW1vdmVfYWxsKGJhcmNvZGUsICIxNlN8YmFyY29kZTB8YmFyY29kZSIpKSkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChMaWJyYXJ5Q29kZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExpYnJhcnlCYXJjb2RlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVhZHNfdW5jbGFzc2lmaWVkID0gdGFyZ2V0X3VuY2xhc3NpZmllZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZsb3dDZWxsU2VyaWFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvdG9jb2xfZ3JvdXBfaWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmbG93X2NlbGxfaWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTZXFSdW5JRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNlcURhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTZXFEYXRlVGltZSkKCndyaXRlLnRhYmxlKGJhcmNvZGVzLCBiYXJjb2RlX2FsaWdubWVudHMkY29tcGlsYXRpb25zW1twYXN0ZTAocGFyYW1zJHNhbXBsZXNldCldXSwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRiwKICAgICAgICAgICAgc2VwID0gIlx0IikKYGBgCgojIyBTZXF1ZW5jaW5nIFJ1bnMKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzZXFydW4udGJsIDwtIHJlYWRfY3N2KHBhdGgkaW52ZW50b3JpZXMkc2VxcnVucykgJT4lIAogIHJlbmFtZV93aXRoKH5zdHJfcmVwbGFjZV9hbGwoLiwgIlxccyIsICJfIikpICU+JQogIG11dGF0ZShTYW1wbGVTZXQgICAgICAgPSBjYXNlX3doZW4oCiAgICBzdHJfZGV0ZWN0KFBvb2xlZF9MaWJyYXJ5X0NvZGUsICJDTSIpICB+ICJtYXJtb3NldCIsIAogICAgc3RyX2RldGVjdChQb29sZWRfTGlicmFyeV9Db2RlLCAiUEwiKSAgfiAibG9yaXMiLAogICAgc3RyX2RldGVjdChQb29sZWRfTGlicmFyeV9Db2RlLCAiTldSIikgfiAiYmF0cyIsIC5kZWZhdWx0ID0gInVua25vd24iKSwKICAgICAgICAgTGlicmFyeUNvZGUgICAgID0gc3RyX3RvX2xvd2VyKFBvb2xlZF9MaWJyYXJ5X0NvZGUpLAogICAgICAgICBMaWJQcmVwV29ya2Zsb3cgPSBjYXNlX3doZW4oCiAgICAgICAgICAgc3RyX2RldGVjdChLaXQsICJMU0siKSAmIFBpcGVsaW5lID09ICIxNlMiIH4gImxzazE2cyIsCiAgICAgICAgICAgUGlwZWxpbmUgPT0gIkhvc3QgbXRETkEiICAgICAgICAgICAgICAgICAgIH4gImxza2FkYXB0aXZlIiwKICAgICAgICAgICBzdHJfZGV0ZWN0KEtpdCwgIlNRSy0xNlMiKSAmIFBpcGVsaW5lID09ICIxNlMiIH4gInJhcGlkMTZzIiksCiAgICAgICAgIExpYlByZXBEYXRlICAgICA9IG1keShSdW5fRGF0ZSksCiAgICAgICAgIFNlcVJ1bkRhdGUgICAgICA9IHltZChzdHJfcmVtb3ZlX2FsbChzdHJfdHJpbShSdW5fSUQsICJib3RoIiksICJNSU5fMTZffE1JTl8xNi18TUlOX01UXyIpKSkgJT4lCiAgbXV0YXRlKExpYnJhcnlDb2RlICAgICA9IHN0cl9yZXBsYWNlX2FsbChMaWJyYXJ5Q29kZSwgInBsMDB8cGwwIiwgImhkeiIpLAogICAgICAgICBzdHJhbmRzICAgICAgICAgPSAyLAogICAgICAgICBmcmFnbWVudF90eXBlICAgPSBpZl9lbHNlKFBpcGVsaW5lID09ICIxNlMiLCAzLCAxKSwKICAgICAgICAgTGVuZ3RoICAgICAgICAgID0gaWZfZWxzZShQaXBlbGluZSA9PSAiMTZTIiwgMTUwMCwgMTAwMDApLAogICAgICAgICBJbnB1dE1hc3NTdGFydCAgPSBpZl9lbHNlKFBpcGVsaW5lID09ICIxNlMiLCAxMCwgMTAwMCksCiAgICAgICAgIFRlbXBsYXRlVm9sUHJlcCA9IGlmX2Vsc2UoTGliUHJlcFdvcmtmbG93ID09ICJyYXBpZDE2cyIsIDE1LCA0NyksCiAgICAgICAgIFBvb2xTYW1wbGVzICAgICA9IGlmX2Vsc2UoUGlwZWxpbmUgPT0gIjE2UyIsICJ5ZXMiLCAibm8iKSwKICAgICAgICAgSW5wdXRNYXNzRmluYWwgID0gNTAKICAgICAgICAgKSAlPiUKICBmaWx0ZXIoU2FtcGxlU2V0ID09IHBhcmFtcyRzYW1wbGVzZXQpICU+JQogIHNlbGVjdCgKICAgICAgICAgU2FtcGxlU2V0LAogICAgICAgICBMaWJyYXJ5Q29kZSwKICAgICAgICAgTGliUHJlcERhdGUsCiAgICAgICAgIExpYlByZXBXb3JrZmxvdywKICAgICAgICAgTGliUHJlcEtpdCAgICAgID0gS2l0LAogICAgICAgICBGbG93Q2VsbFNlcmlhbCAgPSBGbG93X0NlbGxfSUQsCiAgICAgICAgIEZsb3dDZWxsVHlwZSAgICA9IEZsb3dfQ2VsbF9UeXBlLAogICAgICAgICBGbG9uZ2xlQWRhcHRlciAgPSBGbG9uZ2xlX0FkYXB0ZXIsCiAgICAgICAgIFNlcURldmljZSAgICAgICA9IFNlcXVlbmNlciwKICAgICAgICAgc3RyYW5kcywKICAgICAgICAgZnJhZ21lbnRfdHlwZSwKICAgICAgICAgTGVuZ3RoLAogICAgICAgICBJbnB1dE1hc3NTdGFydCwKICAgICAgICAgVGVtcGxhdGVWb2xQcmVwLAogICAgICAgICBQb29sU2FtcGxlcywKICAgICAgICAgSW5wdXRNYXNzRmluYWwpCmBgYAoKCiMjIFNhbXBsZSBSZWNvcmRzCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFfQpzYW1wbGVzICAgICA8LSByZWFkX2NzdihwYXRoJGludmVudG9yaWVzJGNvbGxlY3Rpb24pICU+JSAKICByZW5hbWVfd2l0aCh+c3RyX3JlcGxhY2VfYWxsKC4sICJcXHMiLCAiXyIpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIGZpbHRlcihzdHJfc3RhcnRzKFNhbXBsZUlELCAiXFx3KyIpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICBzZWxlY3QoLVNhbXBsZUJveCkgICU+JQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKFNhbXBsZUlEID0gc3RyX3NxdWlzaChzdHJfdHJpbShTYW1wbGVJRCwgImJvdGgiKSkpICU+JSBkaXN0aW5jdCgpICU+JQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKENvbGxlY3Rpb25EYXRlICAgICA9IG1keShTYW1wbGVEYXRlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdWJqZWN0ICAgICAgICAgICAgPSBzdHJfc3F1aXNoKHN0cl90cmltKFNhbXBsZVN1YmplY3QpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAua2VlcCA9ICJ1bnVzZWQiKSAlPiUgZGlzdGluY3QoKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShTdWJqX0NlcnRhaW50eSA9IGlmX2Vsc2UoU3ViamVjdCAlaW4lIHN1YmplY3RfbGlzdCwgInllcyIsICJubyIpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZShTdWJqZWN0ICAgICAgICA9IHN0cl9yZW1vdmVfYWxsKFN1YmplY3QsICJcXD8iKSkKYGBgCgojIyBETkEgRXh0cmFjdCBSZWNvcmRzCgpXZSB3aWxsIGFsc28gam9pbiB0aGUgcHJldmlvdXMgc2FtcGxlIHJlY29yZHMgdG8gdGhpcyB0YWJsZSBhdCB0aGUgZW5kIG9mIHRoZSBjaHVuay4KCmBgYHtyfQpleHRyYWN0cyA8LSByZWFkX2NzdihwYXRoJGludmVudG9yaWVzJGV4dHJhY3Rpb24pICU+JSAKICByZW5hbWVfd2l0aCh+c3RyX3JlcGxhY2VfYWxsKC4sICJcXHMiLCAiXyIpKSAlPiUKICBmaWx0ZXIoc3RyX3N0YXJ0cyhTYW1wbGVJRCwgIlxcdysiKSkgJT4lCiAgbXV0YXRlKFNhbXBsZUlEICAgICAgICA9IGlmX2Vsc2Uoc3RyX2RldGVjdChTYW1wbGVJRCwgIiNOL0EiKSB8IGlzLm5hKFNhbXBsZUlEKSwgIkV4dHJhY3RDb250cm9sIiwgU2FtcGxlSUQpKSAlPiUKICBtdXRhdGUoU2FtcGxlSUQgPSBzdHJfc3F1aXNoKHN0cl90cmltKFNhbXBsZUlELCAiYm90aCIpKSwKICAgICAgICAgRXh0cmFjdElEPSBzdHJfc3F1aXNoKHN0cl90cmltKEV4dHJhY3RJRCwgImJvdGgiKSksCiAgICAgICAgIEV4dHJhY3REYXRlICAgICAgID0gbWR5KEV4dHJhY3REYXRlKSkgJT4lCiAgbXV0YXRlKEV4dHJhY3RDb25jICAgICAgID0gc3RyX3JlbW92ZV9hbGwoRXh0cmFjdENvbmNlbnRyYXRpb24sICI+IiksIC5rZWVwID0gInVudXNlZCIpICU+JQogIG11dGF0ZShFeHRyYWN0Q29uYyA9IGlmX2Vsc2Uoc3RyX2RldGVjdChFeHRyYWN0Q29uYywgIkhpZ2hlciIpLCAiMTAwIiwgRXh0cmFjdENvbmMpLAogICAgICAgICBFeHRyYWN0Q29uYyA9IGlmX2Vsc2Uoc3RyX2RldGVjdChFeHRyYWN0Q29uYywgIkhJR0hFUiIpLCAiMTAwIiwgRXh0cmFjdENvbmMpLAogICAgICAgICBFeHRyYWN0Q29uYyA9IGlmX2Vsc2UoRXh0cmFjdENvbmMgPT0gIkxPVyIsICIwIiwgRXh0cmFjdENvbmMpLAogICAgICAgICBFeHRyYWN0Q29uYyA9IGlmX2Vsc2UoRXh0cmFjdENvbmMgPT0gIiIsIE5BLCBFeHRyYWN0Q29uYykpICU+JQogIG11dGF0ZShFeHRyYWN0Q29uYyA9IHJvdW5kKGFzLm51bWVyaWMoRXh0cmFjdENvbmMpLCAxKSkgICU+JSBmaWx0ZXIoRXh0cmFjdFR5cGUgPT0gIkROQSIgfCBFeHRyYWN0VHlwZSA9PSAiZG5hIikgJT4lCiAgc2VsZWN0KC1FeHRyYWN0VHlwZSkgJT4lCiAgcmlnaHRfam9pbihzYW1wbGVzKSAlPiUgZGlzdGluY3QoKQpgYGAKCiMjIExpYnJhcmllcyBhbmQgQ29tYmluaW5nIGFsbCBSZWNvcmRzCgoKYGBge3J9CmNvbXBpbGF0aW9uIDwtIHJlYWRfY3N2KHBhdGgkaW52ZW50b3JpZXMkbGlicmFyaWVzLCBzaG93X2NvbF90eXBlcyA9IEZBTFNFLCBjb2xfdHlwZXMgPSBsaXN0KExpYnJhcnlCYXJjb2RlID0gY29sX2NoYXJhY3RlcigpKSkgICU+JSAKICByZW5hbWVfd2l0aCh+c3RyX3JlcGxhY2VfYWxsKC4sICJcXHMiLCAiXyIpKSAlPiUKICByZW5hbWVfd2l0aCh+c3RyX3JlbW92ZV9hbGwoLiwgIlxcKHxcXCkiKSkgJT4lCiAgZmlsdGVyKHN0cl9zdGFydHMoU2VxdWVuY2VJRCwgIlxcdysiKSAmIFNlcV9JRCAhPSAiI04vQSIpICU+JQogIG11dGF0ZShMaWJyYXJ5Q29kZSAgICAgPSBzdHJfdG9fbG93ZXIoU2VxX0lEKSkgJT4lCiAgbXV0YXRlKExpYnJhcnlDb2RlICAgICA9IHN0cl9yZXBsYWNlX2FsbChMaWJyYXJ5Q29kZSwgInBsMDB8cGwwIiAsICJoZHoiKSwKICAgICAgICAgU2FtcFZvbFBvb2wgICAgID0gcm91bmQoYXMubnVtZXJpYyhWb2x1bWVfQWRkZWRfdG9fUG9vbF91TCksIDApLAogICAgICAgICBMaWJyYXJ5QmFyY29kZSAgPSBhcy5udW1lcmljKHN0cl9yZW1vdmVfYWxsKExpYnJhcnlCYXJjb2RlLCAiMTZTfGJhcmNvZGUwfGJhcmNvZGUiKSkpICU+JQogIG11dGF0ZShUb3RhbFBvb2xWb2wgICAgPSBzdW0oU2FtcFZvbFBvb2wpLCAuYnkgPSBMaWJyYXJ5Q29kZSkgJT4lCiAgbXV0YXRlKEJlYWRWb2wgICAgICAgICA9IFRvdGFsUG9vbFZvbCAqIDAuNikgJT4lCiAgc2VsZWN0KFNlcXVlbmNlSUQsCiAgICAgICAgIExpYnJhcnlDb2RlLAogICAgICAgICBMaWJyYXJ5VHViZSwKICAgICAgICAgTGlicmFyeUJhcmNvZGUsCiAgICAgICAgIEV4dHJhY3RJRCwKICAgICAgICAgU2FtcFZvbFBvb2wsCiAgICAgICAgIFRvdGFsUG9vbFZvbCwKICAgICAgICAgQmVhZFZvbCwKICAgICAgICAgQ29uY19RQzIgICAgPSBGaW5hbF9MaWJyYXJ5X0NvbmNlbnRyYXRpb24pICU+JQogIGZ1bGxfam9pbihleHRyYWN0cywgYnkgPSBqb2luX2J5KEV4dHJhY3RJRCkpICU+JSAKICBtdXRhdGUoU2FtcGxlSUQgPSBpZl9lbHNlKGlzLm5hKFNhbXBsZUlEKSAmICFpcy5uYShFeHRyYWN0SUQpLCAiTlRDIiwgU2FtcGxlSUQpKSAlPiUKICBmdWxsX2pvaW4oYmFyY29kZXMsIGJ5ID0gam9pbl9ieShMaWJyYXJ5Q29kZSwgTGlicmFyeUJhcmNvZGUpKSAlPiUKICBzZWxlY3QoLUZsb3dDZWxsU2VyaWFsKSAlPiUKICBsZWZ0X2pvaW4oc2VxcnVuLnRibCwgYnkgPSBqb2luX2J5KExpYnJhcnlDb2RlKSkgJT4lIGRpc3RpbmN0KCkgJT4lCiAgbXV0YXRlKHN0ZXBzX3JlbWFpbmluZyA9IGNhc2Vfd2hlbigKICAgIGlzLm5hKEV4dHJhY3RJRCkgfiAic2FtcGxlIG5vdCBleHRyYWN0ZWQiLAogICAgaXMubmEoU2VxdWVuY2VJRCkgfiAiZXh0cmFjdCBub3Qgc2VxdWVuY2VkIiwKICAgICFpcy5uYShFeHRyYWN0SUQpICYgIWlzLm5hKFNlcXVlbmNlSUQpICYgIWlzLm5hKFNhbXBsZUlEKSB+ICJzYW1wbGUgZXh0cmFjdGVkIGFuZCBzZXF1ZW5jZWQiCiAgKSkgJT4lCiAgcmVsb2NhdGUoU2FtcGxlSUQsIEV4dHJhY3RJRCwgU2VxdWVuY2VJRCwgc3RlcHNfcmVtYWluaW5nKSAlPiUKICBhcnJhbmdlKENvbGxlY3Rpb25EYXRlLCBTdWJqZWN0KQpgYGAKCiMjIyBFeHBvcnRpbmcgYSBTcHJlYWRzaGVldCB3aXRoIFJlY29yZHMKCldlIHdpbGwgdXNlIHRoaXMgc3ByZWFkc2hlZXQgZm9yIGJ1aWxkaW5nIHRoZSBtZXRhZGF0YSB0YWJsZSBidXQgYWxzbyBmb3IgY2FsbGluZyB1cCBzYW1wbGUgaW5mbyBpbiBvdXIgcHJvdG9jb2wgYXBwcy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQp3cml0ZS50YWJsZShjb21waWxhdGlvbiwKICAgICAgICAgICAgcGF0aCRpbnZlbnRvcmllcyRhbGxfc3RhZ2VzLAogICAgICAgICAgICByb3cubmFtZXMgPSBGLAogICAgICAgICAgICBzZXAgPSAiXHQiKQpgYGAKCgojIyBDb3VudGluZyBSZXBsaWNhdGVzCgpgYGB7cn0KY291bnQuZXh0cmFjdHMgICAgPC0gZXh0cmFjdHMgJT4lIHNlbGVjdChFeHRyYWN0SUQsIFNhbXBsZUlEKSAlPiUgZGlzdGluY3QoKSAlPiUgCiAgZ3JvdXBfYnkoU2FtcGxlSUQpICAlPiUgCiAgbXV0YXRlKG5fZG5hX2V4dHJhY3RzID0gbl9kaXN0aW5jdChFeHRyYWN0SUQpKSAlPiUgdW5ncm91cCgpICU+JSBzZWxlY3QoLUV4dHJhY3RJRCkKCmNvdW50LmxpYnJhcmllcyA8LSBjb21waWxhdGlvbiAlPiUgc2VsZWN0KFNlcXVlbmNlSUQsIEV4dHJhY3RJRCwgU2FtcGxlSUQpICU+JSBkaXN0aW5jdCgpICU+JSAKICBncm91cF9ieShFeHRyYWN0SUQpICU+JSBtdXRhdGUobl8xNnNfZXh0cmFjdCA9IG5fZGlzdGluY3QoU2VxdWVuY2VJRCkpICU+JSB1bmdyb3VwKCkgJT4lCiAgZ3JvdXBfYnkoU2FtcGxlSUQpICAlPiUgbXV0YXRlKG5fMTZzX3NhbXBsZSAgPSBuX2Rpc3RpbmN0KFNlcXVlbmNlSUQpKSAlPiUgdW5ncm91cCgpICU+JSBzZWxlY3QoLVNlcXVlbmNlSUQpCmBgYAoKIyMgRXhwb3J0aW5nIFNhbXBsZVNoZWV0cyBmb3JtYXR0ZWQgZm9yIERvcmFkbwoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNhbXBsZXNoZWV0IDwtIGNvbXBpbGF0aW9uICU+JQogIGZpbHRlcihzdGVwc19yZW1haW5pbmcgPT0gInNhbXBsZSBleHRyYWN0ZWQgYW5kIHNlcXVlbmNlZCIpICU+JQogICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKGJhcmNvZGUgPSBpZl9lbHNlKExpYnJhcnlCYXJjb2RlIDwgMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihzdHJfZ2x1ZSgiYmFyY29kZTAiLCAie0xpYnJhcnlCYXJjb2RlfSIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoc3RyX2dsdWUoImJhcmNvZGUiICwgIntMaWJyYXJ5QmFyY29kZX0iKSkpKSAlPiUKICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChmbG93X2NlbGxfaWQgID0gRmxvd0NlbGxTZXJpYWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXhwZXJpbWVudF9pZCA9IHByb3RvY29sX2dyb3VwX2lkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtpdCAgICAgICAgICAgPSBMaWJQcmVwS2l0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhcmNvZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpYXMgICAgICAgICA9IFNlcXVlbmNlSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VxcnVuICAgICAgICA9IExpYnJhcnlDb2RlKQoKd3JpdGUudGFibGUoc2FtcGxlc2hlZXQsIAogICAgICAgICAgc2FtcGxlX3NoZWV0cyRjb21waWxhdGlvbnNbW3Bhc3RlMChwYXJhbXMkc2FtcGxlc2V0KV1dLAogICAgICAgICAgcm93Lm5hbWVzID0gRiwKICAgICAgICAgIHF1b3RlICAgICA9IEYsCiAgICAgICAgICBzZXAgICAgICAgPSAiLCIpCmBgYAoKIyMjIFNwbGl0dGluZyBTYW1wbGVzaGVldCB0byBJbmRpdmlkdWFsIEZpbGVzIGZvciBFYWNoIFJ1bgoKCmBgYHtyfQpzYW1wbGVzaGVldC5uZXN0ZWQgPC0gc2FtcGxlc2hlZXQgJT4lIG5lc3QoLmJ5ID0gc2VxcnVuKSAlPiUKICBkZWZyYW1lKCkKYGBgCgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KaW1hcChzYW1wbGVzaGVldC5uZXN0ZWQsIH4gewogIHdyaXRlLnRhYmxlKC54LCAoc2FtcGxlX3NoZWV0c1tbLnldXSksCiAgICAgICAgICByb3cubmFtZXMgPSBGLAogICAgICAgICAgcXVvdGUgICAgID0gRiwKICAgICAgICAgIHNlcCAgICAgICA9ICIsIikKfSkKYGBgCgoKCiMgTmV4dCBTdGVwCgo+Tm93IHlvdSBzaG91bGQgcHJvY2VlZCB0byB0aGUgUmVhZCBQcm9jZXNzaW5nIHdvcmtmbG93IHRvIGJlZ2luIGJhc2VjYWxsaW5nIHRoZSBzZXF1ZW5jaW5nIHJ1bi4K