Outline
- tidy data
- reading data
- verbs in dplyr()
select()
mutate()
arrange()
summarise()
filter()
- the pipe
%>%
- joining files
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
- each variable is saved in its on column
- each observation is saved in its own row
- each value must have its own cell
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:
- Each variable you measure should be in one column.
- Each different observation of that variable should be in a different row.
- There should be one table for each “kind” of variable.
- If you have multiple tables, they should include a column in the table that allows them to be linked.
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
- ’read_csv()` for comma-delimited
read_tsv()
for tab-delimited
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: %>%
- the pipe is from the
magittr
package
- same as
|
in unix
- allows you to perform multiple sequential functions
- pronounced then
- unleashes the true power of tidyverse functions
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 %>% and
ggplot()`
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
- Get the gff file for your favorite organism from the SGR
- Tidy the gff
- plot distribution of features per chromosome
example
ggplot(data = yeast_features, mapping = aes(x = chromosome, fill = feature)) +
geom_bar(position = "dodge")
LS0tCnRpdGxlOiAidGlkeXZlcnNlIgphdXRob3I6ICJEYXZpZCBHcmVzaGFtIgpkYXRlOiAiTm92ZXJtYmVyIDE1LCAyMDE3IgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdG9jOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KI2tuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRkFMU0UpCmBgYAoKIyMgT3V0bGluZQoKKiB0aWR5IGRhdGEKKiByZWFkaW5nIGRhdGEKKiB2ZXJicyBpbiBkcGx5cigpCiAgICArIGBzZWxlY3QoKWAKICAgICsgYG11dGF0ZSgpYAogICAgKyBgYXJyYW5nZSgpYAogICAgKyBgc3VtbWFyaXNlKClgCiAgICArIGBmaWx0ZXIoKWAKKiB0aGUgcGlwZSBgJT4lYAoqIGpvaW5pbmcgZmlsZXMKCiMjTG9hZCBwYWNrYWdlcwoKYGBge3IsIGVjaG8gPSBUfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShzdHJpbmdyKQpgYGAKCiMjIFRpZHkgZGF0YQoqIGVhY2ggX192YXJpYWJsZV9fIGlzIHNhdmVkIGluIGl0cyBvbiBfX2NvbHVtbl9fCiogZWFjaCBfX29ic2VydmF0aW9uX18gaXMgc2F2ZWQgaW4gaXRzIG93biBfX3Jvd19fCiogZWFjaCBfX3ZhbHVlX18gbXVzdCBoYXZlIGl0cyBvd24gX19jZWxsX18KCmBgYHtyIGVjaG89RkFMU0UsIGZpZy53aWR0aD02LCBmaWcuYXNwID0gMC42MTgsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIiwgd2FybmluZz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vaW1hZ2VzL3RpZHktMS5wbmciKQpgYGAKCiMjIElzIHRoaXMgdGFibGUgdGlkeT8KCmBgYHtyfQp0YWJsZTIKYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CmBgYHtyfQp0YWJsZTMKYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CmBgYHtyfQp0YWJsZTEKYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpnZW5lX2V4cHJlc3Npb24gPC0gdGliYmxlKAogICAgICAgIEdlbmUgPSBjKCJBQ1QxIiwgIkdBUDEiLCAiTUVQMiIsICJEVVIzIiwgIk1TTjIiLCAiREFMODAiKSwKICAgICAgICB0MCA9IGMoMi4yLCA0LjMsIDEuNiwgLTEuMiwgLTIuMCwgMC4yKSwKICAgICAgICB0MSA9IGMoMy4yLCAyLjEsIDAuOCwgLTEuOCwgLTAuOCwgMC42KSwKICAgICAgICB0MiA9IGMoNC41LCAxLjYsIDAuNCwgLTEuOCwgLTAuMSwgMC45KQopCgpgYGAKCmBgYHtyfQpnZW5lX2V4cHJlc3Npb24KYGBgCgojIyBJcyB0aGlzIHRhYmxlIHRpZHk/CgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpmYWNzX2RhdGEgPC0gdGliYmxlKAogICAgICAgIFNhbXBsZSA9IGMoIkExIiwgIkExIiwgIkEyIiwgIkEyIiwgIkEzIiwgIkEzIiksCiAgICAgICAgTWVhc3VyZSA9IGMoIkZTQyIsICJGTDEiLCAiRlNDIiwgIkZMMSIsICJGU0MiLCAiRkwxIiksCiAgICAgICAgVmFsdWUgPSBjKDMuNiwgNC41LCAzLjUsIDMuMiwgMy44LCA0LjIpCikKCmBgYAoKYGBge3J9CmZhY3NfZGF0YQpgYGAKCiMjIHdoYXQgYXJlIHRpZHkgZGF0YT8KCkplZmYgTGVlayBpbiBoaXMgYm9vayBfX1RoZSBFbGVtZW50cyBvZiBEYXRhIEFuYWx5dGljIFN0eWxlX18gc3VtbWFyaXplcyB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRpZHkgZGF0YSBhcyB0aGUgcG9pbnRzOgoKKiBFYWNoIHZhcmlhYmxlIHlvdSBtZWFzdXJlIHNob3VsZCBiZSBpbiBvbmUgY29sdW1uLgoqIEVhY2ggZGlmZmVyZW50IG9ic2VydmF0aW9uIG9mIHRoYXQgdmFyaWFibGUgc2hvdWxkIGJlIGluIGEgZGlmZmVyZW50IHJvdy4KKiBUaGVyZSBzaG91bGQgYmUgb25lIHRhYmxlIGZvciBlYWNoICJraW5kIiBvZiB2YXJpYWJsZS4KKiBJZiB5b3UgaGF2ZSBtdWx0aXBsZSB0YWJsZXMsIHRoZXkgc2hvdWxkIGluY2x1ZGUgYSBjb2x1bW4gaW4gdGhlIHRhYmxlIHRoYXQgYWxsb3dzIHRoZW0gdG8gYmUgbGlua2VkLgoKW2h0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RpZHlfZGF0YV06IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RpZHlfZGF0YQoKCiMjIGdhdGhlcigpCnVzZWQgd2hlbiBjb2x1bW4gbmFtZXMgYXJlIG5vdCBuYW1lcyBvZiB2YXJpYWJsZXMsIGJ1dCBfdmFsdWVzXyBvZiBhIHZhcmlhYmxlIChlLmcuIHRpbWUpLgptYWtlcyB0YWJsZXMgX2xvbmdlcl8gYW5kIF9za2lubnlfIChwcmV2aW91c2x5IGtub3duIGFzIG1lbHRpbmcpCmBgYHtyfQpnZW5lX2V4cHJlc3Npb24KYGBgCgoKIyMgZ2F0aGVyKCkKdXNlZCB3aGVuIGNvbHVtbiBuYW1lcyBhcmUgbm90IG5hbWVzIG9mIHZhcmlhYmxlcywgYnV0IF92YWx1ZXNfIG9mIGEgdmFyaWFibGUgKGUuZy4gdGltZSkuCm1ha2VzIHRhYmxlcyBfbG9uZ2VyXyBhbmQgX3NraW5ueV8gKHByZXZpb3VzbHkga25vd24gYXMgbWVsdGluZykKYGBge3J9CmdhdGhlcihnZW5lX2V4cHJlc3Npb24sIHQwOnQyLCBrZXkgPSAidGltZXBvaW50IiwgdmFsdWUgPSAiZXhwcmVzc2lvbiIpCmBgYAoKIyMgc3ByZWFkKCkKU3ByZWFkaW5nIGlzIHRoZSBvcHBvc2l0ZSBvZiBnYXRoZXJpbmcuICBVc2VkIHdoZW4gYW4gb2JzZXJ2YXRpb24gaXMgc2NhdHRlcmVkIGFjcm9zcyBtdWx0aXBsZSByb3dzLgpgc3ByZWFkKClgIG1ha2VzIHRhYmxlcyBfc2hvcnRlcl8gYW5kIF93aWRlcl8KYGBge3J9CmZhY3NfZGF0YQpgYGAKCiMjIHNwcmVhZCgpClNwcmVhZGluZyBpcyB0aGUgb3Bwb3NpdGUgb2YgZ2F0aGVyaW5nLiAgVXNlZCB3aGVuIGFuIG9ic2VydmF0aW9uIGlzIHNjYXR0ZXJlZCBhY3Jvc3MgbXVsdGlwbGUgcm93cy4KYHNwcmVhZCgpYCBtYWtlcyB0YWJsZXMgX3Nob3J0ZXJfIGFuZCBfd2lkZXJfCmBgYHtyfQpzcHJlYWQoZmFjc19kYXRhLCBrZXkgPSBNZWFzdXJlLCB2YWx1ZSA9IFZhbHVlKQpgYGAKCiMjIEV4ZXJjaXNlIDEKcHV0IHRhYmxlMiBpbiB0aWR5IGZvcm1hdApgYGB7cn0KdGFibGUyWzE6NixdICN0cnVuY2F0ZWQgc28gaXQgZml0cyBvbiBzbGlkZQpgYGAKCmBgYHtyLCBlY2hvPUZBTFNFfQpzcHJlYWQodGFibGUyLCBrZXkgPSB0eXBlLCB2YWx1ZSA9IGNvdW50KQpgYGAKCiMjIEV4ZXJjaXNlIDIKY29udmVydCB0YWJsZTEgdG8gdGFibGUyCmBgYHtyfQp0YWJsZTEKYGBgCgpgYGB7ciBlY2hvPUZBTFNFfQpoZWFkKHRhYmxlMikKYGBgCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KZ2F0aGVyKHRhYmxlMSwgYyhjb3VudHJ5LCB5ZWFyLCBjYXNlcywgcG9wdWxhdGlvbiksIGtleSA9ICJ5ZWFyIiwgdmFsdWUgPSAiY291bnQiKQpgYGAKCgojIyBkYXRhIGltcG9ydApgcmVhZHIoKWAgaGFzIG51bWVyb3VzIGZ1bmN0aW9ucyBmb3IgcmVhZGluZyBpbiBmaWxlcyBhcyB0aWJibGVzCgorICdyZWFkX2NzdigpYCBmb3IgY29tbWEtZGVsaW1pdGVkCisgYHJlYWRfdHN2KClgIGZvciB0YWItZGVsaW1pdGVkCmBgYHtyIGRhdGF9CmdmZiA8LSByZWFkX2RlbGltKCJTYWNjaGFyb215Y2VzX2NlcmV2aXNpYWUuUjY0LTEtMS4zNC5nZmYzIiwgCiAgICAiXHQiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIGNvbF9uYW1lcyA9IEZBTFNFLCAKICAgIGNvbW1lbnQgPSAiIyIsIHRyaW1fd3MgPSBUUlVFLCBza2lwID0gMjQpCmBgYAoKIyNQZWFrIGF0IGRhdGEKYSB0aWJibGUgaXMgYSBkYXRhZnJhbWUKYGBge3J9CmhlYWQoZ2ZmKQpgYGAKCiMjTG9vayBhdCBkYXRhIHN0cnVjdHVyZSB3aXRoIHN0cigpCmBgYHtyfQpzdHIoZ2ZmKQpgYGAKCiMjTG9vayBhdCB0aGUgZGF0YSB0aGUgdGlkeXZlcnNlIHdheQojIyN1c2luZyBnbGltcHNlKCkKCmBgYHtyfQpnbGltcHNlKGdmZikKYGBgCgojI0Fzc2lnbiBtZWFuaW5nZnVsIG5hbWVzIHRvIGNvbHVtbnMKc2FtZSBhcHByb2FjaCBhcyBuYW1pbmcgZGF0YWZyYW1lIGNvbHVtbnMgaW4gYmFzZSBSCmBgYHtyfQpuYW1lcyhnZmYpIDwtIGMoImNocm9tb3NvbWUiLCAKICAgICAgICAgICAgICAgICJzb3VyY2UiLCAKICAgICAgICAgICAgICAgICJmZWF0dXJlIiwgCiAgICAgICAgICAgICAgICAic3RhcnQiLAogICAgICAgICAgICAgICAgInN0b3AiLCAKICAgICAgICAgICAgICAgICJ1bmtub3duMSIsCiAgICAgICAgICAgICAgICAic3RyYW5kIiwKICAgICAgICAgICAgICAgICJ1bmtub3duMiIsCiAgICAgICAgICAgICAgICAiaW5mbyIKICAgICAgICAgICAgICAgICkKYGBgCgojI0RhdGFmcmFtZSBub3cgaGFzIG1lYW5pbmdmdWwgbmFtZXMKbm90ZSB0aGF0IHRpZHl2ZXJzZSB0cmllcyB0byBndWVzcyBkYXRhIHR5cGUKYGBge3J9CmdsaW1wc2UoZ2ZmKQpgYGAKCiMjYXNzaWduIGNvbHVtbnMgcHJvcGVyIGRhdGF0eXBlcwphc3NpZ25pbmcgY29ycmVjdCBkYXRhIHR5cGUgaXMgY3JpdGljYWwgZm9yIGFubGF5c2VzIGFuZCBwbG90dGluZyB3aXRoIGdncGxvdCgpCmBgYHtyfQpnZmYkZmVhdHVyZSA9IGFzLmZhY3RvcihnZmYkZmVhdHVyZSkKZ2ZmJGNocm9tb3NvbWUgPSBhcy5mYWN0b3IoZ2ZmJGNocm9tb3NvbWUpCmdmZiRzdHJhbmQgPSBhcy5mYWN0b3IoZ2ZmJHN0cmFuZCkKZ2xpbXBzZShnZmYpCmBgYAoKIyNTZWxlY3QgY29sdW1ucyB1c2luZyBgc2VsZWN0KClgCmBgYHtyfQpnZmYgPC0gc2VsZWN0KGdmZiwgYygiY2hyb21vc29tZSIsICJmZWF0dXJlIiwgInN0YXJ0IiwgInN0b3AiLCAic3RyYW5kIikpCmhlYWQoZ2ZmKQpgYGAKCiMjQWRkIGEgY29sdW1uIHdpdGggYG11dGF0ZSgpYApgYGB7cn0KZ2ZmIDwtIG11dGF0ZShnZmYsIGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKQpoZWFkKGdmZikKYGBgCgojI1NvcnQgdGliYmxlIGJ5IGNvbHVtbiB3aXRoIGBhcnJhbmdlKClgCndyaXRpbmcgYGRwbHlyOjphcnJhbmdlYCBzcGVjaWZpZXMgdGhlIHBhY2thZ2UgYW5kIGZ1bmN0aW9uIApgYGB7cn0KZHBseXI6OmFycmFuZ2UoZ2ZmLGxlbmd0aCkKYGBgCgojI1NvcnQgYnkgZmVhdHVyZSBzaXplIHdpdGggYXJyYW5nZSgpCnNvcnQgbGFyZ2VzdCB0byBzbWFsbGVzdCB1c2luZyBgLWAKYGBge3J9CmRwbHlyOjphcnJhbmdlKGdmZiwtbGVuZ3RoKQpgYGAKCiMjQW5hbHl6ZSB3aXRoIGBzdW1tYXJpemUoKWAKdGhpcyBjcmVhdGVzIGEgbmV3IHRpYmJsZS9kYXRhZnJhbWUgCmBgYHtyfQpzdW1tYXJpc2UoZ2ZmLCBtZWFuID0gbWVhbihsZW5ndGgpLCBzZCA9IHNkKGxlbmd0aCksIG1pbiA9IG1pbihsZW5ndGgpLCBtYXggPSBtYXgobGVuZ3RoKSwgbiA9IG4oKSkKYGBgCgojI0FuYWx5emUgd2l0aCBgc3VtbWFyaXplKClgCnRoZSBmdW5jdGlvbiBgbigpYCBjb3VudHMgaG93IG1hbnkgb2JzZXJ2YXRpb25zIHRoZWlyIGFyZQpgYGB7cn0Kc3VtbWFyaXNlKGdmZiwgbWVhbiA9IG1lYW4obGVuZ3RoKSwgc2QgPSBzZChsZW5ndGgpLCBtaW4gPSBtaW4obGVuZ3RoKSwgbWF4ID0gbWF4KGxlbmd0aCksIG4gPSBuKCkpCmBgYAoKIyN1c2luZyB0aGUgcGlwZTogYCU+JWAKKyB0aGUgcGlwZSBpcyBmcm9tIHRoZSBgbWFnaXR0cmAgcGFja2FnZQorIHNhbWUgYXMgYHxgIGluIHVuaXgKKyBhbGxvd3MgeW91IHRvIHBlcmZvcm0gbXVsdGlwbGUgc2VxdWVudGlhbCBmdW5jdGlvbnMKKyBwcm9ub3VuY2VkIF9fdGhlbl9fCisgdW5sZWFzaGVzIHRoZSB0cnVlIHBvd2VyIG9mIHRpZHl2ZXJzZSBmdW5jdGlvbnMKCgojI3VzaW5nIHRoZSBwaXBlOiBgJT4lYAojIyNzdWJzZXQgZGF0YSB3aXRoIGBncm91cF9ieSgpYApgYGB7cn0KZ2ZmICU+JQptdXRhdGUobGVuZ3RoID0gYWJzKHN0YXJ0IC0gc3RvcCkpICU+JQpncm91cF9ieShmZWF0dXJlKSAlPiUKc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGxlbmd0aCksIHNkID0gc2QobGVuZ3RoKSwgbWluID0gbWluKGxlbmd0aCksIG1heCA9IG1heChsZW5ndGgpLCBuID0gbigpKQpgYGAKCiMjRmlsdGVyIHJvd3Mgd2l0aCBgZmlsdGVyKClgCmBgYHtyfQpnZmYgJT4lCmZpbHRlcihmZWF0dXJlICE9ICJtUk5BIiAmIGZlYXR1cmUgIT0gInJSTkFfZ2VuZSIgJiBmZWF0dXJlICE9ICJzbm9STkFfZ2VuZSImIGZlYXR1cmUgIT0gInNuUk5BX2dlbmUiKSAlPiUKbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKZ3JvdXBfYnkoZmVhdHVyZSkgJT4lCnN1bW1hcmlzZShtZWFuID0gbWVhbihsZW5ndGgpLCBzZCA9IHNkKGxlbmd0aCksIG1pbiA9IG1pbihsZW5ndGgpLCBtYXggPSBtYXgobGVuZ3RoKSwgbiA9IG4oKSkgCmBgYAoKIyNQYXNzIGRhdGFmcmFtZSB0byBnZ3Bsb3QgZm9yIHBsb3R0aW5nCiMjIyBOT1RFOiBnZ3Bsb3QgdXNlcyBgK2Agbm90IHRoZSBwaXBlIGAlPiVgCmBgYHtyLCBmaWcuaGVpZ2h0PTIsIGZpZy53aWR0aD0yfQpnZmYgJT4lCmZpbHRlcihmZWF0dXJlID09IGMoIkNEUyIpKSAlPiUKZ2dwbG90KGFlcyh4ID0gbGVuZ3RoKSkgKyAKICAgICAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKQpgYGAKCiMjRXhlcmNpc2UgMwpwbG90IHRoZSBwb3B1bGF0aW9uIG9mIGVhY2ggY291bnRyeSBpbiAxOTk5IHVzaW5nIGAlPiUgYW5kICBgZ2dwbG90KClgCmBgYHtyfQp0YWJsZTEKYGBgCgojI0V4ZXJjaXNlIDMKYGBge3IgZWNobz1GQUxTRX0KdGFibGUxICU+JQogICAgICAgIHNlbGVjdCgtY2FzZXMpICU+JQogICAgICAgIGZpbHRlcih5ZWFyID09ICIxOTk5IikgJT4lCiAgICAgICAgZ2dwbG90KG1hcHBpbmcgPSBhZXMoeCA9IGNvdW50cnksIGZpbGwgPSBjb3VudHJ5LCB5ID0gcG9wdWxhdGlvbikpICsKICAgICAgICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpCmBgYAoKCiMjU3RyaW5nIG1hbmlwdWxhdGlvbiB3aXRoIGBzdHJpbmdyKClgCiMjIyBIb3cgZG8gd2UgZ2V0IHRoZSBnZW5lIG5hbWVzPwpgYGB7ciwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KZ2ZmIDwtIHJlYWRfZGVsaW0oIlNhY2NoYXJvbXljZXNfY2VyZXZpc2lhZS5SNjQtMS0xLjM0LmdmZjMiLCAKICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgY29sX25hbWVzID0gRkFMU0UsIAogICAgY29tbWVudCA9ICIjIiwgdHJpbV93cyA9IFRSVUUsIHNraXAgPSAyNCkKCm5hbWVzKGdmZikgPC0gYygiY2hyb21vc29tZSIsIAogICAgICAgICAgICAgICAgInNvdXJjZSIsIAogICAgICAgICAgICAgICAgImZlYXR1cmUiLCAKICAgICAgICAgICAgICAgICJzdGFydCIsCiAgICAgICAgICAgICAgICAic3RvcCIsIAogICAgICAgICAgICAgICAgInVua25vd24xIiwKICAgICAgICAgICAgICAgICJzdHJhbmQiLAogICAgICAgICAgICAgICAgInVua25vd24yIiwKICAgICAgICAgICAgICAgICJpbmZvIgogICAgICAgICAgICAgICAgKQoKZ2ZmJGZlYXR1cmUgPSBhcy5mYWN0b3IoZ2ZmJGZlYXR1cmUpCmdmZiRjaHJvbW9zb21lID0gYXMuZmFjdG9yKGdmZiRjaHJvbW9zb21lKQpnZmYkc3RyYW5kID0gYXMuZmFjdG9yKGdmZiRzdHJhbmQpCmBgYAoKYGBge3J9CnNlbGVjdChnZmYsIGluZm8pCmBgYAoKCiMjU2VwYXJhdGUgdmFsdWVzIGluIGNvbHVtbiB3aXRoIGBzZXBhcmF0ZSgpYCB7LnNtYWxsZXJ9CmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpnZmYgJT4lCm11dGF0ZShsZW5ndGggPSBhYnMoc3RhcnQgLSBzdG9wKSkgJT4lCmZpbHRlcihmZWF0dXJlID09ICJnZW5lIikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvIiwgaW50byA9IGMoImluZm8xIiwgImluZm8yIiwgImluZm8zIiwgImluZm80IiwgImluZm81IiksIHNlcCA9ICI7IiwgZXh0cmEgPSAibWVyZ2UiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8xIiwgaW50byA9IGMoImp1bmsiLCAiU3lzdGVtYXRpY19uYW1lIiksIHNlcCA9ICI6IikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvMiIsIGludG8gPSBjKCJqdW5rMiIsICJHZW5lIiksIHNlcCA9ICJOYW1lPSIpICU+JQpzZXBhcmF0ZShjb2wgPSAiaW5mbzMiLCBpbnRvID0gYygianVuazMiLCAiRGVzY3JpcHRpb24xIiksIHNlcCA9ICJkZXNjcmlwdGlvbj0iKSAlPiUgIApzZXBhcmF0ZShjb2wgPSAiaW5mbzQiLCBpbnRvID0gYygianVuazQiLCAiRGVzY3JpcHRpb24yIiksIHNlcCA9ICJkZXNjcmlwdGlvbj0iKSAlPiUKc2VsZWN0KGMoRGVzY3JpcHRpb24xLCBEZXNjcmlwdGlvbjIpKQpgYGAKCiMjQ29tYmluZSBjb2x1bW5zIHdpdGggYHVuaXRlKClgIHsuc21hbGxlcn0KYGBge3IsIHdhcm5pbmc9RkFMU0V9CmdmZiAlPiUKbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKZmlsdGVyKGZlYXR1cmUgPT0gImdlbmUiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8iLCBpbnRvID0gYygiaW5mbzEiLCAiaW5mbzIiLCAiaW5mbzMiLCAiaW5mbzQiLCAiaW5mbzUiKSwgc2VwID0gIjsiLCBleHRyYSA9ICJtZXJnZSIpICU+JQpzZXBhcmF0ZShjb2wgPSAiaW5mbzEiLCBpbnRvID0gYygianVuayIsICJTeXN0ZW1hdGljX25hbWUiKSwgc2VwID0gIjoiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8yIiwgaW50byA9IGMoImp1bmsyIiwgIkdlbmUiKSwgc2VwID0gIk5hbWU9IikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvMyIsIGludG8gPSBjKCJqdW5rMyIsICJEZXNjcmlwdGlvbjEiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgCnNlcGFyYXRlKGNvbCA9ICJpbmZvNCIsIGludG8gPSBjKCJqdW5rNCIsICJEZXNjcmlwdGlvbjIiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgICAKdW5pdGUoRGVzY3JpcHRpb24sIERlc2NyaXB0aW9uMSwgRGVzY3JpcHRpb24yLCBzZXAgPSAiIikgJT4lCnNlbGVjdChjKERlc2NyaXB0aW9uKSkKYGBgCgojI1NhdmUgdG8gYSBuZXcgdmFyaWFibGUgey5zbWFsbGVyfQpBIGdlbmVyYWwgcnVsZSBpcyBpZiB5b3UgYXJlIHBpcGluZyBtb3JlIHRoYW4gMTAgc3RlcHMgc2F2ZSBhcyBhIG5ldyB2YXJpYWJsZQpgYGB7ciwgd2FybmluZz1GQUxTRX0KZ2ZmX2NsZWFuIDwtIGdmZiAlPiUKbXV0YXRlKGxlbmd0aCA9IGFicyhzdGFydCAtIHN0b3ApKSAlPiUKZmlsdGVyKGZlYXR1cmUgPT0gImdlbmUiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8iLCBpbnRvID0gYygiaW5mbzEiLCAiaW5mbzIiLCAiaW5mbzMiLCAiaW5mbzQiLCAiaW5mbzUiKSwgc2VwID0gIjsiLCBleHRyYSA9ICJtZXJnZSIpICU+JQpzZXBhcmF0ZShjb2wgPSAiaW5mbzEiLCBpbnRvID0gYygianVuayIsICJTeXN0ZW1hdGljX25hbWUiKSwgc2VwID0gIjoiKSAlPiUKc2VwYXJhdGUoY29sID0gImluZm8yIiwgaW50byA9IGMoImp1bmsyIiwgIkdlbmUiKSwgc2VwID0gIk5hbWU9IikgJT4lCnNlcGFyYXRlKGNvbCA9ICJpbmZvMyIsIGludG8gPSBjKCJqdW5rMyIsICJEZXNjcmlwdGlvbjEiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgCnNlcGFyYXRlKGNvbCA9ICJpbmZvNCIsIGludG8gPSBjKCJqdW5rNCIsICJEZXNjcmlwdGlvbjIiKSwgc2VwID0gImRlc2NyaXB0aW9uPSIpICU+JSAgICAKdW5pdGUoRGVzY3JpcHRpb24sIERlc2NyaXB0aW9uMSwgRGVzY3JpcHRpb24yLCBzZXAgPSAiIikgJT4lCnNlbGVjdChjKFN5c3RlbWF0aWNfbmFtZSwgR2VuZSwgRGVzY3JpcHRpb24pKQpgYGAKCiMjQ2xlYW4gdXAgc3RyaW5ncyB3aXRoIGBzdHJpbmdyKClgIHsuc21hbGxlcn0KYGBge3IsIHdhcm5pbmc9RkFMU0V9CmdmZl9jbGVhbiREZXNjcmlwdGlvbiA8LSBzdHJfcmVwbGFjZV9hbGwoZ2ZmX2NsZWFuJERlc2NyaXB0aW9uLCAiJTNCIiwgIiIpCmdmZl9jbGVhbiREZXNjcmlwdGlvbiA8LSBzdHJfcmVwbGFjZV9hbGwoZ2ZmX2NsZWFuJERlc2NyaXB0aW9uLCAiJTJDIiwgIiIpCmdmZl9jbGVhbiREZXNjcmlwdGlvbiA8LSBzdHJfcmVwbGFjZV9hbGwoZ2ZmX2NsZWFuJERlc2NyaXB0aW9uLCAiXk5BIiwgIiIpCgpnZmZfY2xlYW4gJT4lCnNlbGVjdChjKERlc2NyaXB0aW9uKSkKCmBgYAoKIyNXcml0ZSBmaWxlCmBgYHtyfQp3cml0ZV90c3YoZ2ZmX2NsZWFuLCAiWWVhc3RfZ2VuZXMudHh0IiwgbmEgPSAiTkEiKQpgYGAKCgoKIyMgSG93IGRvIHdlIGNvbWJpbmUgdGFibGVzPwoKIyMjIE11dGF0aW5nIGpvaW5zCkEgbXV0YXRpbmcgam9pbiBhbGxvd3MgeW91IGNvbWJpbmUgdmFyaWFibGVzIGZyb20gdHdvIHRhYmxlcyBieSBtYXRjaGl1bmcgb2JzZXJ2YXRpb25zIGJ5IHRoZWlyIGtleXMKCiMjIyMxLiBJbm5lciBKb2luCm1hdGNoZXMgcGFpcnMgb2Ygb2JzZXJ2YXRpb24gZnJvbSB0d28gdGFibGVzIHdoZW5ldmVyIHRoZWlyIGtleXMgYXJlIGVxdWFsCgojIyMjMi4gT3V0ZXIgam9pbgprZWVwcyBvYnNlcnZhdGlvbnMgdGhhdCBhcHBlYXIgaW4gYXQgbGVhc3Qgb25lIG9mIHRoZSB0YWJsZXMKCisgbGVmdCBqb2luIGtlZXBzIGFsbCB0aGUgb2JzZXJ2YXRpb25zIGluIHggKHNob3VsZCBiZSB0aGUgZGVmYXVsdCkKKyByaWdodCBqb2luIGtlZXBzIGFsbCB0aGUgb2JzZXJ2YXRpb25zIGluIHkKKyBmdWxsIGpvaW4ga2VlcHMgYWxsIG9ic2VydmF0aW9ucyBpbiB4IGFuZCB5CgojIyNGaWx0ZXJpbmcgam9pbnMKYWZmZWN0cyAoZmlsdGVycykgdGhlIG9ic2VydmF0aW9ucyBub3QgdGhlIHZhcmlhYmxlcwoKKyBzZW1pX2pvaW4oeCwgeSkga2VlcHMgYWxsIG9ic2VydmF0aW9ucyBpbiB4IHRoYXQgaGF2ZSBhIG1hdGNoIGluIHkKKyBhbnRpX2pvaW4oeCwgeSkgZHJvcHMgYWxsIG9ic2VydmF0aW9ucyBpbiB4IHRoYXQgaGF2ZSBhIG1hdGNoIGluIHkKCiMjRGF0YXNldCBtdXN0IGNvbnRhaW4gY29tbW9uIHZhbHVlcyAoZ2VuZSBuYW1lcykgey5zbWFsbGVyfQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpkYXRhIDwtICByZWFkX2RlbGltKCJOZXltb3Rpbl9UYWJsZV9TMS50eHQiLCAKICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgY29sX25hbWVzID0gVFJVRSwgCiAgICBjb21tZW50ID0gIiMiLCB0cmltX3dzID0gVFJVRSkKYGBgCgpgYGB7cn0Kc3RyKGRhdGEpCmBgYAoKIyNGaWxlIHRvIGpvaW4gd2l0aApgYGB7cn0Kc3RyKGdmZl9jbGVhbikKYGBgCgojI0pvaW5pbmcgZGF0YQpgZHBseXI6OmxlZnRfam9pbihhLCBiLCBieSA9ICJ4MSIpYApKb2luIG1hdGNoaW5nIHJvd3MgZnJvbSBiIHRvIGEuCgpgYGB7cn0KbGVmdF9qb2luKGdmZl9jbGVhbiwgZGF0YSwgYnkgPSBjKCJTeXN0ZW1hdGljX25hbWUiID0gIlN5c3QiKSkgJT4lCiAgICAgICAgc3RyKCkKYGBgCgojIyBFeGNlcmNpc2U0CgojIyB0aWR5IGRhdGEgZG9uJ3QgYWxsb3cgY29ycmVsYXRpb24gcGxvdHMKCmBgYHtyfQp0aWR5X2dlbmVfZXhwcmVzc2lvbiA8LSBnZW5lX2V4cHJlc3Npb24gJT4lCiAgICAgICAgZ2F0aGVyKHQwOnQyLCBrZXkgPSAidGltZXBvaW50IiwgdmFsdWUgPSAiZXhwcmVzc2lvbiIpCnRpZHlfZ2VuZV9leHByZXNzaW9uCmBgYAoKIyMgUmVhcnJhbmdlIHRoZSBkYXRhIAoKYGBge3IgZWNobz1GQUxTRX0KdGlkeV9nZW5lX2V4cHJlc3Npb24gJT4lCiAgICAgICAgc3ByZWFkKGtleSA9IHRpbWVwb2ludCwgdmFsdWUgPSBleHByZXNzaW9uKQpgYGAKCiMjIFBsb3QgY29ycmVsYXRpb24gYmV0d2VlbiB0MCBhbmQgdDEKCmBgYHtyIGVjaG89RkFMU0V9CnRpZHlfZ2VuZV9leHByZXNzaW9uICU+JQogICAgICAgIHNwcmVhZChrZXkgPSB0aW1lcG9pbnQsIHZhbHVlID0gZXhwcmVzc2lvbikgJT4lCiAgICAgICAgZ2dwbG90KGFlcyh4ID0gdDAsIHkgPSB0MSkpICsgCiAgICAgICAgZ2VvbV9wb2ludCgpCmBgYAoKIyMgRXhjZXJjaXNlNQoKKyBHZXQgdGhlIGdmZiBmaWxlIGZvciB5b3VyIGZhdm9yaXRlIG9yZ2FuaXNtIGZyb20gdGhlIFNHUgorIFRpZHkgdGhlIGdmZgorIHBsb3QgZGlzdHJpYnV0aW9uIG9mIGZlYXR1cmVzIHBlciBjaHJvbW9zb21lCgoKYGBge3IgaW5jbHVkZT1GQUxTRX0KZ2ZmIDwtIHJlYWRfZGVsaW0oIlNhY2NoYXJvbXljZXNfY2VyZXZpc2lhZS5SNjQtMS0xLjM0LmdmZjMiLCAKICAgICJcdCIsIGVzY2FwZV9kb3VibGUgPSBGQUxTRSwgY29sX25hbWVzID0gRkFMU0UsIAogICAgY29tbWVudCA9ICIjIiwgdHJpbV93cyA9IFRSVUUsIHNraXAgPSAyNCkKbmFtZXMoZ2ZmKSA8LSBjKCJjaHJvbW9zb21lIiwgCiAgICAgICAgICAgICAgICAic291cmNlIiwgCiAgICAgICAgICAgICAgICAiZmVhdHVyZSIsIAogICAgICAgICAgICAgICAgInN0YXJ0IiwKICAgICAgICAgICAgICAgICJzdG9wIiwgCiAgICAgICAgICAgICAgICAidW5rbm93bjEiLAogICAgICAgICAgICAgICAgInN0cmFuZCIsCiAgICAgICAgICAgICAgICAidW5rbm93bjIiLAogICAgICAgICAgICAgICAgImluZm8iCiAgICAgICAgICAgICAgICApCiNjb3JyZWN0IGRhdGEgdHlwZXMKZ2ZmJGZlYXR1cmUgPSBhcy5mYWN0b3IoZ2ZmJGZlYXR1cmUpCmdmZiRjaHJvbW9zb21lID0gYXMuZmFjdG9yKGdmZiRjaHJvbW9zb21lKQpnZmYkc3RyYW5kID0gYXMuZmFjdG9yKGdmZiRzdHJhbmQpCgp5ZWFzdF9mZWF0dXJlcyA8LSBnZmYgJT4lCiAgICAgICAgc2VsZWN0KGNocm9tb3NvbWUsIGZlYXR1cmUsIHN0YXJ0LCBzdG9wLCBzdHJhbmQpICU+JQogICAgICAgIG11dGF0ZShsZW5ndGggPSBhYnMoc3RhcnQgLSBzdG9wKSkgJT4lCiAgICAgICAgZmlsdGVyKGZlYXR1cmUgPT0gIkNEUyIgfCBmZWF0dXJlID09ICJyUk5BIiB8IGZlYXR1cmUgPT0gInNub1JOQSIgfCBmZWF0dXJlID09ICJzblJOQSIgfCBmZWF0dXJlID09ICJ0Uk5BX2dlbmUiKQpgYGAKCiMjZXhhbXBsZQoKYGBge3IsIGZpZy53aWR0aD02LCBmaWcuYXNwID0gMC42MTgsIG91dC53aWR0aCA9ICI3MCUiLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0KZ2dwbG90KGRhdGEgPSB5ZWFzdF9mZWF0dXJlcywgbWFwcGluZyA9IGFlcyh4ID0gY2hyb21vc29tZSwgZmlsbCA9IGZlYXR1cmUpKSArCiAgICAgICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQpgYGAKCgoKIyNSZXNvdXJjZXMKCiogQmFzZSBSIHRvIHRpZHl2ZXJzZSAgaHR0cDovL3d3dy5zaWduaWZpY2FudGRpZ2l0cy5vcmcvMjAxNy8xMC9zd2l0Y2hpbmctZnJvbS1iYXNlLXItdG8tdGlkeXZlcnNlLwoqIENoZWF0c2hlZXQ6IGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAyL2RhdGEtd3JhbmdsaW5nLWNoZWF0c2hlZXQucGRmCgoK