Outline

Load packages

library(tidyverse)
Loading tidyverse: ggplot2
Loading tidyverse: tibble
Loading tidyverse: tidyr
Loading tidyverse: readr
Loading tidyverse: purrr
Loading tidyverse: dplyr
Conflicts with tidy packages -----------------------------------------------------------------------------------
filter(): dplyr, stats
lag():    dplyr, stats
library(stringr)

Tidy data

Is this table tidy?

table2

Is this table tidy?

table3

Is this table tidy?

table1

Is this table tidy?

gene_expression

Is this table tidy?

facs_data

what are tidy data?

Jeff Leek in his book The Elements of Data Analytic Style summarizes the characteristics of tidy data as the points:

gather()

used when column names are not names of variables, but values of a variable (e.g. time). makes tables longer and skinny (previously known as melting)

gene_expression

gather()

used when column names are not names of variables, but values of a variable (e.g. time). makes tables longer and skinny (previously known as melting)

gather(gene_expression, t0:t2, key = "timepoint", value = "expression")

spread()

Spreading is the opposite of gathering. Used when an observation is scattered across multiple rows. spread() makes tables shorter and wider

facs_data

spread()

Spreading is the opposite of gathering. Used when an observation is scattered across multiple rows. spread() makes tables shorter and wider

spread(facs_data, key = Measure, value = Value)

Exercise 1

put table2 in tidy format

table2[1:6,]

Exercise 2

convert table1 to table2

table1

data import

readr() has numerous functions for reading in files as tibbles

gff <- read_delim("Saccharomyces_cerevisiae.R64-1-1.34.gff3", 
    "\t", escape_double = FALSE, col_names = FALSE, 
    comment = "#", trim_ws = TRUE, skip = 24)
Parsed with column specification:
cols(
  X1 = col_character(),
  X2 = col_character(),
  X3 = col_character(),
  X4 = col_integer(),
  X5 = col_integer(),
  X6 = col_character(),
  X7 = col_character(),
  X8 = col_character(),
  X9 = col_character()
)

Peak at data

a tibble is a dataframe

head(gff)

Look at data structure with str()

str(gff)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame':   28848 obs. of  9 variables:
 $ X1: chr  "I" "I" "I" "I" ...
 $ X2: chr  "SGD" "SGD" "SGD" "SGD" ...
 $ X3: chr  "CDS" "gene" "mRNA" "exon" ...
 $ X4: int  10091 11565 11565 11565 11565 12046 12046 12046 12046 13363 ...
 $ X5: int  10399 11951 11951 11951 11951 12426 12426 12426 12426 13743 ...
 $ X6: chr  "." "." "." "." ...
 $ X7: chr  "+" "-" "-" "-" ...
 $ X8: chr  "0" "." "." "." ...
 $ X9: chr  "ID=CDS:YAL066W;Parent=transcript:YAL066W;protein_id=YAL066W" "ID=gene:YAL065C;biotype=protein_coding;description=Putative protein of unknown function%3B has homology to FLO1%3B possible pse"| __truncated__ "ID=transcript:YAL065C;Parent=gene:YAL065C;biotype=protein_coding;transcript_id=YAL065C" "Parent=transcript:YAL065C;Name=YAL065C.1;constitutive=1;ensembl_end_phase=0;ensembl_phase=0;exon_id=YAL065C.1;rank=1" ...
 - attr(*, "spec")=List of 2
  ..$ cols   :List of 9
  .. ..$ X1: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ X2: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ X3: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ X4: list()
  .. .. ..- attr(*, "class")= chr  "collector_integer" "collector"
  .. ..$ X5: list()
  .. .. ..- attr(*, "class")= chr  "collector_integer" "collector"
  .. ..$ X6: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ X7: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ X8: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  .. ..$ X9: list()
  .. .. ..- attr(*, "class")= chr  "collector_character" "collector"
  ..$ default: list()
  .. ..- attr(*, "class")= chr  "collector_guess" "collector"
  ..- attr(*, "class")= chr "col_spec"

Look at the data the tidyverse way

using glimpse()

glimpse(gff)
Observations: 28,848
Variables: 9
$ X1 <chr> "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "...
$ X2 <chr> "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD"...
$ X3 <chr> "CDS", "gene", "mRNA", "exon", "CDS", "gene", "mRNA", "exon", "CDS", "gene", "mRNA", "exon", "CD...
$ X4 <int> 10091, 11565, 11565, 11565, 11565, 12046, 12046, 12046, 12046, 13363, 13363, 13363, 13363, 21566...
$ X5 <int> 10399, 11951, 11951, 11951, 11951, 12426, 12426, 12426, 12426, 13743, 13743, 13743, 13743, 21850...
$ X6 <chr> ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "...
$ X7 <chr> "+", "-", "-", "-", "-", "+", "+", "+", "+", "-", "-", "-", "-", "+", "+", "+", "+", "-", "-", "...
$ X8 <chr> "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", "...
$ X9 <chr> "ID=CDS:YAL066W;Parent=transcript:YAL066W;protein_id=YAL066W", "ID=gene:YAL065C;biotype=protein_...

Assign meaningful names to columns

same approach as naming dataframe columns in base R

names(gff) <- c("chromosome", 
                "source", 
                "feature", 
                "start",
                "stop", 
                "unknown1",
                "strand",
                "unknown2",
                "info"
                )

Dataframe now has meaningful names

note that tidyverse tries to guess data type

glimpse(gff)
Observations: 28,848
Variables: 9
$ chromosome <chr> "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I", "I"...
$ source     <chr> "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD...
$ feature    <chr> "CDS", "gene", "mRNA", "exon", "CDS", "gene", "mRNA", "exon", "CDS", "gene", "mRNA", "ex...
$ start      <int> 10091, 11565, 11565, 11565, 11565, 12046, 12046, 12046, 12046, 13363, 13363, 13363, 1336...
$ stop       <int> 10399, 11951, 11951, 11951, 11951, 12426, 12426, 12426, 12426, 13743, 13743, 13743, 1374...
$ unknown1   <chr> ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "."...
$ strand     <chr> "+", "-", "-", "-", "-", "+", "+", "+", "+", "-", "-", "-", "-", "+", "+", "+", "+", "-"...
$ unknown2   <chr> "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", ".", "0", "."...
$ info       <chr> "ID=CDS:YAL066W;Parent=transcript:YAL066W;protein_id=YAL066W", "ID=gene:YAL065C;biotype=...

assign columns proper datatypes

assigning correct data type is critical for anlayses and plotting with ggplot()

gff$feature = as.factor(gff$feature)
gff$chromosome = as.factor(gff$chromosome)
gff$strand = as.factor(gff$strand)
glimpse(gff)
Observations: 28,848
Variables: 9
$ chromosome <fctr> I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, ...
$ source     <chr> "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD", "SGD...
$ feature    <fctr> CDS, gene, mRNA, exon, CDS, gene, mRNA, exon, CDS, gene, mRNA, exon, CDS, gene, mRNA, e...
$ start      <int> 10091, 11565, 11565, 11565, 11565, 12046, 12046, 12046, 12046, 13363, 13363, 13363, 1336...
$ stop       <int> 10399, 11951, 11951, 11951, 11951, 12426, 12426, 12426, 12426, 13743, 13743, 13743, 1374...
$ unknown1   <chr> ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", ".", "."...
$ strand     <fctr> +, -, -, -, -, +, +, +, +, -, -, -, -, +, +, +, +, -, -, -, -, -, -, -, -, +, +, +, +, ...
$ unknown2   <chr> "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", ".", "0", ".", ".", ".", "0", "."...
$ info       <chr> "ID=CDS:YAL066W;Parent=transcript:YAL066W;protein_id=YAL066W", "ID=gene:YAL065C;biotype=...

Select columns using select()

gff <- select(gff, c("chromosome", "feature", "start", "stop", "strand"))
head(gff)

Add a column with mutate()

gff <- mutate(gff, length = abs(start - stop))
head(gff)

Sort tibble by column with arrange()

writing dplyr::arrange specifies the package and function

dplyr::arrange(gff,length)

Sort by feature size with arrange()

sort largest to smallest using -

dplyr::arrange(gff,-length)

Analyze with summarize()

this creates a new tibble/dataframe

summarise(gff, mean = mean(length), sd = sd(length), min = min(length), max = max(length), n = n())

Analyze with summarize()

the function n() counts how many observations their are

summarise(gff, mean = mean(length), sd = sd(length), min = min(length), max = max(length), n = n())

using the pipe: %>%

using the pipe: %>%

subset data with group_by()

gff %>%
mutate(length = abs(start - stop)) %>%
group_by(feature) %>%
summarise(mean = mean(length), sd = sd(length), min = min(length), max = max(length), n = n())

Filter rows with filter()

gff %>%
filter(feature != "mRNA" & feature != "rRNA_gene" & feature != "snoRNA_gene"& feature != "snRNA_gene") %>%
mutate(length = abs(start - stop)) %>%
group_by(feature) %>%
summarise(mean = mean(length), sd = sd(length), min = min(length), max = max(length), n = n()) 

Pass dataframe to ggplot for plotting

NOTE: ggplot uses + not the pipe %>%

gff %>%
filter(feature == c("CDS")) %>%
ggplot(aes(x = length)) + 
        geom_histogram(bins = 100)

Exercise 3

plot the population of each country in 1999 using %>% andggplot()`

table1

Exercise 3

String manipulation with stringr()

How do we get the gene names?

select(gff, info)

Separate values in column with separate()

gff %>%
mutate(length = abs(start - stop)) %>%
filter(feature == "gene") %>%
separate(col = "info", into = c("info1", "info2", "info3", "info4", "info5"), sep = ";", extra = "merge") %>%
separate(col = "info1", into = c("junk", "Systematic_name"), sep = ":") %>%
separate(col = "info2", into = c("junk2", "Gene"), sep = "Name=") %>%
separate(col = "info3", into = c("junk3", "Description1"), sep = "description=") %>%  
separate(col = "info4", into = c("junk4", "Description2"), sep = "description=") %>%
select(c(Description1, Description2))

Combine columns with unite()

gff %>%
mutate(length = abs(start - stop)) %>%
filter(feature == "gene") %>%
separate(col = "info", into = c("info1", "info2", "info3", "info4", "info5"), sep = ";", extra = "merge") %>%
separate(col = "info1", into = c("junk", "Systematic_name"), sep = ":") %>%
separate(col = "info2", into = c("junk2", "Gene"), sep = "Name=") %>%
separate(col = "info3", into = c("junk3", "Description1"), sep = "description=") %>%  
separate(col = "info4", into = c("junk4", "Description2"), sep = "description=") %>%    
unite(Description, Description1, Description2, sep = "") %>%
select(c( Description))

Save to a new variable

A general rule is if you are piping more than 10 steps save as a new variable

gff_clean <- gff %>%
mutate(length = abs(start - stop)) %>%
filter(feature == "gene") %>%
separate(col = "info", into = c("info1", "info2", "info3", "info4", "info5"), sep = ";", extra = "merge") %>%
separate(col = "info1", into = c("junk", "Systematic_name"), sep = ":") %>%
separate(col = "info2", into = c("junk2", "Gene"), sep = "Name=") %>%
separate(col = "info3", into = c("junk3", "Description1"), sep = "description=") %>%  
separate(col = "info4", into = c("junk4", "Description2"), sep = "description=") %>%    
unite(Description, Description1, Description2, sep = "") %>%
select(c(Systematic_name, Gene, Description))

Clean up strings with stringr()

gff_clean$Description <- str_replace_all(gff_clean$Description, "%3B", "")
gff_clean$Description <- str_replace_all(gff_clean$Description, "%2C", "")
gff_clean$Description <- str_replace_all(gff_clean$Description, "^NA", "")

gff_clean %>%
select(c(Description))

Write file

write_tsv(gff_clean, "Yeast_genes.txt", na = "NA")

How do we combine tables?

Mutating joins

A mutating join allows you combine variables from two tables by matchiung observations by their keys

1. Inner Join

matches pairs of observation from two tables whenever their keys are equal

2. Outer join

keeps observations that appear in at least one of the tables

  • left join keeps all the observations in x (should be the default)
  • right join keeps all the observations in y
  • full join keeps all observations in x and y

Filtering joins

affects (filters) the observations not the variables

  • semi_join(x, y) keeps all observations in x that have a match in y
  • anti_join(x, y) drops all observations in x that have a match in y

Dataset must contain common values (gene names)

str(data)

File to join with

str(gff_clean)

Joining data

dplyr::left_join(a, b, by = "x1") Join matching rows from b to a.

left_join(gff_clean, data, by = c("Systematic_name" = "Syst")) %>%
        str()

Excercise4

tidy data don’t allow correlation plots

tidy_gene_expression <- gene_expression %>%
        gather(t0:t2, key = "timepoint", value = "expression")
tidy_gene_expression

Rearrange the data

Plot correlation between t0 and t1

Excercise5

example

ggplot(data = yeast_features, mapping = aes(x = chromosome, fill = feature)) +
        geom_bar(position = "dodge")

Resources

LS0tCnRpdGxlOiAidGlkeXZlcnNlIgphdXRob3I6ICJEYXZpZCBHcmVzaGFtIgpkYXRlOiAiTm92ZXJtYmVyIDE1LCAyMDE3IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KI2tuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRkFMU0UpCmBgYAoKIyMgT3V0bGluZQoKKiB0aWR5IGRhdGEKKiByZWFkaW5nIGRhdGEKKiB2ZXJicyBpbiBkcGx5cigpCiAgICArIGBzZWxlY3QoKWAKICAgICsgYG11dGF0ZSgpYAogICAgKyBgYXJyYW5nZSgpYAogICAgKyBgc3VtbWFyaXNlKClgCiAgICArIGBmaWx0ZXIoKWAKKiB0aGUgcGlwZSBgJT4lYAoqIGpvaW5pbmcgZmlsZXMKCiMjTG9hZCBwYWNrYWdlcwoKYGBge3IsIGVjaG8gPSBUfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzdHJpbmdyKQpgYGAKCiMjIFRpZHkgZGF0YQoqIGVhY2ggX192YXJpYWJsZV9fIGlzIHNhdmVkIGluIGl0cyBvbiBfX2NvbHVtbl9fCiogZWFjaCBfX29ic2VydmF0aW9uX18gaXMgc2F2ZWQgaW4gaXRzIG93biBfX3Jvd19fCiogZWFjaCBfX3ZhbHVlX18gbXVzdCBoYXZlIGl0cyBvd24gX19jZWxsX18KCmBgYHtyIGVjaG89RkFMU0UsIGZpZy53aWR0aD02LCBmaWcuYXNwID0gMC42MTgsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgd2FybmluZz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzL3RpZHktMS5wbmciKQpgYGAKCiMjIElzIHRoaXMgdGFibGUgdGlkeT8KCmBgYHtyfQp0YWJsZTIKYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CmBgYHtyfQp0YWJsZTMKYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CmBgYHtyfQp0YWJsZTEKYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpnZW5lX2V4cHJlc3Npb24gPC0gdGliYmxlKAogICAgICAgIEdlbmUgPSBjKCJBQ1QxIiwgIkdBUDEiLCAiTUVQMiIsICJEVVIzIiwgIk1TTjIiLCAiREFMODAiKSwKICAgICAgICB0MCA9IGMoMi4yLCA0LjMsIDEuNiwgLTEuMiwgLTIuMCwgMC4yKSwKICAgICAgICB0MSA9IGMoMy4yLCAyLjEsIDAuOCwgLTEuOCwgLTAuOCwgMC42KSwKICAgICAgICB0MiA9IGMoNC41LCAxLjYsIDAuNCwgLTEuOCwgLTAuMSwgMC45KQopCgpgYGAKCmBgYHtyfQpnZW5lX2V4cHJlc3Npb24KYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpmYWNzX2RhdGEgPC0gdGliYmxlKAogICAgICAgIFNhbXBsZSA9IGMoIkExIiwgIkExIiwgIkEyIiwgIkEyIiwgIkEzIiwgIkEzIiksCiAgICAgICAgTWVhc3VyZSA9IGMoIkZTQyIsICJGTDEiLCAiRlNDIiwgIkZMMSIsICJGU0MiLCAiRkwxIiksCiAgICAgICAgVmFsdWUgPSBjKDMuNiwgNC41LCAzLjUsIDMuMiwgMy44LCA0LjIpCikKCmBgYAoKYGBge3J9CmZhY3NfZGF0YQpgYGAKCiMjIHdoYXQgYXJlIHRpZHkgZGF0YT8KCkplZmYgTGVlayBpbiBoaXMgYm9vayBfX1RoZSBFbGVtZW50cyBvZiBEYXRhIEFuYWx5dGljIFN0eWxlX18gc3VtbWFyaXplcyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRpZHkgZGF0YSBhcyB0aGUgcG9pbnRzOgoKKiBFYWNoIHZhcmlhYmxlIHlvdSBtZWFzdXJlIHNob3VsZCBiZSBpbiBvbmUgY29sdW1uLgoqIEVhY2ggZGlmZmVyZW50IG9ic2VydmF0aW9uIG9mIHRoYXQgdmFyaWFibGUgc2hvdWxkIGJlIGluIGEgZGlmZmVyZW50IHJvdy4KKiBUaGVyZSBzaG91bGQgYmUgb25lIHRhYmxlIGZvciBlYWNoICJraW5kIiBvZiB2YXJpYWJsZS4KKiBJZiB5b3UgaGF2ZSBtdWx0aXBsZSB0YWJsZXMsIHRoZXkgc2hvdWxkIGluY2x1ZGUgYSBjb2x1bW4gaW4gdGhlIHRhYmxlIHRoYXQgYWxsb3dzIHRoZW0gdG8gYmUgbGlua2VkLgoKW2h0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RpZHlfZGF0YV06IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RpZHlfZGF0YQoKCiMjIGdhdGhlcigpCnVzZWQgd2hlbiBjb2x1bW4gbmFtZXMgYXJlIG5vdCBuYW1lcyBvZiB2YXJpYWJsZXMsIGJ1dCBfdmFsdWVzXyBvZiBhIHZhcmlhYmxlIChlLmcuIHRpbWUpLgptYWtlcyB0YWJsZXMgX2xvbmdlcl8gYW5kIF9za2lubnlfIChwcmV2aW91c2x5IGtub3duIGFzIG1lbHRpbmcpCmBgYHtyfQpnZW5lX2V4cHJlc3Npb24KYGBgCgoKIyMgZ2F0aGVyKCkKdXNlZCB3aGVuIGNvbHVtbiBuYW1lcyBhcmUgbm90IG5hbWVzIG9mIHZhcmlhYmxlcywgYnV0IF92YWx1ZXNfIG9mIGEgdmFyaWFibGUgKGUuZy4gdGltZSkuCm1ha2VzIHRhYmxlcyBfbG9uZ2VyXyBhbmQgX3NraW5ueV8gKHByZXZpb3VzbHkga25vd24gYXMgbWVsdGluZykKYGBge3J9CmdhdGhlcihnZW5lX2V4cHJlc3Npb24sIHQwOnQyLCBrZXkgPSAidGltZXBvaW50IiwgdmFsdWUgPSAiZXhwcmVzc2lvbiIpCmBgYAoKIyMgc3ByZWFkKCkKU3ByZWFkaW5nIGlzIHRoZSBvcHBvc2l0ZSBvZiBnYXRoZXJpbmcuICBVc2VkIHdoZW4gYW4gb2JzZXJ2YXRpb24gaXMgc2NhdHRlcmVkIGFjcm9zcyBtdWx0aXBsZSByb3dzLgpgc3ByZWFkKClgIG1ha2VzIHRhYmxlcyBfc2hvcnRlcl8gYW5kIF93aWRlcl8KYGBge3J9CmZhY3NfZGF0YQpgYGAKCiMjIHNwcmVhZCgpClNwcmVhZGluZyBpcyB0aGUgb3Bwb3NpdGUgb2YgZ2F0aGVyaW5nLiAgVXNlZCB3aGVuIGFuIG9ic2VydmF0aW9uIGlzIHNjYXR0ZXJlZCBhY3Jvc3MgbXVsdGlwbGUgcm93cy4KYHNwcmVhZCgpYCBtYWtlcyB0YWJsZXMgX3Nob3J0ZXJfIGFuZCBfd2lkZXJfCmBgYHtyfQpzcHJlYWQoZmFjc19kYXRhLCBrZXkgPSBNZWFzdXJlLCB2YWx1ZSA9IFZhbHVlKQpgYGAKCiMjIEV4ZXJjaXNlIDEKcHV0IHRhYmxlMiBpbiB0aWR5IGZvcm1hdApgYGB7cn0KdGFibGUyWzE6NixdICN0cnVuY2F0ZWQgc28gaXQgZml0cyBvbiBzbGlkZQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpzcHJlYWQodGFibGUyLCBrZXkgPSB0eXBlLCB2YWx1ZSA9IGNvdW50KQpgYGAKCiMjIEV4ZXJjaXNlIDIKY29udmVydCB0YWJsZTEgdG8gdGFibGUyCmBgYHtyfQp0YWJsZTEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpoZWFkKHRhYmxlMikKYGBgCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KZ2F0aGVyKHRhYmxlMSwgYyhjb3VudHJ5LCB5ZWFyLCBjYXNlcywgcG9wdWxhdGlvbiksIGtleSA9ICJ5ZWFyIiwgdmFsdWUgPSAiY291bnQiKQpgYGAKCgojIyBkYXRhIGltcG9ydApgcmVhZHIoKWAgaGFzIG51bWVyb3VzIGZ1bmN0aW9ucyBmb3IgcmVhZGluZyBpbiBmaWxlcyBhcyB0aWJibGVzCgorICdyZWFkX2NzdigpYCBmb3IgY29tbWEtZGVsaW1pdGVkCisgYHJlYWRfdHN2KClgIGZvciB0YWItZGVsaW1pdGVkCmBgYHtyIGRhdGF9CmdmZiA8LSByZWFkX2RlbGltKCJTYWNjaGFyb215Y2VzX2NlcmV2aXNpYWUuUjY0LTEtMS4zNC5nZmYzIiwgCiAgICAiXHQiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFLCAKICAgIGNvbW1lbnQgPSAiIyIsIHRyaW1fd3MgPSBUUlVFLCBza2lwID0gMjQpCmBgYAoKIyNQZWFrIGF0IGRhdGEKYSB0aWJibGUgaXMgYSBkYXRhZnJhbWUKYGBge3J9CmhlYWQoZ2ZmKQpgYGAKCiMjTG9vayBhdCBkYXRhIHN0cnVjdHVyZSB3aXRoIHN0cigpCmBgYHtyfQpzdHIoZ2ZmKQpgYGAKCiMjTG9vayBhdCB0aGUgZGF0YSB0aGUgdGlkeXZlcnNlIHdheQojIyN1c2luZyBnbGltcHNlKCkKCmBgYHtyfQpnbGltcHNlKGdmZikKYGBgCgojI0Fzc2lnbiBtZWFuaW5nZnVsIG5hbWVzIHRvIGNvbHVtbnMKc2FtZSBhcHByb2FjaCBhcyBuYW1pbmcgZGF0YWZyYW1lIGNvbHVtbnMgaW4gYmFzZSBSCmBgYHtyfQpuYW1lcyhnZmYpIDwtIGMoImNocm9tb3NvbWUiLCAKICAgICAgICAgICAgICAgICJzb3VyY2UiLCAKICAgICAgICAgICAgICAgICJmZWF0dXJlIiwgCiAgICAgICAgICAgICAgICAic3RhcnQiLAogICAgICAgICAgICAgICAgInN0b3AiLCAKICAgICAgICAgICAgICAgICJ1bmtub3duMSIsCiAgICAgICAgICAgICAgICAic3RyYW5kIiwKICAgICAgICAgICAgICAgICJ1bmtub3duMiIsCiAgICAgICAgICAgICAgICAiaW5mbyIKICAgICAgICAgICAgICAgICkKYGBgCgojI0RhdGFmcmFtZSBub3cgaGFzIG1lYW5pbmdmdWwgbmFtZXMKbm90ZSB0aGF0IHRpZHl2ZXJzZSB0cmllcyB0byBndWVzcyBkYXRhIHR5cGUKYGBge3J9CmdsaW1wc2UoZ2ZmKQpgYGAKCiMjYXNzaWduIGNvbHVtbnMgcHJvcGVyIGRhdGF0eXBlcwphc3NpZ25pbmcgY29ycmVjdCBkYXRhIHR5cGUgaXMgY3JpdGljYWwgZm9yIGFubGF5c2VzIGFuZCBwbG90dGluZyB3aXRoIGdncGxvdCgpCmBgYHtyfQpnZmYkZmVhdHVyZSA9IGFzLmZhY3RvcihnZmYkZmVhdHVyZSkKZ2ZmJGNocm9tb3NvbWUgPSBhcy5mYWN0b3IoZ2ZmJGNocm9tb3NvbWUpCmdmZiRzdHJhbmQgPSBhcy5mYWN0b3IoZ2ZmJHN0cmFuZCkKZ2xpbXBzZShnZmYpCmBgYAoKIyNTZWxlY3QgY29sdW1ucyB1c2luZyBgc2VsZWN0KClgCmBgYHtyfQpnZmYgPC0gc2VsZWN0KGdmZiwgYygiY2hyb21vc29tZSIsICJmZWF0dXJlIiwgInN0YXJ0IiwgInN0b3AiLCAic3RyYW5kIikpCmhlYWQoZ2ZmKQpgYGAKCiMjQWRkIGEgY29sdW1uIHdpdGggYG11dGF0ZSgpYApgYGB7cn0KZ2ZmIDwtIG11dGF0ZShnZmYsIGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKQpoZWFkKGdmZikKYGBgCgojI1NvcnQgdGliYmxlIGJ5IGNvbHVtbiB3aXRoIGBhcnJhbmdlKClgCndyaXRpbmcgYGRwbHlyOjphcnJhbmdlYCBzcGVjaWZpZXMgdGhlIHBhY2thZ2UgYW5kIGZ1bmN0aW9uIApgYGB7cn0KZHBseXI6OmFycmFuZ2UoZ2ZmLGxlbmd0aCkKYGBgCgojI1NvcnQgYnkgZmVhdHVyZSBzaXplIHdpdGggYXJyYW5nZSgpCnNvcnQgbGFyZ2VzdCB0byBzbWFsbGVzdCB1c2luZyBgLWAKYGBge3J9CmRwbHlyOjphcnJhbmdlKGdmZiwtbGVuZ3RoKQpgYGAKCiMjQW5hbHl6ZSB3aXRoIGBzdW1tYXJpemUoKWAKdGhpcyBjcmVhdGVzIGEgbmV3IHRpYmJsZS9kYXRhZnJhbWUgCmBgYHtyfQpzdW1tYXJpc2UoZ2ZmLCBtZWFuID0gbWVhbihsZW5ndGgpLCBzZCA9IHNkKGxlbmd0aCksIG1pbiA9IG1pbihsZW5ndGgpLCBtYXggPSBtYXgobGVuZ3RoKSwgbiA9IG4oKSkKYGBgCgojI0FuYWx5emUgd2l0aCBgc3VtbWFyaXplKClgCnRoZSBmdW5jdGlvbiBgbigpYCBjb3VudHMgaG93IG1hbnkgb2JzZXJ2YXRpb25zIHRoZWlyIGFyZQpgYGB7cn0Kc3VtbWFyaXNlKGdmZiwgbWVhbiA9IG1lYW4obGVuZ3RoKSwgc2QgPSBzZChsZW5ndGgpLCBtaW4gPSBtaW4obGVuZ3RoKSwgbWF4ID0gbWF4KGxlbmd0aCksIG4gPSBuKCkpCmBgYAoKIyN1c2luZyB0aGUgcGlwZTogYCU+JWAKKyB0aGUgcGlwZSBpcyBmcm9tIHRoZSBgbWFnaXR0cmAgcGFja2FnZQorIHNhbWUgYXMgYHxgIGluIHVuaXgKKyBhbGxvd3MgeW91IHRvIHBlcmZvcm0gbXVsdGlwbGUgc2VxdWVudGlhbCBmdW5jdGlvbnMKKyBwcm9ub3VuY2VkIF9fdGhlbl9fCisgdW5sZWFzaGVzIHRoZSB0cnVlIHBvd2VyIG9mIHRpZHl2ZXJzZSBmdW5jdGlvbnMKCgojI3VzaW5nIHRoZSBwaXBlOiBgJT4lYAojIyNzdWJzZXQgZGF0YSB3aXRoIGBncm91cF9ieSgpYApgYGB7cn0KZ2ZmICU+JQptdXRhdGUobGVuZ3RoID0gYWJzKHN0YXJ0IC0gc3RvcCkpICU+JQpncm91cF9ieShmZWF0dXJlKSAlPiUKc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGxlbmd0aCksIHNkID0gc2QobGVuZ3RoKSwgbWluID0gbWluKGxlbmd0aCksIG1heCA9IG1heChsZW5ndGgpLCBuID0gbigpKQpgYGAKCiMjRmlsdGVyIHJvd3Mgd2l0aCBgZmlsdGVyKClgCmBgYHtyfQpnZmYgJT4lCmZpbHRlcihmZWF0dXJlICE9ICJtUk5BIiAmIGZlYXR1cmUgIT0gInJSTkFfZ2VuZSIgJiBmZWF0dXJlICE9ICJzbm9STkFfZ2VuZSImIGZlYXR1cmUgIT0gInNuUk5BX2dlbmUiKSAlPiUKbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKZ3JvdXBfYnkoZmVhdHVyZSkgJT4lCnN1bW1hcmlzZShtZWFuID0gbWVhbihsZW5ndGgpLCBzZCA9IHNkKGxlbmd0aCksIG1pbiA9IG1pbihsZW5ndGgpLCBtYXggPSBtYXgobGVuZ3RoKSwgbiA9IG4oKSkgCmBgYAoKIyNQYXNzIGRhdGFmcmFtZSB0byBnZ3Bsb3QgZm9yIHBsb3R0aW5nCiMjIyBOT1RFOiBnZ3Bsb3QgdXNlcyBgK2Agbm90IHRoZSBwaXBlIGAlPiVgCmBgYHtyLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0yfQpnZmYgJT4lCmZpbHRlcihmZWF0dXJlID09IGMoIkNEUyIpKSAlPiUKZ2dwbG90KGFlcyh4ID0gbGVuZ3RoKSkgKyAKICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKQpgYGAKCiMjRXhlcmNpc2UgMwpwbG90IHRoZSBwb3B1bGF0aW9uIG9mIGVhY2ggY291bnRyeSBpbiAxOTk5IHVzaW5nIGAlPiUgYW5kICBgZ2dwbG90KClgCmBgYHtyfQp0YWJsZTEKYGBgCgojI0V4ZXJjaXNlIDMKYGBge3IgZWNobz1GQUxTRX0KdGFibGUxICU+JQogICAgICAgIHNlbGVjdCgtY2FzZXMpICU+JQogICAgICAgIGZpbHRlcih5ZWFyID09ICIxOTk5IikgJT4lCiAgICAgICAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNvdW50cnksIGZpbGwgPSBjb3VudHJ5LCB5ID0gcG9wdWxhdGlvbikpICsKICAgICAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpCmBgYAoKCiMjU3RyaW5nIG1hbmlwdWxhdGlvbiB3aXRoIGBzdHJpbmdyKClgCiMjIyBIb3cgZG8gd2UgZ2V0IHRoZSBnZW5lIG5hbWVzPwpgYGB7ciwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZ2ZmIDwtIHJlYWRfZGVsaW0oIlNhY2NoYXJvbXljZXNfY2VyZXZpc2lhZS5SNjQtMS0xLjM0LmdmZjMiLCAKICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgY29sX25hbWVzID0gRkFMU0UsIAogICAgY29tbWVudCA9ICIjIiwgdHJpbV93cyA9IFRSVUUsIHNraXAgPSAyNCkKCm5hbWVzKGdmZikgPC0gYygiY2hyb21vc29tZSIsIAogICAgICAgICAgICAgICAgInNvdXJjZSIsIAogICAgICAgICAgICAgICAgImZlYXR1cmUiLCAKICAgICAgICAgICAgICAgICJzdGFydCIsCiAgICAgICAgICAgICAgICAic3RvcCIsIAogICAgICAgICAgICAgICAgInVua25vd24xIiwKICAgICAgICAgICAgICAgICJzdHJhbmQiLAogICAgICAgICAgICAgICAgInVua25vd24yIiwKICAgICAgICAgICAgICAgICJpbmZvIgogICAgICAgICAgICAgICAgKQoKZ2ZmJGZlYXR1cmUgPSBhcy5mYWN0b3IoZ2ZmJGZlYXR1cmUpCmdmZiRjaHJvbW9zb21lID0gYXMuZmFjdG9yKGdmZiRjaHJvbW9zb21lKQpnZmYkc3RyYW5kID0gYXMuZmFjdG9yKGdmZiRzdHJhbmQpCmBgYAoKYGBge3J9CnNlbGVjdChnZmYsIGluZm8pCmBgYAoKCiMjU2VwYXJhdGUgdmFsdWVzIGluIGNvbHVtbiB3aXRoIGBzZXBhcmF0ZSgpYCB7LnNtYWxsZXJ9CmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpnZmYgJT4lCm11dGF0ZShsZW5ndGggPSBhYnMoc3RhcnQgLSBzdG9wKSkgJT4lCmZpbHRlcihmZWF0dXJlID09ICJnZW5lIikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvIiwgaW50byA9IGMoImluZm8xIiwgImluZm8yIiwgImluZm8zIiwgImluZm80IiwgImluZm81IiksIHNlcCA9ICI7IiwgZXh0cmEgPSAibWVyZ2UiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8xIiwgaW50byA9IGMoImp1bmsiLCAiU3lzdGVtYXRpY19uYW1lIiksIHNlcCA9ICI6IikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvMiIsIGludG8gPSBjKCJqdW5rMiIsICJHZW5lIiksIHNlcCA9ICJOYW1lPSIpICU+JQpzZXBhcmF0ZShjb2wgPSAiaW5mbzMiLCBpbnRvID0gYygianVuazMiLCAiRGVzY3JpcHRpb24xIiksIHNlcCA9ICJkZXNjcmlwdGlvbj0iKSAlPiUgIApzZXBhcmF0ZShjb2wgPSAiaW5mbzQiLCBpbnRvID0gYygianVuazQiLCAiRGVzY3JpcHRpb24yIiksIHNlcCA9ICJkZXNjcmlwdGlvbj0iKSAlPiUKc2VsZWN0KGMoRGVzY3JpcHRpb24xLCBEZXNjcmlwdGlvbjIpKQpgYGAKCiMjQ29tYmluZSBjb2x1bW5zIHdpdGggYHVuaXRlKClgIHsuc21hbGxlcn0KYGBge3IsIHdhcm5pbmc9RkFMU0V9CmdmZiAlPiUKbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKZmlsdGVyKGZlYXR1cmUgPT0gImdlbmUiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8iLCBpbnRvID0gYygiaW5mbzEiLCAiaW5mbzIiLCAiaW5mbzMiLCAiaW5mbzQiLCAiaW5mbzUiKSwgc2VwID0gIjsiLCBleHRyYSA9ICJtZXJnZSIpICU+JQpzZXBhcmF0ZShjb2wgPSAiaW5mbzEiLCBpbnRvID0gYygianVuayIsICJTeXN0ZW1hdGljX25hbWUiKSwgc2VwID0gIjoiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8yIiwgaW50byA9IGMoImp1bmsyIiwgIkdlbmUiKSwgc2VwID0gIk5hbWU9IikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvMyIsIGludG8gPSBjKCJqdW5rMyIsICJEZXNjcmlwdGlvbjEiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgCnNlcGFyYXRlKGNvbCA9ICJpbmZvNCIsIGludG8gPSBjKCJqdW5rNCIsICJEZXNjcmlwdGlvbjIiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgICAKdW5pdGUoRGVzY3JpcHRpb24sIERlc2NyaXB0aW9uMSwgRGVzY3JpcHRpb24yLCBzZXAgPSAiIikgJT4lCnNlbGVjdChjKERlc2NyaXB0aW9uKSkKYGBgCgojI1NhdmUgdG8gYSBuZXcgdmFyaWFibGUgey5zbWFsbGVyfQpBIGdlbmVyYWwgcnVsZSBpcyBpZiB5b3UgYXJlIHBpcGluZyBtb3JlIHRoYW4gMTAgc3RlcHMgc2F2ZSBhcyBhIG5ldyB2YXJpYWJsZQpgYGB7ciwgd2FybmluZz1GQUxTRX0KZ2ZmX2NsZWFuIDwtIGdmZiAlPiUKbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKZmlsdGVyKGZlYXR1cmUgPT0gImdlbmUiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8iLCBpbnRvID0gYygiaW5mbzEiLCAiaW5mbzIiLCAiaW5mbzMiLCAiaW5mbzQiLCAiaW5mbzUiKSwgc2VwID0gIjsiLCBleHRyYSA9ICJtZXJnZSIpICU+JQpzZXBhcmF0ZShjb2wgPSAiaW5mbzEiLCBpbnRvID0gYygianVuayIsICJTeXN0ZW1hdGljX25hbWUiKSwgc2VwID0gIjoiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8yIiwgaW50byA9IGMoImp1bmsyIiwgIkdlbmUiKSwgc2VwID0gIk5hbWU9IikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvMyIsIGludG8gPSBjKCJqdW5rMyIsICJEZXNjcmlwdGlvbjEiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgCnNlcGFyYXRlKGNvbCA9ICJpbmZvNCIsIGludG8gPSBjKCJqdW5rNCIsICJEZXNjcmlwdGlvbjIiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgICAKdW5pdGUoRGVzY3JpcHRpb24sIERlc2NyaXB0aW9uMSwgRGVzY3JpcHRpb24yLCBzZXAgPSAiIikgJT4lCnNlbGVjdChjKFN5c3RlbWF0aWNfbmFtZSwgR2VuZSwgRGVzY3JpcHRpb24pKQpgYGAKCiMjQ2xlYW4gdXAgc3RyaW5ncyB3aXRoIGBzdHJpbmdyKClgIHsuc21hbGxlcn0KYGBge3IsIHdhcm5pbmc9RkFMU0V9CmdmZl9jbGVhbiREZXNjcmlwdGlvbiA8LSBzdHJfcmVwbGFjZV9hbGwoZ2ZmX2NsZWFuJERlc2NyaXB0aW9uLCAiJTNCIiwgIiIpCmdmZl9jbGVhbiREZXNjcmlwdGlvbiA8LSBzdHJfcmVwbGFjZV9hbGwoZ2ZmX2NsZWFuJERlc2NyaXB0aW9uLCAiJTJDIiwgIiIpCmdmZl9jbGVhbiREZXNjcmlwdGlvbiA8LSBzdHJfcmVwbGFjZV9hbGwoZ2ZmX2NsZWFuJERlc2NyaXB0aW9uLCAiXk5BIiwgIiIpCgpnZmZfY2xlYW4gJT4lCnNlbGVjdChjKERlc2NyaXB0aW9uKSkKCmBgYAoKIyNXcml0ZSBmaWxlCmBgYHtyfQp3cml0ZV90c3YoZ2ZmX2NsZWFuLCAiWWVhc3RfZ2VuZXMudHh0IiwgbmEgPSAiTkEiKQpgYGAKCgoKIyMgSG93IGRvIHdlIGNvbWJpbmUgdGFibGVzPwoKIyMjIE11dGF0aW5nIGpvaW5zCkEgbXV0YXRpbmcgam9pbiBhbGxvd3MgeW91IGNvbWJpbmUgdmFyaWFibGVzIGZyb20gdHdvIHRhYmxlcyBieSBtYXRjaGl1bmcgb2JzZXJ2YXRpb25zIGJ5IHRoZWlyIGtleXMKCiMjIyMxLiBJbm5lciBKb2luCm1hdGNoZXMgcGFpcnMgb2Ygb2JzZXJ2YXRpb24gZnJvbSB0d28gdGFibGVzIHdoZW5ldmVyIHRoZWlyIGtleXMgYXJlIGVxdWFsCgojIyMjMi4gT3V0ZXIgam9pbgprZWVwcyBvYnNlcnZhdGlvbnMgdGhhdCBhcHBlYXIgaW4gYXQgbGVhc3Qgb25lIG9mIHRoZSB0YWJsZXMKCisgbGVmdCBqb2luIGtlZXBzIGFsbCB0aGUgb2JzZXJ2YXRpb25zIGluIHggKHNob3VsZCBiZSB0aGUgZGVmYXVsdCkKKyByaWdodCBqb2luIGtlZXBzIGFsbCB0aGUgb2JzZXJ2YXRpb25zIGluIHkKKyBmdWxsIGpvaW4ga2VlcHMgYWxsIG9ic2VydmF0aW9ucyBpbiB4IGFuZCB5CgojIyNGaWx0ZXJpbmcgam9pbnMKYWZmZWN0cyAoZmlsdGVycykgdGhlIG9ic2VydmF0aW9ucyBub3QgdGhlIHZhcmlhYmxlcwoKKyBzZW1pX2pvaW4oeCwgeSkga2VlcHMgYWxsIG9ic2VydmF0aW9ucyBpbiB4IHRoYXQgaGF2ZSBhIG1hdGNoIGluIHkKKyBhbnRpX2pvaW4oeCwgeSkgZHJvcHMgYWxsIG9ic2VydmF0aW9ucyBpbiB4IHRoYXQgaGF2ZSBhIG1hdGNoIGluIHkKCiMjRGF0YXNldCBtdXN0IGNvbnRhaW4gY29tbW9uIHZhbHVlcyAoZ2VuZSBuYW1lcykgey5zbWFsbGVyfQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpkYXRhIDwtICByZWFkX2RlbGltKCJOZXltb3Rpbl9UYWJsZV9TMS50eHQiLCAKICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgY29sX25hbWVzID0gVFJVRSwgCiAgICBjb21tZW50ID0gIiMiLCB0cmltX3dzID0gVFJVRSkKYGBgCgpgYGB7cn0Kc3RyKGRhdGEpCmBgYAoKIyNGaWxlIHRvIGpvaW4gd2l0aApgYGB7cn0Kc3RyKGdmZl9jbGVhbikKYGBgCgojI0pvaW5pbmcgZGF0YQpgZHBseXI6OmxlZnRfam9pbihhLCBiLCBieSA9ICJ4MSIpYApKb2luIG1hdGNoaW5nIHJvd3MgZnJvbSBiIHRvIGEuCgpgYGB7cn0KbGVmdF9qb2luKGdmZl9jbGVhbiwgZGF0YSwgYnkgPSBjKCJTeXN0ZW1hdGljX25hbWUiID0gIlN5c3QiKSkgJT4lCiAgICAgICAgc3RyKCkKYGBgCgojIyBFeGNlcmNpc2U0CgojIyB0aWR5IGRhdGEgZG9uJ3QgYWxsb3cgY29ycmVsYXRpb24gcGxvdHMKCmBgYHtyfQp0aWR5X2dlbmVfZXhwcmVzc2lvbiA8LSBnZW5lX2V4cHJlc3Npb24gJT4lCiAgICAgICAgZ2F0aGVyKHQwOnQyLCBrZXkgPSAidGltZXBvaW50IiwgdmFsdWUgPSAiZXhwcmVzc2lvbiIpCnRpZHlfZ2VuZV9leHByZXNzaW9uCmBgYAoKIyMgUmVhcnJhbmdlIHRoZSBkYXRhIAoKYGBge3IgZWNobz1GQUxTRX0KdGlkeV9nZW5lX2V4cHJlc3Npb24gJT4lCiAgICAgICAgc3ByZWFkKGtleSA9IHRpbWVwb2ludCwgdmFsdWUgPSBleHByZXNzaW9uKQpgYGAKCiMjIFBsb3QgY29ycmVsYXRpb24gYmV0d2VlbiB0MCBhbmQgdDEKCmBgYHtyIGVjaG89RkFMU0V9CnRpZHlfZ2VuZV9leHByZXNzaW9uICU+JQogICAgICAgIHNwcmVhZChrZXkgPSB0aW1lcG9pbnQsIHZhbHVlID0gZXhwcmVzc2lvbikgJT4lCiAgICAgICAgZ2dwbG90KGFlcyh4ID0gdDAsIHkgPSB0MSkpICsgCiAgICAgICAgZ2VvbV9wb2ludCgpCmBgYAoKIyMgRXhjZXJjaXNlNQoKKyBHZXQgdGhlIGdmZiBmaWxlIGZvciB5b3VyIGZhdm9yaXRlIG9yZ2FuaXNtIGZyb20gdGhlIFNHUgorIFRpZHkgdGhlIGdmZgorIHBsb3QgZGlzdHJpYnV0aW9uIG9mIGZlYXR1cmVzIHBlciBjaHJvbW9zb21lCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KZ2ZmIDwtIHJlYWRfZGVsaW0oIlNhY2NoYXJvbXljZXNfY2VyZXZpc2lhZS5SNjQtMS0xLjM0LmdmZjMiLCAKICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgY29sX25hbWVzID0gRkFMU0UsIAogICAgY29tbWVudCA9ICIjIiwgdHJpbV93cyA9IFRSVUUsIHNraXAgPSAyNCkKbmFtZXMoZ2ZmKSA8LSBjKCJjaHJvbW9zb21lIiwgCiAgICAgICAgICAgICAgICAic291cmNlIiwgCiAgICAgICAgICAgICAgICAiZmVhdHVyZSIsIAogICAgICAgICAgICAgICAgInN0YXJ0IiwKICAgICAgICAgICAgICAgICJzdG9wIiwgCiAgICAgICAgICAgICAgICAidW5rbm93bjEiLAogICAgICAgICAgICAgICAgInN0cmFuZCIsCiAgICAgICAgICAgICAgICAidW5rbm93bjIiLAogICAgICAgICAgICAgICAgImluZm8iCiAgICAgICAgICAgICAgICApCiNjb3JyZWN0IGRhdGEgdHlwZXMKZ2ZmJGZlYXR1cmUgPSBhcy5mYWN0b3IoZ2ZmJGZlYXR1cmUpCmdmZiRjaHJvbW9zb21lID0gYXMuZmFjdG9yKGdmZiRjaHJvbW9zb21lKQpnZmYkc3RyYW5kID0gYXMuZmFjdG9yKGdmZiRzdHJhbmQpCgp5ZWFzdF9mZWF0dXJlcyA8LSBnZmYgJT4lCiAgICAgICAgc2VsZWN0KGNocm9tb3NvbWUsIGZlYXR1cmUsIHN0YXJ0LCBzdG9wLCBzdHJhbmQpICU+JQogICAgICAgIG11dGF0ZShsZW5ndGggPSBhYnMoc3RhcnQgLSBzdG9wKSkgJT4lCiAgICAgICAgZmlsdGVyKGZlYXR1cmUgPT0gIkNEUyIgfCBmZWF0dXJlID09ICJyUk5BIiB8IGZlYXR1cmUgPT0gInNub1JOQSIgfCBmZWF0dXJlID09ICJzblJOQSIgfCBmZWF0dXJlID09ICJ0Uk5BX2dlbmUiKQpgYGAKCiMjZXhhbXBsZQoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuYXNwID0gMC42MTgsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgZmlsbCA9IGZlYXR1cmUpKSArCiAgICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCgoKIyNSZXNvdXJjZXMKCiogQmFzZSBSIHRvIHRpZHl2ZXJzZSAgaHR0cDovL3d3dy5zaWduaWZpY2FudGRpZ2l0cy5vcmcvMjAxNy8xMC9zd2l0Y2hpbmctZnJvbS1iYXNlLXItdG8tdGlkeXZlcnNlLwoqIENoZWF0c2hlZXQ6IGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL2RhdGEtd3JhbmdsaW5nLWNoZWF0c2hlZXQucGRmCgoK