Basic commands, operators and data types
Most of the sections in this document are structured as follows:
- pseudocode
- R code examples
- results or running the R code examples
In pseudocode throughout this document:
- syntax like
<something>
is used as a descriptive palceholder for an appropriate, context-specific identifier or another piece of code in lines of an actual R script
- syntax like
"<something>"
is used as a descriptive palceholder for an appropriate, context-specific string in lines of an actual R script
- leading ‘+’ character in multi-line pseudocommands is used for indentation purposes only; it should be omitted in actual R code
Print a line of text in the console
print("Hi :)")
Assignment statement
x <- <something>
x <- 2
x
Manipulating objects in the workspace
ls() # list all objects in memory
rm(<o1>, <o2>, <o3>, ...) # remove one or more objects from memory by their names
rm(list = ls()) # remove all objects from memory (usually not recommended)
ls()
rm(x, y)
Operators
- + Add, 2 + 3 = 5
- - Subtract, 5 - 2 = 3
- * Multiply, 2 * 3 = 6
- / Divide, 6 / 2 = 3
- ^ Exponent, 2 ^ 3 = 8
- %% Modulus operator, 9%%2 = 1
- %/% Integer division, 9 %/% 2 = 4
- < Less than
- > Greater than
- = Equal to
- <= Less than or equal to
- >= Greater than or equal to
- != Not equal to
- ! Not
- | OR
- & And
Expressions
E.g., x / y - z^2
etc.
3.4 / 2 + 7^2
Vectors
<y> <- c(<something1>, <something2>, <something3>, ...)
<y> <- rep(<something>, <times>)
<y> <- <int1>:<int2>
<y> <- seq(<value1>, <value2>, by = <step>)
The index of the first element in a vector is 1, not 0.
y <- c(1, 2, 3)
z <- c(1.2, 3)
t <- 2:6
w <- seq(3.2, 4.7, by = 0.2)
w[3]
w[]
w
Matrices
<m> <- matrix(c(3, 5, 7, 1, 9, 4), nrow = 3, ncol = 2, byrow = TRUE)
<m>.nrow <- nrow(<m>) # number of rows
<m>.ncol <- ncol(<m>) # number of columns
<m> <- t(<m>) # transpose <m>
<m>[2,3]
<m>[2]
<m>[2, ]
a <- matrix(8:1, nrow = 2, ncol = 4, byrow = TRUE)
a
a.nrow <- nrow(a)
a.nrow
a <- t(a)
a
a[1,2]
a[2, ]
a[2]
a[]
Lists
Ordered collections of elements of different types.
<list> <- list(<e1.name> = <e1>, <e2.name> = <e2>, <e3.name> = <e3>, ...)
<list>[[<index>]] # accessing list element by index, showing value only
<list>[<index>] # accessing list element by index, showing both name and value
<list>$<element.name> # accessing list element by its name
is.list(<something>) # Is <something> a list?
<combined.list> <- c(<list1>, <list2>, <list3>, ...) # list concatenation
names(<list>) # names of list elements
<list>[names(<list>) == <element.name>] # all elements of a list having the same name
unlist(<list>) # convert list into a named character vector
unlist(<list>, use.names = FALSE) # convert list into a character vector
append(<list>, # insert new element into an existing list, after index <n>
+ list(<e1.name> = <e>), # new element must be a list itself, that's why list(<e1.name> = <e>)
+ <n>) # <n> is optional; if omitted, new element is appended at the end
<list>[[<n>]] <- NULL # remove <n>th element from <list>
traveler1 <- list(adult = TRUE, passport = "P212123", age = 34)
traveler1
traveler1[[3]]
traveler2 <- list(adult = FALSE, passport = "P4567756", age = 14)
traveler2
traveler2$age
travelers <- c(traveler1, traveler2)
travelers
travelers[[3]]
travelers[[5]]
travelers[5]
is.list(travelers)
is.vector(travelers)
names(travelers)
travelers[names(travelers) == "age"]
unlist(travelers)
unlist(travelers, use.names = FALSE)
age.of.travelers <- unlist(travelers[names(travelers) == "age"], use.names = FALSE)
age.of.travelers
length(traveler1)
traveler1 <- append(traveler1, list(country = "AUS"), 2)
length(traveler1)
traveler1
traveler1[[3]] <- NULL
length(traveler1)
traveler1
Data types
Vector, factor, numeric, character, logical, data.frame, matrix, list, …
class(<something>) # data type
mode(something), typeof(<something>) # how a data item is internally stored in memory
class(a)
mode(a)
typeof(a)
typeof(2.3)
Factors
<b> <- c(1, 2, 2, 2, 3, 1, 1, 4, 5, 4)
<f> <- as.factor(b)
levels(<f>)
<f> <- factor(c(1, 2, 3))
<f> <- gl(3, # gl() "generates levels" (here 3), i.e. factors
+ 1, length = 10, labels = c("One", "Two", "Three")) # each level replicated 1 time, length(<f>) = 10
b <- c(1, 1, 1, 2, 1, 1, 1, 1, 5, 4)
b.as.factor <- as.factor(b)
levels(b.as.factor)
f <- gl(3, 1, length = 10, labels = c("One", "Two", "Three"))
f
f <- gl(3, 2, length = 10, labels = c("One", "Two", "Three"))
f
meal = factor(c("Lunch","Dinner"))
meal
meal = factor(c("Lunch","Dinner"), levels=c("Lunch","Dinner"))
meal
Dataframes
<dataframe> <- as.data.frame(<matrix>)
str(<dataframe>)
a.data.frame <- as.data.frame(a)
a.data.frame
str(a.data.frame)
ggplot2
# install.packages("ggplot2")
library(ggplot2)
Data to plot (used in the examples below):
actor.xy <- data.frame(year = factor(c(2014, 2015, 2016, 2017)), movies = (c(2, 3, 2, 1)))
actor.xy
Bar graphs
ggplot(data = <dataframe>,
+ aes(x = <column 1>, y = <column 2>, fill = <column 1>)) + # fill = <column 1> is optional; no y for counts
+ geom_bar(stat = "identity") + # "identity" for values, "count" for counts
+ xlab("<x-axis label>") + ylab("<y-axis label>") +
+ ggtitle("<graph title>")
actor.plot.set <- ggplot(data = actor.xy, aes(x = year, y = movies, fill = year))
actor.plot.set + geom_bar(stat = "identity")
actor.plot.set +
geom_bar(stat = "identity") +
xlab("recent years") + ylab("# of movies") +
ggtitle("XY's recent movies")
Line graphs
ggplot(data = <dataframe>,
+ aes(x = <column 1>, y = <column 2>, group = 1)) + # group = 1: one line, all points connected
+ geom_line(colour = "<colour>", linetype = "<linetype>", size = <line thickness>) +
+ geom_point(colour="<colour>", size = <point size>, shape = <point shape>, fill = "<point fill colour>") +
+ xlab("<x-axis label>") + ylab("<y-axis label>") +
+ ggtitle("<graph title>")
All parameters in geom_line() and in geom_point() are optional.
The defaults are: colour = "black", linetype = "solid", size = 1, shape = 21 (circle), fill = "black"
See <http://www.cookbook-r.com/Graphs/Colors_(ggplot2)/> for more information on colors.
See <http://www.cookbook-r.com/Graphs/Shapes_and_line_types/> for information on shapes and line types.
actor.plot.set <- ggplot(data = actor.xy, aes(x = year, y = movies, group = 1))
actor.plot.set + geom_line()
actor.plot.set + geom_line() + geom_point()
actor.plot.set +
geom_line(colour = "blue", linetype = "dotted", size = 2) +
geom_point(colour="green", size = 4, shape = 21, fill = "yellow")
# geom_point(color="green", size = 8, shape = 18, fill = "yellow")
Working with datasets / dataframes
Reading a dataset
<dataframe> <- read.csv("<filename>", stringsAsFactors = FALSE)
str(<dataframe>) # structure of <dataframe>, all variables/columns
head(<dataframe>) # the first few rows
tail(<dataframe>) # the last few rows
the.beatles.songs <- read.csv("The Beatles songs dataset, v1.csv",
stringsAsFactors = FALSE)
Examining a dataframe
str(<dataframe>) # structure of <dataframe>, all variables/columns
dim(<dataframe>) # showing dimensions (numbers of rows and columns) of a dataframe
names(<dataframe>) # showing column names
head(<dataframe>) # the first few rows
tail(<dataframe>) # the last few rows
<dataframe>[ , ] # the entire dataframe
<dataframe> # the entire dataframe
<dataframe>[<m>, ] # m-th row
<dataframe>[ ,<n>] # n-th column
summary(<dataframe>$<column>) # summarizing a variable/column values
fix(<dataframe>) # editing a dataframe
new.df <- edit(<dataframe>) # editing a dataframe and assigning the modified dataframe to another datavrame
str(the.beatles.songs)
dim(the.beatles.songs)
names(the.beatles.songs)
head(the.beatles.songs)
tail(the.beatles.songs)
the.beatles.songs[4, ]
the.beatles.songs[ ,2]
summary(the.beatles.songs$Duration)
summary(the.beatles.songs$Title)
summary(the.beatles.songs$Year)
# fix(the.beatles.songs)
# a.data.frame.1 <- edit(a.data.frame)
Examining a dataframe visually, with ggplot()
the.beatles.songs.clean <- read.csv("The Beatles songs dataset, v1, no NAs.csv",
stringsAsFactors = FALSE)
the.beatles.songs.clean$Year <- factor(the.beatles.songs.clean$Year) # because write.csv/read.csv produces int's
g1 <- ggplot(data = the.beatles.songs.clean, aes(x = Year, y = Duration, fill = Year))
g1 + geom_bar(stat = "identity")
g2 <- ggplot(data = the.beatles.songs.clean, aes(x = Year, fill = Year))
g2 + geom_bar(stat = "count") +
xlab("Year") + ylab("No. of songs") +
ggtitle("The number Beatles songs per year")
g3 <- ggplot(the.beatles.songs.clean[1:5, ], aes(x = Year, y = Duration, group = 1))
g3 + geom_line(color = "orange", size = 2, linetype = "longdash") +
geom_point(color = "red", shape = 25, size = 8, fill = "yellow")
Adding/Removing columns to/from a dataframe
<dataframe>$<new column name> <- <default value> # adding a new column (default values)
<dataframe>$<column name> <- NULL # removing a column
the.beatles.songs$Not.on.album <- FALSE
the.beatles.songs$Not.on.album <- NULL
the.beatles.songs$On.album <- FALSE
the.beatles.songs$On.album[the.beatles.songs$Album.debut != ""] <- TRUE
Adding new rows to a dataframe
In case of adding one new row, it must be a 1-line dataframe with the same column names. It is also possible to add an entire dataframe to the existing one (with the same column names).
<new row> <- data.frame(<column name 1> = <value 1>, <column name 2> = <value 2>,...)
<new data frame> <- rbind(<dataframe>, <new row>) # append new row to the end of the existing dataframe
<new data frame> <- rbind(<dataframe>[1:i, ], # insert new row in the middle
+ <new row>,
+ <dataframe>[(i + 1):nrow(<dataframe>), ])
new.song <- data.frame(the.beatles.songs[1, ])
the.beatles.songs <- rbind(the.beatles.songs, new.song)
the.beatles.songs <- rbind(the.beatles.songs[1:3, ], # Rstudio keeps the original row numbers in View()
new.song,
the.beatles.songs[4:nrow(the.beatles.songs), ])
Removing rows from a dataframe
<dataframe>[-i, ] # show dataframe without i-th row
<dataframe>[-c(i, j, k), ] # show dataframe without rows i, j, k
<dataframe> <- <dataframe>[-i, ] # remove i-th row from dataframe
<dataframe> <- <dataframe>[-c(i, j, k), ] # remove rows i, j, k from dataframe
<dataframe> <- <dataframe>[-(i:k), ] # remove rows i to k from dataframe
nrow(the.beatles.songs)
the.beatles.songs <- the.beatles.songs[-nrow(the.beatles.songs), ]
the.beatles.songs1 <- the.beatles.songs[-(305:310), ]
the.beatles.songs <- the.beatles.songs[-(1:304), ]
the.beatles.songs <- rbind(the.beatles.songs1, the.beatles.songs)
Changing column names
colnames(<dataframe>)[i] <- "<new name>"
colnames(the.beatles.songs)
which(colnames(the.beatles.songs) == "Genre")
colnames(the.beatles.songs)[which(colnames(the.beatles.songs) == "Genre")] <- "Song.genre"
colnames(the.beatles.songs)[6] <- "Genre"
Changing row names
rownames(<dataframe>)[i] <- "<new name>"
rownames(<dataframe>) <- c("<new name 1>", "<new name 2>",...)
rownames(<dataframe>) <- c(1, 2,...)
rownames(<dataframe>) <- list("<new name 1>", <numeric 2>,...)
rownames(the.beatles.songs) <- paste("song", 1:nrow(the.beatles.songs))
rownames(the.beatles.songs) <- c(1:nrow(the.beatles.songs))
Slicing and dicing dataframes
<selection> <- <dataframe>[<some rows>, <some columns>]
<selection> <- <dataframe>[i:k, c("<column 1>", "<column 2>",...)]
<indexes> <- with(<dataframe>, which(<condition; can be complex>)) # a with()-which() selection, like an SQL query
<selection> <- <dataframe>[<indexes>, ]
<selection> <- subset(<dataframe>, # subset() is much like SELECT... FROM... WHERE
+ <logical condition for the rows to return>,
+ <select statement for the columns to return>) # can be omitted;
+ # column names not prefixed by <dataframe>$
library(dplyr)
<selection> <- filter(<dataframe>, # filter() is from dplyr
+ <logical condition for the rows to return>) # can include column referencing,
+ # not-prefixed by <dataframe>$
selected.songs <- the.beatles.songs[1:5, c("Title", "Album.debut")]
# View(selected.songs)
indexes <- with(the.beatles.songs, which((Year == "1964") & (Lead.vocal != "McCartney")))
selected.songs <- the.beatles.songs[indexes, ]
songs.1958 <- subset(the.beatles.songs, Year == 1958, c("Title", "Album.debut"))
library(dplyr)
filter(the.beatles.songs,
as.integer(rownames(the.beatles.songs)) < 33 & Title == "12-Bar Original")
Shuffling rows/columns
<dataframe> <- <dataframe>[sample(nrow(<dataframe>)), ] # shuffle row-wise
<dataframe> <- <dataframe>[, sample(ncol(<dataframe>))] # shuffle column-wise
the.beatles.songs <- the.beatles.songs[sample(nrow(the.beatles.songs)), ]
the.beatles.songs <- the.beatles.songs[, sample(ncol(the.beatles.songs))]
Replacing selected values in a column
<selected var name> <- <dataframe>$<column> == <selected value>
<dataframe>$<column>[<selected var name>] <- <new value>
empty.album.debut <- the.beatles.songs$Album.debut == ""
empty.album.debut
the.beatles.songs$Album.debut[empty.album.debut] <- "empty"
the.beatles.songs$Album.debut[empty.album.debut] <- ""
Applying functions to all elements in rows/columns of a dataframe
apply(<dataframe>, <1 | 2>, <function(x) {...}>) # 1 | 2: apply function(x) by row | column
mapply(function(x, y, ...) {...}, <dataframe>$<column 1>, <dataframe>$<column 2>, ...)
+ # <dataframe>$<column 1> corresponds to x, <dataframe>$<column 2> corresponds to y, ...
+ # alternatively: <f> <- function(x, y, ...) {...}
+ mapply(<f>, <dataframe>$<column 1>, <dataframe>$<column 2>, ...)
+ # <f> is just the function name (!)
+ # <dataframe>$<column 1> corresponds to x, <dataframe>$<column 2> corresponds to y, ...,
+ # or can be columns from different dataframes, "independent" vectors,... (of the same length)
sapply(<vector>, FUN = function(x) {...}) # function(x): function to be applied to each element of <vector>
apply(the.beatles.songs[1, ], 1, function(x) {print(x)})
apply(the.beatles.songs[1, ], 2, function(x) {print(x)})
mapply(function(x, y) {print(x); print(y)},
the.beatles.songs[111:113, ]$Title,
the.beatles.songs[111:113, ]$Year)
sapply(the.beatles.songs[1, ], FUN = function(x) {print(x)})
Partitioning a dataframe
# install.packages('caret')
library(caret)
set.seed(<any specific int>) # allows for repeating the randomization process exactly
<indexes> <- createDataPartition(<dataframe>$<column>, p = 0.8, list = FALSE)
<partition 1> <- <dataframe>[<indexes>, ]
<partition 2> <- <dataframe>[-<indexes>, ]
library(caret)
set.seed(222)
indexes <- createDataPartition(the.beatles.songs$Year, p = 0.8, list = FALSE)
the.beatles.songs.p1 <- the.beatles.songs[indexes, ]
the.beatles.songs.p2 <- the.beatles.songs[-indexes, ]
Saving a dataset (modified or newly created dataset)
write.csv(x = <dataframe>, file = "<filename>", row.names = F) # do not include the row names (row numbers) column
saveRDS(object = <dataframe or another R object>, file = "<filename>") # save R object for the next session
<dataframe or another R object> <- readRDS(file = "<filename>") # restore R object in the next session
write.csv(the.beatles.songs.p2, "p2.csv", row.names = F)
saveRDS(the.beatles.songs.p2, "p2.RData")
p2 <- readRDS("p2.RData")
Data type conversion
# Covered above:
# b <- c(1, 2, 2, 2, 3, 1, 1, 4, 5, 4)
# b.as.factor <- as.factor(b)
# levels(b.as.factor)
# e.g., <dataframe> <- as.data.frame(<matrix>)
# str(<dataframe>)
Difference between character and factor vectors
summary(<character vector>)
summary(as.factor(<character vector>))
class(the.beatles.songs$Year)
summary(the.beatles.songs$Year)
summary(as.factor(the.beatles.songs$Year))
Convert numeric to factor
<dataframe>$<numeric column with few different values> <-
+ factor(<dataframe>$<numeric column with few different values>,
+ levels = c(0, 1, ..., k), labels = c("<l1>", "<l2>", ..., "<lk>"))
the.beatles.songs1 <- the.beatles.songs
the.beatles.songs1$Billboard.hit <- 0
the.beatles.songs1$Billboard.hit[!is.na(the.beatles.songs1$Top.50.Billboard)] <- 1
the.beatles.songs1$Billboard.hit <-
factor(the.beatles.songs1$Billboard.hit, levels = c(0,1), labels = c("N", "Y"))
class(the.beatles.songs1$Billboard.hit)
summary(the.beatles.songs1$Billboard.hit)
levels(the.beatles.songs1$Billboard.hit)
Examples
Fixing some values in the.beatles.songs$Year
summary(the.beatles.songs$Year)
summary(as.factor(the.beatles.songs$Year))
the.beatles.songs$Year[the.beatles.songs$Year == "196?"] <- "1969"
the.beatles.songs$Year[the.beatles.songs$Year == "1977/1994"] <- "1977"
the.beatles.songs$Year[the.beatles.songs$Year == "1980/1995"] <- "1980"
summary(as.factor(the.beatles.songs$Year))
Creating the.beatles.songs$Billboard.hit column as factor
the.beatles.songs$Billboard.hit <- 0
the.beatles.songs$Billboard.hit[!is.na(the.beatles.songs$Top.50.Billboard)] <- 1
the.beatles.songs$Billboard.hit <-
factor(the.beatles.songs$Billboard.hit, levels = c(0,1), labels = c("N", "Y"))
Working with tables
The table() function
table(<var>) # typically a factor or an integer var
table(the.beatles.songs1$Year)
table(the.beatles.songs1$Top.50.Billboard)
table(the.beatles.songs1$Billboard.hit)
table(the.beatles.songs1$Billboard.hit)[1]
x <- table(the.beatles.songs1$Billboard.hit)[1]
x
y <- as.numeric(x)
y
The prop.table() function
prop.table(table(<var>))
round(prop.table(table(<var>)), digits = <n>)
prop.table(table(the.beatles.songs1$Billboard.hit))
round(prop.table(table(the.beatles.songs1$Billboard.hit)), digits = 2)
Row and column margins
table(<var1>, <var2>) # <var1>, <var2>: usually factors or integers
table(<rows title> = <var1>, <columns title> = <var2>) # add common titles for rows/columns
prop.table(table(<var1>, <var2>), margin = 1) # all row margins are 1.0
prop.table(table(<var1>, <var2>), margin = 2) # all column margins are 1.0
table(the.beatles.songs$Billboard.hit, the.beatles.songs$Year)
table(Hit = the.beatles.songs$Billboard.hit, Year = the.beatles.songs$Year)
round(prop.table(table(the.beatles.songs$Billboard.hit, the.beatles.songs$Year), 1), digits = 2)
round(prop.table(table(the.beatles.songs$Billboard.hit, the.beatles.songs$Year), 2), digits = 2)
round(prop.table(table(the.beatles.songs$Billboard.hit, the.beatles.songs$Year)), digits = 2)
Example: converting the.beatles.songs$Year to factor and showing it in tables
factor(the.beatles.songs$Year)
the.beatles.songs$Year <- as.factor(the.beatles.songs$Year)
class(the.beatles.songs$Year)
summary(the.beatles.songs$Year)
prop.table((table(the.beatles.songs$Year)))
round(prop.table((table(the.beatles.songs$Year))), digits = 2)
The xtabs() function
xtabs(~<column 1> + <column 2>, <dataframe>)
xtabs(~Billboard.hit + Year, the.beatles.songs)
Working with vectors
Differences in initializing vectors and dataframe columns
<vector> <- rep(<value>, <times>)
<vector> <- <value>
<dataframe>$<column> <- rep(<value>, <times>)
<dataframe>$<column> <- <value>
v <- rep(0, 5)
v
v <- 2
v
df <- data.frame(a = c(1, 2, 3), b = c(4, 5, 6))
df
df$a <- rep(1, 3)
df
df$a <- 0
df
Counting the number of elements with the values of <x>
in a vector
<table> <- table(<vector>)
<table>
<table>[names(<table>) == <x>]
sum(<vector> == <x>)
length(which(<vector> == <x>)) # which() is like WHERE in SQL
v <- c(1, 2, 1, 3, 2, 4, 5, 1, 3, 1)
t <- table(v)
t
t[names(t) == 1]
t[names(t) == "1"]
sum(v == 1)
length(which(v == 1))
Appending an element to a vector
<vector> <- append(<vector>, <element>) # type conversion occurs if <element> is of different type than v[i]
<vector> <- append(<vector>, <element>, after = <n>) # insert <=> append at a desired location
<vector> <- append(<vector>, NA)
v <- c(1, 2, 1, 3, 2, 4, 5, 1, 3, 1)
v
v <- append(v, NA)
v <- append(v, NA, after = 5)
v
v <- append(v, "s")
v
Removing NAs from a vector in NA-sensitive functions
<function>(<vector>, na.rm = TRUE)
v <- c(1, 2, 1, 3, 2, 4, 5, 1, 3, 1)
v
v <- append(v, NA)
v <- append(v, NA)
mean(v)
mean(v, na.rm = TRUE)
Selecting vector elements that match criteria, with controlling for NAs and NaNs
<numeric vector> <- c(<n1>, <n2>, <n3>, ..., NA, ...NaN)
<selected> <- <numeric vector>[<logical criterion> & !is.na(<numeric vector>)] # is.na() is TRUE for both NA and NaN
Using is.na()
is the only way to test if <something>
is NA (<something> == NA
does not work).
v <- c(1, 2, 1, 3, NA, 4, 5, 1, 3, NaN, 1)
v
v <- v[v > 1 & !is.na(v)]
v
LS0tDQp0aXRsZTogIkdldHRpbmcgc3RhcnRlZCB3aXRoIFIiDQphdXRob3I6ICJWbGFkYW4gRGV2ZWR6aWMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMjIEludHJvZHVjdGlvbg0KIyMjIFNldHVwDQpEb3dubG9hZCBSIGZyb20gPGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnLz4gYW5kIGluc3RhbGwgaXQuICANCkRvd25sb2FkIFJTdHVkaW8gZnJvbSA8aHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvcnN0dWRpby9kb3dubG9hZC8+IGFuZCBpbnN0YWxsIGl0Lg0KDQojIyMgT3BlbiBuZXcgcHJvamVjdCBhbmQgbmV3IFIgc2NyaXB0DQpPcGVuIG5ldyBwcm9qZWN0IGluIFJTdHVkaW8gKEZpbGUgPiBOZXcgUHJvamVjdC4uLikuICANCk9wZW4gbmV3IFIgc2NyaXB0IGluIHRoZSBwcm9qZWN0IChGaWxlID4gTmV3IEZpbGUgPiBSIFNjcmlwdCkuDQoNCiMjIyBXb3JraW5nIGRpcmVjdG9yeQ0KR2V0IHdvcmtpbmcgZGlyZWN0b3J5Og0KYGBge3IgZXZhbD1GQUxTRX0NCmdldHdkKCkNCmBgYA0KU2V0IHdvcmtpbmcgZGlyZWN0b3J5OiAgDQpTZXNzaW9uID4gU2V0IFdvcmtpbmcgRGlyZWN0b3J5DQoNCiMjIyBIZWxwDQpgYGB7ciBldmFsPUZBTFNFfQ0KP3NldHdkDQpoZWxwKCJzZXR3ZCIpDQpgYGANCg0KIyMjIEluc3RhbGwgYW5kIGxvYWQgcGFja2FnZXMNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIGV2YWw9RkFMU0V9DQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjYXJldCIpDQpsaWJyYXJ5KGNhcmV0KQ0KYGBgDQoNCiMjIEJhc2ljIGNvbW1hbmRzLCBvcGVyYXRvcnMgYW5kIGRhdGEgdHlwZXMNCg0KTW9zdCBvZiB0aGUgc2VjdGlvbnMgaW4gdGhpcyBkb2N1bWVudCBhcmUgc3RydWN0dXJlZCBhcyBmb2xsb3dzOiAgDQoNCiogcHNldWRvY29kZSAgDQoqIFIgY29kZSBleGFtcGxlcyAgDQoqIHJlc3VsdHMgb3IgcnVubmluZyB0aGUgUiBjb2RlIGV4YW1wbGVzICANCg0KSW4gcHNldWRvY29kZSB0aHJvdWdob3V0IHRoaXMgZG9jdW1lbnQ6ICANCg0KKiBzeW50YXggbGlrZSBgPHNvbWV0aGluZz5gIGlzIHVzZWQgYXMgYSBkZXNjcmlwdGl2ZSBwYWxjZWhvbGRlciBmb3IgYW4gYXBwcm9wcmlhdGUsIGNvbnRleHQtc3BlY2lmaWMgaWRlbnRpZmllciBvciBhbm90aGVyIHBpZWNlIG9mIGNvZGUgaW4gbGluZXMgb2YgYW4gYWN0dWFsIFIgc2NyaXB0ICANCiogc3ludGF4IGxpa2UgYCI8c29tZXRoaW5nPiJgIGlzIHVzZWQgYXMgYSBkZXNjcmlwdGl2ZSBwYWxjZWhvbGRlciBmb3IgYW4gYXBwcm9wcmlhdGUsIGNvbnRleHQtc3BlY2lmaWMgKnN0cmluZyogaW4gbGluZXMgb2YgYW4gYWN0dWFsIFIgc2NyaXB0ICANCiogbGVhZGluZyAnKycgY2hhcmFjdGVyIGluIG11bHRpLWxpbmUgcHNldWRvY29tbWFuZHMgaXMgdXNlZCBmb3IgaW5kZW50YXRpb24gcHVycG9zZXMgb25seTsgaXQgc2hvdWxkIGJlIG9taXR0ZWQgaW4gYWN0dWFsIFIgY29kZQ0KDQojIyMgUHJpbnQgYSBsaW5lIG9mIHRleHQgaW4gdGhlIGNvbnNvbGUNCmBgYHtyfQ0KcHJpbnQoIkhpIDopIikNCmBgYA0KDQojIyMgTmFtaW5nIGFuZCBjb2RpbmcgY29udmVudGlvbnMNClNlZSA8aHR0cHM6Ly9nb29nbGUuZ2l0aHViLmlvL3N0eWxlZ3VpZGUvUmd1aWRlLnhtbD4uDQoNCiMjIyBBc3NpZ25tZW50IHN0YXRlbWVudA0KYHggPC0gPHNvbWV0aGluZz5gDQpgYGB7cn0NCnggPC0gMg0KeA0KYGBgDQoNCiMjIyBNYW5pcHVsYXRpbmcgb2JqZWN0cyBpbiB0aGUgd29ya3NwYWNlDQpgbHMoKSAgICAgICAgICAgICAgICAgICAgICAjIGxpc3QgYWxsIG9iamVjdHMgaW4gbWVtb3J5YCAgDQpgcm0oPG8xPiwgPG8yPiwgPG8zPiwgLi4uKSAjIHJlbW92ZSBvbmUgb3IgbW9yZSBvYmplY3RzIGZyb20gbWVtb3J5IGJ5IHRoZWlyIG5hbWVzYCAgDQpgcm0obGlzdCA9IGxzKCkpICAgICAgICAgICAjIHJlbW92ZSBhbGwgb2JqZWN0cyBmcm9tIG1lbW9yeSAodXN1YWxseSBub3QgcmVjb21tZW5kZWQpYA0KYGBge3IgZXZhbD1GQUxTRX0NCmxzKCkNCnJtKHgsIHkpDQpgYGANCg0KIyMjIE9wZXJhdG9ycw0KKiBcKwkgIEFkZCwgMiArIDMgPSA1ICANCiogXC0JICBTdWJ0cmFjdCwgNSAtIDIgPSAzICANCiogXCoJICBNdWx0aXBseSwgMiAqIDMgPSA2ICANCiogXC8JICBEaXZpZGUsIDYgLyAyID0gMyAgDQoqIFxeCSAgRXhwb25lbnQsIDIgXiAzID0gOCAgDQoqIFwlJQlNb2R1bHVzIG9wZXJhdG9yLCA5JSUyID0gMSAgDQoqIFwlLyUJSW50ZWdlciBkaXZpc2lvbiwgOSAlLyUgMiA9IDQgIA0KKiBcPAkgIExlc3MgdGhhbiAgDQoqIFw+CSAgR3JlYXRlciB0aGFuICANCiogXD0JICBFcXVhbCB0byAgDQoqIFw8PQlMZXNzIHRoYW4gb3IgZXF1YWwgdG8gIA0KKiBcPj0JR3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvICANCiogXCE9CU5vdCBlcXVhbCB0byAgDQoqIFwhCSAgTm90ICANCiogXHwJICBPUiAgDQoqIFwmCSAgQW5kDQoNCiMjIyBFeHByZXNzaW9ucw0KRS5nLiwgYHggLyB5IC0gel4yYCBldGMuDQpgYGB7cn0NCjMuNCAvIDIgKyA3XjINCmBgYA0KDQojIyMgVmVjdG9ycw0KYDx5PiA8LSBjKDxzb21ldGhpbmcxPiwgPHNvbWV0aGluZzI+LCA8c29tZXRoaW5nMz4sIC4uLilgICANCmA8eT4gPC0gcmVwKDxzb21ldGhpbmc+LCA8dGltZXM+KWAgIA0KYDx5PiA8LSA8aW50MT46PGludDI+YCAgDQpgPHk+IDwtIHNlcSg8dmFsdWUxPiwgPHZhbHVlMj4sIGJ5ID0gPHN0ZXA+KWAgIA0KVGhlIGluZGV4IG9mIHRoZSBmaXJzdCBlbGVtZW50IGluIGEgdmVjdG9yIGlzIDEsIG5vdCAwLg0KYGBge3J9DQp5IDwtIGMoMSwgMiwgMykNCnogPC0gYygxLjIsIDMpDQp0IDwtIDI6Ng0KdyA8LSBzZXEoMy4yLCA0LjcsIGJ5ID0gMC4yKQ0KDQp3WzNdDQp3W10NCncNCmBgYA0KDQojIyMgTWF0cmljZXMNCmA8bT4gPC0gbWF0cml4KGMoMywgNSwgNywgMSwgOSwgNCksIG5yb3cgPSAzLCBuY29sID0gMiwgYnlyb3cgPSBUUlVFKWAgIA0KYDxtPi5ucm93IDwtIG5yb3coPG0+KSAjIG51bWJlciBvZiByb3dzYCAgDQpgPG0+Lm5jb2wgPC0gbmNvbCg8bT4pICMgbnVtYmVyIG9mIGNvbHVtbnNgICANCmA8bT4gPC0gdCg8bT4pICAgICAgICAgIyB0cmFuc3Bvc2UgPG0+YCAgDQpgPG0+WzIsM11gICANCmA8bT5bMl1gICANCmA8bT5bMiwgXWANCmBgYHtyfQ0KYSA8LSBtYXRyaXgoODoxLCBucm93ID0gMiwgbmNvbCA9IDQsIGJ5cm93ID0gVFJVRSkNCmENCmEubnJvdyA8LSBucm93KGEpDQphLm5yb3cNCmEgPC0gdChhKQ0KYQ0KYVsxLDJdDQphWzIsIF0NCmFbMl0NCmFbXQ0KYGBgDQoNCiMjIyBMaXN0cw0KT3JkZXJlZCBjb2xsZWN0aW9ucyBvZiBlbGVtZW50cyBvZiBkaWZmZXJlbnQgdHlwZXMuICANCmA8bGlzdD4gPC0gbGlzdCg8ZTEubmFtZT4gPSA8ZTE+LCA8ZTIubmFtZT4gPSA8ZTI+LCA8ZTMubmFtZT4gPSA8ZTM+LCAuLi4pYCAgDQpgPGxpc3Q+W1s8aW5kZXg+XV0gICAgICMgYWNjZXNzaW5nIGxpc3QgZWxlbWVudCBieSBpbmRleCwgc2hvd2luZyB2YWx1ZSBvbmx5YCAgDQpgPGxpc3Q+WzxpbmRleD5dICAgICAgICMgYWNjZXNzaW5nIGxpc3QgZWxlbWVudCBieSBpbmRleCwgc2hvd2luZyBib3RoIG5hbWUgYW5kIHZhbHVlYCAgDQpgPGxpc3Q+JDxlbGVtZW50Lm5hbWU+ICMgYWNjZXNzaW5nIGxpc3QgZWxlbWVudCBieSBpdHMgbmFtZWAgIA0KYGlzLmxpc3QoPHNvbWV0aGluZz4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgSXMgPHNvbWV0aGluZz4gYSBsaXN0P2AgIA0KYDxjb21iaW5lZC5saXN0PiA8LSBjKDxsaXN0MT4sIDxsaXN0Mj4sIDxsaXN0Mz4sIC4uLikgICMgbGlzdCBjb25jYXRlbmF0aW9uYCAgDQpgbmFtZXMoPGxpc3Q+KSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbmFtZXMgb2YgbGlzdCBlbGVtZW50c2AgIA0KYDxsaXN0PltuYW1lcyg8bGlzdD4pID09IDxlbGVtZW50Lm5hbWU+XSAjIGFsbCBlbGVtZW50cyBvZiBhIGxpc3QgaGF2aW5nIHRoZSBzYW1lIG5hbWVgICANCmB1bmxpc3QoPGxpc3Q+KSAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjb252ZXJ0IGxpc3QgaW50byBhIG5hbWVkIGNoYXJhY3RlciB2ZWN0b3JgICANCmB1bmxpc3QoPGxpc3Q+LCB1c2UubmFtZXMgPSBGQUxTRSkgICAgICAgIyBjb252ZXJ0IGxpc3QgaW50byBhIGNoYXJhY3RlciB2ZWN0b3JgICANCmBhcHBlbmQoPGxpc3Q+LCAgICAgICAgICAgICAgICAgICAgICAgICAgIyBpbnNlcnQgbmV3IGVsZW1lbnQgaW50byBhbiBleGlzdGluZyBsaXN0LCBhZnRlciBpbmRleCA8bj5gICANCmArICAgICAgbGlzdCg8ZTEubmFtZT4gPSA8ZT4pLCAgICAgICAgICAgIyBuZXcgZWxlbWVudCBtdXN0IGJlIGEgbGlzdCBpdHNlbGYsIHRoYXQncyB3aHkgbGlzdCg8ZTEubmFtZT4gPSAgPGU+KWAgIA0KYCsgICAgICAgICAgIDxuPikgICAgICAgICAgICAgICAgICAgICAgICAjIDxuPiBpcyBvcHRpb25hbDsgaWYgb21pdHRlZCwgbmV3IGVsZW1lbnQgaXMgYXBwZW5kZWQgYXQgdGhlIGVuZGAgIA0KYDxsaXN0PltbPG4+XV0gPC0gTlVMTCAgICAgICAgICAgICAgICAgICAjIHJlbW92ZSA8bj50aCBlbGVtZW50IGZyb20gPGxpc3Q+YA0KYGBge3J9DQp0cmF2ZWxlcjEgPC0gbGlzdChhZHVsdCA9IFRSVUUsIHBhc3Nwb3J0ID0gIlAyMTIxMjMiLCBhZ2UgPSAzNCkNCnRyYXZlbGVyMQ0KdHJhdmVsZXIxW1szXV0NCnRyYXZlbGVyMiA8LSBsaXN0KGFkdWx0ID0gRkFMU0UsIHBhc3Nwb3J0ID0gIlA0NTY3NzU2IiwgYWdlID0gMTQpDQp0cmF2ZWxlcjINCnRyYXZlbGVyMiRhZ2UNCnRyYXZlbGVycyA8LSBjKHRyYXZlbGVyMSwgdHJhdmVsZXIyKQ0KdHJhdmVsZXJzDQp0cmF2ZWxlcnNbWzNdXQ0KdHJhdmVsZXJzW1s1XV0NCnRyYXZlbGVyc1s1XQ0KaXMubGlzdCh0cmF2ZWxlcnMpDQppcy52ZWN0b3IodHJhdmVsZXJzKQ0KbmFtZXModHJhdmVsZXJzKQ0KdHJhdmVsZXJzW25hbWVzKHRyYXZlbGVycykgPT0gImFnZSJdDQp1bmxpc3QodHJhdmVsZXJzKQ0KdW5saXN0KHRyYXZlbGVycywgdXNlLm5hbWVzID0gRkFMU0UpDQphZ2Uub2YudHJhdmVsZXJzIDwtIHVubGlzdCh0cmF2ZWxlcnNbbmFtZXModHJhdmVsZXJzKSA9PSAiYWdlIl0sIHVzZS5uYW1lcyA9IEZBTFNFKQ0KYWdlLm9mLnRyYXZlbGVycw0KbGVuZ3RoKHRyYXZlbGVyMSkNCnRyYXZlbGVyMSA8LSBhcHBlbmQodHJhdmVsZXIxLCBsaXN0KGNvdW50cnkgPSAiQVVTIiksIDIpDQpsZW5ndGgodHJhdmVsZXIxKQ0KdHJhdmVsZXIxDQp0cmF2ZWxlcjFbWzNdXSA8LSBOVUxMDQpsZW5ndGgodHJhdmVsZXIxKQ0KdHJhdmVsZXIxDQpgYGANCg0KIyMjIERhdGEgdHlwZXMNClZlY3RvciwgZmFjdG9yLCBudW1lcmljLCBjaGFyYWN0ZXIsIGxvZ2ljYWwsIGRhdGEuZnJhbWUsIG1hdHJpeCwgbGlzdCwgLi4uICANCmBjbGFzcyg8c29tZXRoaW5nPikgICAgICAgICAgICAgICAgICAgICMgZGF0YSB0eXBlYCAgDQpgbW9kZShzb21ldGhpbmcpLCB0eXBlb2YoPHNvbWV0aGluZz4pICAjIGhvdyBhIGRhdGEgaXRlbSBpcyBpbnRlcm5hbGx5IHN0b3JlZCBpbiBtZW1vcnlgDQpgYGB7cn0NCmNsYXNzKGEpDQptb2RlKGEpDQp0eXBlb2YoYSkNCnR5cGVvZigyLjMpDQpgYGANCg0KIyMjIEZhY3RvcnMNCmA8Yj4gPC0gYygxLCAyLCAyLCAyLCAzLCAxLCAxLCA0LCA1LCA0KWAgIA0KYDxmPiA8LSBhcy5mYWN0b3IoYilgICANCmBsZXZlbHMoPGY+KWAgIA0KYDxmPiA8LSBmYWN0b3IoYygxLCAyLCAzKSlgICANCmA8Zj4gPC0gZ2woMywgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBnbCgpICJnZW5lcmF0ZXMgbGV2ZWxzIiAoaGVyZSAzKSwgaS5lLiBmYWN0b3JzYCAgDQpgKyAgICAgICAgIDEsIGxlbmd0aCA9IDEwLCBsYWJlbHMgPSBjKCJPbmUiLCAiVHdvIiwgIlRocmVlIikpICAgICMgZWFjaCBsZXZlbCByZXBsaWNhdGVkIDEgdGltZSwgbGVuZ3RoKDxmPikgPSAxMGANCmBgYHtyfQ0KYiA8LSBjKDEsIDEsIDEsIDIsIDEsIDEsIDEsIDEsIDUsIDQpDQpiLmFzLmZhY3RvciA8LSBhcy5mYWN0b3IoYikNCmxldmVscyhiLmFzLmZhY3RvcikNCmYgPC0gZ2woMywgMSwgbGVuZ3RoID0gMTAsIGxhYmVscyA9IGMoIk9uZSIsICJUd28iLCAiVGhyZWUiKSkNCmYNCmYgPC0gZ2woMywgMiwgbGVuZ3RoID0gMTAsIGxhYmVscyA9IGMoIk9uZSIsICJUd28iLCAiVGhyZWUiKSkNCmYNCm1lYWwgPSBmYWN0b3IoYygiTHVuY2giLCJEaW5uZXIiKSkNCm1lYWwNCm1lYWwgPSBmYWN0b3IoYygiTHVuY2giLCJEaW5uZXIiKSwgbGV2ZWxzPWMoIkx1bmNoIiwiRGlubmVyIikpDQptZWFsDQpgYGANCg0KDQojIyMgRGF0YWZyYW1lcw0KYDxkYXRhZnJhbWU+IDwtIGFzLmRhdGEuZnJhbWUoPG1hdHJpeD4pYCAgDQpgc3RyKDxkYXRhZnJhbWU+KWANCmBgYHtyfQ0KYS5kYXRhLmZyYW1lIDwtIGFzLmRhdGEuZnJhbWUoYSkNCmEuZGF0YS5mcmFtZQ0Kc3RyKGEuZGF0YS5mcmFtZSkNCmBgYA0KDQojIyBMb29wcyBhbmQgYnJhbmNoaW5nDQojIyMgZm9yLCBpZiwgYnJlYWssIG5leHQNCmBmb3IgKDxpPiBpbiA8aW50IHZlY3Rvcj4pIHtgICANCmArICA8bGluZSAxPmAgIA0KYCsgIDxsaW5lIDI+YCAgDQpgKyAgLi4uYCAgDQpgKyAgaWYgKDxsb2dpY2FsIGNvbmRpdGlvbj4pIHtgICANCmArICAgIDxsaW5lIGkxPmAgIA0KYCsgICAgPGxpbmUgaTI+YCAgDQpgKyAgICAuLi5gICANCmArICAgIGJyZWFrICAgICAgICMgYnJlYWs6IGV4aXQgdGhlIGxvb3A7IG5leHQ6IHNraXAgdGhlIHJlbWFpbmluZyBsaW5lcyBpbiB0aGlzIGl0ZXJhdGlvbmAgIA0KYCsgIH1gICANCmArICAuLi5gICANCmArICA8bGluZSBuPmAgIA0KYH1gICANCmBgYHtyfQ0KZm9yIChpIGluIDE6MTApIHsNCiAgaWYgKGkgPT0gMykgew0KICAgIHByaW50KCJEb25lIikNCiAgICBicmVhaw0KICB9DQogIHMgPC0gcGFzdGUoaSwiaXMgY3VycmVudCBpbmRleCIsIHNlcCA9ICIgIikNCiAgcHJpbnQocykNCn0NCmBgYA0KDQojIyMgd2hpbGUsIGlmLWVsc2UsIGJyZWFrLCBuZXh0DQpgPGk+IDwtIDxpbml0aWFsIHZhbHVlPmAgIA0KYHdoaWxlIChsb2dpY2FsIGNvbmRpdGlvbiBpbnZvbHZpbmcgPGk+KSB7YCAgDQpgKyAgPGxpbmUgMT5gICANCmArICA8bGluZSAyPmAgIA0KYCsgIC4uLmAgIA0KYCsgIGlmICg8bG9naWNhbCBjb25kaXRpb24+KSB7YCAgDQpgKyAgICA8bGluZSBpMT5gICANCmArICAgIDxsaW5lIGkyPmAgIA0KYCsgICAgLi4uYCAgDQpgKyAgICBicmVhayAgICAgICAjIGJyZWFrOiBleGl0IHRoZSBsb29wOyBuZXh0OiBza2lwIHRoZSByZW1haW5pbmcgbGluZXMgaW4gdGhpcyBpdGVyYXRpb25gICANCmArICB9IGVsc2Uge2AgIA0KYCsgICAgPGxpbmUgajE+YCAgDQpgKyAgICA8bGluZSBqMj5gICANCmArICAgIC4uLmAgIA0KYCsgIH1gICANCmArICAuLi5gICANCmArICA8bGluZSBuPmAgIA0KYCsgIDxpPiA8LSA8bW9kaWZ5IDxpPj5gICANCmB9YCAgDQpgYGB7cn0NCmkgPC0gMQ0Kd2hpbGUgKGkgPD0gMTApIHsNCiAgaWYgKGkgPT0gNSkgew0KICAgIGkgPC0gaSArIDENCiAgICBuZXh0DQogIH0gZWxzZSB7DQogICAgcHJpbnQocGFzdGUoaSwgImlzIGN1cnJlbnQgaW5kZXgiLCBzZXAgPSAiICIpKQ0KICAgIGkgPC0gaSArIDENCiAgfQ0KfQ0KYGBgDQoNCiMjIyBpZmVsc2UoPGNvbmRpdGlvbj4sIHYxLCB2MikNCkNhbiByZXR1cm4gYSB2ZWN0b3IgKGlmIDxjb25kaXRpb24+IGludm9sdmVzIGFub3RoZXIgdmVjdG9yKS4NCmBgYHtyfQ0KaWZlbHNlKDEgPCA2LCBUUlVFLCBGQUxTRSkNCmlmZWxzZSgxIDwgNiwgIjwiLCAiTm90IDwiKQ0KaWZlbHNlKDE6MTAgPCA2LCAxLCAyKQ0KYGBgDQoNCiMjIGdncGxvdDINCg0KYGBge3J9DQojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KYGBgDQoNCkRhdGEgdG8gcGxvdCAodXNlZCBpbiB0aGUgZXhhbXBsZXMgYmVsb3cpOg0KYGBge3J9DQphY3Rvci54eSA8LSBkYXRhLmZyYW1lKHllYXIgPSBmYWN0b3IoYygyMDE0LCAyMDE1LCAyMDE2LCAyMDE3KSksIG1vdmllcyA9IChjKDIsIDMsIDIsIDEpKSkNCmFjdG9yLnh5DQpgYGANCg0KIyMjIEJhciBncmFwaHMNCmBnZ3Bsb3QoZGF0YSA9IDxkYXRhZnJhbWU+LCBgICANCmArICAgICAgYWVzKHggPSA8Y29sdW1uIDE+LCB5ID0gPGNvbHVtbiAyPiwgZmlsbCA9IDxjb2x1bW4gMT4pKSArICAjIGZpbGwgPSA8Y29sdW1uIDE+IGlzIG9wdGlvbmFsOyBubyB5IGZvciBjb3VudHNgICANCmArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICJpZGVudGl0eSIgZm9yIHZhbHVlcywgImNvdW50IiBmb3IgY291bnRzYCAgDQpgKyB4bGFiKCI8eC1heGlzIGxhYmVsPiIpICsgeWxhYigiPHktYXhpcyBsYWJlbD4iKSArYCAgDQpgKyBnZ3RpdGxlKCI8Z3JhcGggdGl0bGU+IilgICANCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQphY3Rvci5wbG90LnNldCA8LSBnZ3Bsb3QoZGF0YSA9IGFjdG9yLnh5LCBhZXMoeCA9IHllYXIsIHkgPSBtb3ZpZXMsIGZpbGwgPSB5ZWFyKSkNCmFjdG9yLnBsb3Quc2V0ICsgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpDQphY3Rvci5wbG90LnNldCArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIHhsYWIoInJlY2VudCB5ZWFycyIpICsgeWxhYigiIyBvZiBtb3ZpZXMiKSArDQogIGdndGl0bGUoIlhZJ3MgcmVjZW50IG1vdmllcyIpDQpgYGANCg0KIyMjIExpbmUgZ3JhcGhzDQpgZ2dwbG90KGRhdGEgPSA8ZGF0YWZyYW1lPixgICANCmArICAgICAgYWVzKHggPSA8Y29sdW1uIDE+LCB5ID0gPGNvbHVtbiAyPiwgZ3JvdXAgPSAxKSkgKyAgIyBncm91cCA9IDE6IG9uZSBsaW5lLCBhbGwgcG9pbnRzIGNvbm5lY3RlZGAgIA0KYCsgZ2VvbV9saW5lKGNvbG91ciA9ICI8Y29sb3VyPiIsIGxpbmV0eXBlID0gIjxsaW5ldHlwZT4iLCBzaXplID0gPGxpbmUgdGhpY2tuZXNzPikgK2AgIA0KYCsgZ2VvbV9wb2ludChjb2xvdXI9Ijxjb2xvdXI+Iiwgc2l6ZSA9IDxwb2ludCBzaXplPiwgc2hhcGUgPSA8cG9pbnQgc2hhcGU+LCBmaWxsID0gIjxwb2ludCBmaWxsIGNvbG91cj4iKSArYCAgDQpgKyB4bGFiKCI8eC1heGlzIGxhYmVsPiIpICsgeWxhYigiPHktYXhpcyBsYWJlbD4iKSArYCAgDQpgKyBnZ3RpdGxlKCI8Z3JhcGggdGl0bGU+IilgICANCmBBbGwgcGFyYW1ldGVycyBpbiBnZW9tX2xpbmUoKSBhbmQgaW4gZ2VvbV9wb2ludCgpIGFyZSBvcHRpb25hbC5gICANCmBUaGUgZGVmYXVsdHMgYXJlOiBjb2xvdXIgPSAiYmxhY2siLCBsaW5ldHlwZSA9ICJzb2xpZCIsIHNpemUgPSAxLCBzaGFwZSA9IDIxIChjaXJjbGUpLCBmaWxsID0gImJsYWNrImAgIA0KYFNlZSA8aHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvQ29sb3JzXyhnZ3Bsb3QyKS8+IGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGNvbG9ycy5gICANCmBTZWUgPGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL1NoYXBlc19hbmRfbGluZV90eXBlcy8+IGZvciBpbmZvcm1hdGlvbiBvbiBzaGFwZXMgYW5kIGxpbmUgdHlwZXMuYA0KDQpgYGB7cn0NCmFjdG9yLnBsb3Quc2V0IDwtIGdncGxvdChkYXRhID0gYWN0b3IueHksIGFlcyh4ID0geWVhciwgeSA9IG1vdmllcywgZ3JvdXAgPSAxKSkNCmFjdG9yLnBsb3Quc2V0ICsgZ2VvbV9saW5lKCkNCmFjdG9yLnBsb3Quc2V0ICsgZ2VvbV9saW5lKCkgKyBnZW9tX3BvaW50KCkNCmFjdG9yLnBsb3Quc2V0ICsgDQogIGdlb21fbGluZShjb2xvdXIgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRvdHRlZCIsIHNpemUgPSAyKSArIA0KICBnZW9tX3BvaW50KGNvbG91cj0iZ3JlZW4iLCBzaXplID0gNCwgc2hhcGUgPSAyMSwgZmlsbCA9ICJ5ZWxsb3ciKQ0KIyBnZW9tX3BvaW50KGNvbG9yPSJncmVlbiIsIHNpemUgPSA4LCBzaGFwZSA9IDE4LCBmaWxsID0gInllbGxvdyIpDQpgYGANCg0KIyMgV29ya2luZyB3aXRoIGRhdGFzZXRzIC8gZGF0YWZyYW1lcw0KDQojIyMgUmVhZGluZyBhIGRhdGFzZXQNCmA8ZGF0YWZyYW1lPiA8LSByZWFkLmNzdigiPGZpbGVuYW1lPiIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSlgICANCmAgc3RyKDxkYXRhZnJhbWU+KSAgIyBzdHJ1Y3R1cmUgb2YgPGRhdGFmcmFtZT4sIGFsbCB2YXJpYWJsZXMvY29sdW1uc2AgIA0KYCBoZWFkKDxkYXRhZnJhbWU+KSAjIHRoZSBmaXJzdCBmZXcgcm93c2AgIA0KYCB0YWlsKDxkYXRhZnJhbWU+KSAjIHRoZSBsYXN0IGZldyByb3dzYA0KYGBge3J9DQp0aGUuYmVhdGxlcy5zb25ncyA8LSByZWFkLmNzdigiVGhlIEJlYXRsZXMgc29uZ3MgZGF0YXNldCwgdjEuY3N2IiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpDQpgYGANCg0KIyMjIEV4YW1pbmluZyBhIGRhdGFmcmFtZQ0KYHN0cig8ZGF0YWZyYW1lPikgICAgICAgICAgICAgICMgc3RydWN0dXJlIG9mIDxkYXRhZnJhbWU+LCBhbGwgdmFyaWFibGVzL2NvbHVtbnNgICANCmBkaW0oPGRhdGFmcmFtZT4pICAgICAgICAgICAgICAjIHNob3dpbmcgZGltZW5zaW9ucyAobnVtYmVycyBvZiByb3dzIGFuZCBjb2x1bW5zKSBvZiBhIGRhdGFmcmFtZWAgIA0KYG5hbWVzKDxkYXRhZnJhbWU+KSAgICAgICAgICAgICMgc2hvd2luZyBjb2x1bW4gbmFtZXNgICANCmBoZWFkKDxkYXRhZnJhbWU+KSAgICAgICAgICAgICAjIHRoZSBmaXJzdCBmZXcgcm93c2AgIA0KYHRhaWwoPGRhdGFmcmFtZT4pICAgICAgICAgICAgICMgdGhlIGxhc3QgZmV3IHJvd3NgICANCmA8ZGF0YWZyYW1lPlsgLCBdICAgICAgICAgICAgICAjIHRoZSBlbnRpcmUgZGF0YWZyYW1lYCAgDQpgPGRhdGFmcmFtZT4gICAgICAgICAgICAgICAgICAgIyB0aGUgZW50aXJlIGRhdGFmcmFtZWAgIA0KYDxkYXRhZnJhbWU+WzxtPiwgXSAgICAgICAgICAgICMgbS10aCByb3dgICANCmA8ZGF0YWZyYW1lPlsgLDxuPl0gICAgICAgICAgICAjIG4tdGggY29sdW1uYCAgDQpgc3VtbWFyeSg8ZGF0YWZyYW1lPiQ8Y29sdW1uPikgIyBzdW1tYXJpemluZyBhIHZhcmlhYmxlL2NvbHVtbiB2YWx1ZXNgICANCmBmaXgoPGRhdGFmcmFtZT4pICAgICAgICAgICAgICAjIGVkaXRpbmcgYSBkYXRhZnJhbWVgICANCmBuZXcuZGYgPC0gZWRpdCg8ZGF0YWZyYW1lPikgICAjIGVkaXRpbmcgYSBkYXRhZnJhbWUgYW5kIGFzc2lnbmluZyB0aGUgbW9kaWZpZWQgZGF0YWZyYW1lIHRvIGFub3RoZXIgZGF0YXZyYW1lYCAgDQpgYGB7cn0NCnN0cih0aGUuYmVhdGxlcy5zb25ncykNCmRpbSh0aGUuYmVhdGxlcy5zb25ncykNCm5hbWVzKHRoZS5iZWF0bGVzLnNvbmdzKQ0KaGVhZCh0aGUuYmVhdGxlcy5zb25ncykNCnRhaWwodGhlLmJlYXRsZXMuc29uZ3MpDQp0aGUuYmVhdGxlcy5zb25nc1s0LCBdDQp0aGUuYmVhdGxlcy5zb25nc1sgLDJdDQpzdW1tYXJ5KHRoZS5iZWF0bGVzLnNvbmdzJER1cmF0aW9uKQ0Kc3VtbWFyeSh0aGUuYmVhdGxlcy5zb25ncyRUaXRsZSkNCnN1bW1hcnkodGhlLmJlYXRsZXMuc29uZ3MkWWVhcikNCiMgZml4KHRoZS5iZWF0bGVzLnNvbmdzKQ0KIyBhLmRhdGEuZnJhbWUuMSA8LSBlZGl0KGEuZGF0YS5mcmFtZSkNCmBgYA0KDQojIyMgRXhhbWluaW5nIGEgZGF0YWZyYW1lIHZpc3VhbGx5LCB3aXRoIGdncGxvdCgpDQpgYGB7cn0NCnRoZS5iZWF0bGVzLnNvbmdzLmNsZWFuIDwtIHJlYWQuY3N2KCJUaGUgQmVhdGxlcyBzb25ncyBkYXRhc2V0LCB2MSwgbm8gTkFzLmNzdiIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQ0KdGhlLmJlYXRsZXMuc29uZ3MuY2xlYW4kWWVhciA8LSBmYWN0b3IodGhlLmJlYXRsZXMuc29uZ3MuY2xlYW4kWWVhcikgICAgICAjIGJlY2F1c2Ugd3JpdGUuY3N2L3JlYWQuY3N2IHByb2R1Y2VzIGludCdzDQpnMSA8LSBnZ3Bsb3QoZGF0YSA9IHRoZS5iZWF0bGVzLnNvbmdzLmNsZWFuLCBhZXMoeCA9IFllYXIsIHkgPSBEdXJhdGlvbiwgZmlsbCA9IFllYXIpKQ0KZzEgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikNCmcyIDwtIGdncGxvdChkYXRhID0gdGhlLmJlYXRsZXMuc29uZ3MuY2xlYW4sIGFlcyh4ID0gWWVhciwgZmlsbCA9IFllYXIpKQ0KZzIgKyBnZW9tX2JhcihzdGF0ID0gImNvdW50IikgKw0KICB4bGFiKCJZZWFyIikgKyB5bGFiKCJOby4gb2Ygc29uZ3MiKSArDQogIGdndGl0bGUoIlRoZSBudW1iZXIgQmVhdGxlcyBzb25ncyBwZXIgeWVhciIpDQpnMyA8LSBnZ3Bsb3QodGhlLmJlYXRsZXMuc29uZ3MuY2xlYW5bMTo1LCBdLCBhZXMoeCA9IFllYXIsIHkgPSBEdXJhdGlvbiwgZ3JvdXAgPSAxKSkNCmczICsgZ2VvbV9saW5lKGNvbG9yID0gIm9yYW5nZSIsIHNpemUgPSAyLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpICsgDQogICAgIGdlb21fcG9pbnQoY29sb3IgPSAicmVkIiwgc2hhcGUgPSAyNSwgc2l6ZSA9IDgsIGZpbGwgPSAieWVsbG93IikNCmBgYA0KDQojIyMgQWRkaW5nL1JlbW92aW5nIGNvbHVtbnMgdG8vZnJvbSBhIGRhdGFmcmFtZQ0KYDxkYXRhZnJhbWU+JDxuZXcgY29sdW1uIG5hbWU+IDwtIDxkZWZhdWx0IHZhbHVlPiAgIyBhZGRpbmcgYSBuZXcgY29sdW1uIChkZWZhdWx0IHZhbHVlcylgICANCmA8ZGF0YWZyYW1lPiQ8Y29sdW1uIG5hbWU+IDwtIE5VTEwgICAgICAgICAgICAgICAgICMgcmVtb3ZpbmcgYSBjb2x1bW5gDQoNCmBgYHtyfQ0KdGhlLmJlYXRsZXMuc29uZ3MkTm90Lm9uLmFsYnVtIDwtIEZBTFNFDQp0aGUuYmVhdGxlcy5zb25ncyROb3Qub24uYWxidW0gPC0gTlVMTA0KdGhlLmJlYXRsZXMuc29uZ3MkT24uYWxidW0gPC0gRkFMU0UNCnRoZS5iZWF0bGVzLnNvbmdzJE9uLmFsYnVtW3RoZS5iZWF0bGVzLnNvbmdzJEFsYnVtLmRlYnV0ICE9ICIiXSA8LSBUUlVFDQpgYGANCg0KIyMjIEFkZGluZyBuZXcgcm93cyB0byBhIGRhdGFmcmFtZQ0KSW4gY2FzZSBvZiBhZGRpbmcgb25lIG5ldyByb3csIGl0IG11c3QgYmUgYSAxLWxpbmUgZGF0YWZyYW1lIHdpdGggdGhlIHNhbWUgY29sdW1uIG5hbWVzLiBJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIGFkZCBhbiBlbnRpcmUgZGF0YWZyYW1lIHRvIHRoZSBleGlzdGluZyBvbmUgKHdpdGggdGhlIHNhbWUgY29sdW1uIG5hbWVzKS4gIA0KYDxuZXcgcm93PiA8LSBkYXRhLmZyYW1lKDxjb2x1bW4gbmFtZSAxPiA9IDx2YWx1ZSAxPiwgPGNvbHVtbiBuYW1lIDI+ID0gPHZhbHVlIDI+LC4uLilgICANCmA8bmV3IGRhdGEgZnJhbWU+IDwtIHJiaW5kKDxkYXRhZnJhbWU+LCA8bmV3IHJvdz4pICMgYXBwZW5kIG5ldyByb3cgdG8gdGhlIGVuZCBvZiB0aGUgZXhpc3RpbmcgZGF0YWZyYW1lYCAgDQpgPG5ldyBkYXRhIGZyYW1lPiA8LSByYmluZCg8ZGF0YWZyYW1lPlsxOmksIF0sICAgICAjIGluc2VydCBuZXcgcm93IGluIHRoZSBtaWRkbGVgICANCmArICAgICAgICAgICAgICAgICAgICAgICAgIDxuZXcgcm93PixgICANCmArICAgICAgICAgICAgICAgICAgICAgICAgIDxkYXRhZnJhbWU+WyhpICsgMSk6bnJvdyg8ZGF0YWZyYW1lPiksIF0pYA0KYGBge3J9DQpuZXcuc29uZyA8LSBkYXRhLmZyYW1lKHRoZS5iZWF0bGVzLnNvbmdzWzEsIF0pDQp0aGUuYmVhdGxlcy5zb25ncyA8LSByYmluZCh0aGUuYmVhdGxlcy5zb25ncywgbmV3LnNvbmcpDQp0aGUuYmVhdGxlcy5zb25ncyA8LSByYmluZCh0aGUuYmVhdGxlcy5zb25nc1sxOjMsIF0sICAgICMgUnN0dWRpbyBrZWVwcyB0aGUgb3JpZ2luYWwgcm93IG51bWJlcnMgaW4gVmlldygpDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcuc29uZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICB0aGUuYmVhdGxlcy5zb25nc1s0Om5yb3codGhlLmJlYXRsZXMuc29uZ3MpLCBdKQ0KDQpgYGANCg0KIyMjIFJlbW92aW5nIHJvd3MgZnJvbSBhIGRhdGFmcmFtZQ0KYDxkYXRhZnJhbWU+Wy1pLCBdICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzaG93IGRhdGFmcmFtZSB3aXRob3V0IGktdGggcm93YCAgDQpgPGRhdGFmcmFtZT5bLWMoaSwgaiwgayksIF0gICAgICAgICAgICAgICAgICAgICAgICAjIHNob3cgZGF0YWZyYW1lIHdpdGhvdXQgcm93cyBpLCBqLCBrYCAgDQpgPGRhdGFmcmFtZT4gPC0gPGRhdGFmcmFtZT5bLWksIF0gICAgICAgICAgICAgICAgICAjIHJlbW92ZSBpLXRoIHJvdyBmcm9tIGRhdGFmcmFtZWAgIA0KYDxkYXRhZnJhbWU+IDwtIDxkYXRhZnJhbWU+Wy1jKGksIGosIGspLCBdICAgICAgICAgIyByZW1vdmUgcm93cyBpLCBqLCBrIGZyb20gZGF0YWZyYW1lYCAgDQpgPGRhdGFmcmFtZT4gPC0gPGRhdGFmcmFtZT5bLShpOmspLCBdICAgICAgICAgICAgICAjIHJlbW92ZSByb3dzIGkgdG8gayBmcm9tIGRhdGFmcmFtZWAgIA0KDQpgYGB7cn0NCm5yb3codGhlLmJlYXRsZXMuc29uZ3MpDQp0aGUuYmVhdGxlcy5zb25ncyA8LSB0aGUuYmVhdGxlcy5zb25nc1stbnJvdyh0aGUuYmVhdGxlcy5zb25ncyksIF0NCnRoZS5iZWF0bGVzLnNvbmdzMSA8LSB0aGUuYmVhdGxlcy5zb25nc1stKDMwNTozMTApLCBdDQp0aGUuYmVhdGxlcy5zb25ncyA8LSB0aGUuYmVhdGxlcy5zb25nc1stKDE6MzA0KSwgXQ0KdGhlLmJlYXRsZXMuc29uZ3MgPC0gcmJpbmQodGhlLmJlYXRsZXMuc29uZ3MxLCB0aGUuYmVhdGxlcy5zb25ncykNCmBgYA0KDQojIyMgQ2hhbmdpbmcgY29sdW1uIG5hbWVzDQpgY29sbmFtZXMoPGRhdGFmcmFtZT4pW2ldIDwtICI8bmV3IG5hbWU+ImAgIA0KDQpgYGB7cn0NCmNvbG5hbWVzKHRoZS5iZWF0bGVzLnNvbmdzKQ0Kd2hpY2goY29sbmFtZXModGhlLmJlYXRsZXMuc29uZ3MpID09ICJHZW5yZSIpDQpjb2xuYW1lcyh0aGUuYmVhdGxlcy5zb25ncylbd2hpY2goY29sbmFtZXModGhlLmJlYXRsZXMuc29uZ3MpID09ICJHZW5yZSIpXSA8LSAiU29uZy5nZW5yZSINCmNvbG5hbWVzKHRoZS5iZWF0bGVzLnNvbmdzKVs2XSA8LSAiR2VucmUiDQpgYGANCg0KIyMjIENoYW5naW5nIHJvdyBuYW1lcw0KYHJvd25hbWVzKDxkYXRhZnJhbWU+KVtpXSA8LSAiPG5ldyBuYW1lPiJgICANCmByb3duYW1lcyg8ZGF0YWZyYW1lPikgPC0gYygiPG5ldyBuYW1lIDE+IiwgIjxuZXcgbmFtZSAyPiIsLi4uKWAgIA0KYHJvd25hbWVzKDxkYXRhZnJhbWU+KSA8LSBjKDEsIDIsLi4uKWAgIA0KYHJvd25hbWVzKDxkYXRhZnJhbWU+KSA8LSBsaXN0KCI8bmV3IG5hbWUgMT4iLCA8bnVtZXJpYyAyPiwuLi4pYCAgDQoNCmBgYHtyfQ0Kcm93bmFtZXModGhlLmJlYXRsZXMuc29uZ3MpIDwtIHBhc3RlKCJzb25nIiwgMTpucm93KHRoZS5iZWF0bGVzLnNvbmdzKSkNCnJvd25hbWVzKHRoZS5iZWF0bGVzLnNvbmdzKSA8LSBjKDE6bnJvdyh0aGUuYmVhdGxlcy5zb25ncykpDQpgYGANCg0KIyMjIFNsaWNpbmcgYW5kIGRpY2luZyBkYXRhZnJhbWVzDQpgPHNlbGVjdGlvbj4gPC0gPGRhdGFmcmFtZT5bPHNvbWUgcm93cz4sIDxzb21lIGNvbHVtbnM+XWAgIA0KYDxzZWxlY3Rpb24+IDwtIDxkYXRhZnJhbWU+W2k6aywgYygiPGNvbHVtbiAxPiIsICI8Y29sdW1uIDI+IiwuLi4pXWAgIA0KYDxpbmRleGVzPiA8LSB3aXRoKDxkYXRhZnJhbWU+LCB3aGljaCg8Y29uZGl0aW9uOyBjYW4gYmUgY29tcGxleD4pKSAgIyBhIHdpdGgoKS13aGljaCgpIHNlbGVjdGlvbiwgbGlrZSBhbiBTUUwgcXVlcnlgICANCmA8c2VsZWN0aW9uPiA8LSA8ZGF0YWZyYW1lPls8aW5kZXhlcz4sIF1gICANCmA8c2VsZWN0aW9uPiA8LSBzdWJzZXQoPGRhdGFmcmFtZT4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc3Vic2V0KCkgaXMgbXVjaCBsaWtlIFNFTEVDVC4uLiBGUk9NLi4uIFdIRVJFYCAgDQpgKyAgICAgICAgICAgICAgICAgICAgIDxsb2dpY2FsIGNvbmRpdGlvbiBmb3IgdGhlIHJvd3MgdG8gcmV0dXJuPixgICANCmArICAgICAgICAgICAgICAgICAgICAgPHNlbGVjdCBzdGF0ZW1lbnQgZm9yIHRoZSBjb2x1bW5zIHRvIHJldHVybj4pICAgIyBjYW4gYmUgb21pdHRlZDsgYCAgDQpgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY29sdW1uIG5hbWVzIG5vdCBwcmVmaXhlZCBieSA8ZGF0YWZyYW1lPiRgICANCmBsaWJyYXJ5KGRwbHlyKWAgIA0KYDxzZWxlY3Rpb24+IDwtIGZpbHRlcig8ZGF0YWZyYW1lPiwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGZpbHRlcigpIGlzIGZyb20gZHBseXJgICANCmArICAgICAgICAgICAgICAgICAgICAgPGxvZ2ljYWwgY29uZGl0aW9uIGZvciB0aGUgcm93cyB0byByZXR1cm4+KSAgICAgIyBjYW4gaW5jbHVkZSBjb2x1bW4gcmVmZXJlbmNpbmcsIGAgIA0KYCsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG5vdC1wcmVmaXhlZCBieSA8ZGF0YWZyYW1lPiRgDQpgYGB7cn0NCnNlbGVjdGVkLnNvbmdzIDwtIHRoZS5iZWF0bGVzLnNvbmdzWzE6NSwgYygiVGl0bGUiLCAiQWxidW0uZGVidXQiKV0NCiMgVmlldyhzZWxlY3RlZC5zb25ncykNCmluZGV4ZXMgPC0gd2l0aCh0aGUuYmVhdGxlcy5zb25ncywgd2hpY2goKFllYXIgPT0gIjE5NjQiKSAmIChMZWFkLnZvY2FsICE9ICJNY0NhcnRuZXkiKSkpDQpzZWxlY3RlZC5zb25ncyA8LSB0aGUuYmVhdGxlcy5zb25nc1tpbmRleGVzLCBdDQpzb25ncy4xOTU4IDwtIHN1YnNldCh0aGUuYmVhdGxlcy5zb25ncywgWWVhciA9PSAxOTU4LCBjKCJUaXRsZSIsICJBbGJ1bS5kZWJ1dCIpKQ0KbGlicmFyeShkcGx5cikNCmZpbHRlcih0aGUuYmVhdGxlcy5zb25ncywgDQogICAgICAgYXMuaW50ZWdlcihyb3duYW1lcyh0aGUuYmVhdGxlcy5zb25ncykpIDwgMzMgJiBUaXRsZSA9PSAiMTItQmFyIE9yaWdpbmFsIikNCg0KYGBgDQoNCiMjIyBTaHVmZmxpbmcgcm93cy9jb2x1bW5zDQpgPGRhdGFmcmFtZT4gPC0gPGRhdGFmcmFtZT5bc2FtcGxlKG5yb3coPGRhdGFmcmFtZT4pKSwgXSAgICMgc2h1ZmZsZSByb3ctd2lzZWAgIA0KYDxkYXRhZnJhbWU+IDwtIDxkYXRhZnJhbWU+Wywgc2FtcGxlKG5jb2woPGRhdGFmcmFtZT4pKV0gICAjIHNodWZmbGUgY29sdW1uLXdpc2VgICANCg0KYGBge3J9DQp0aGUuYmVhdGxlcy5zb25ncyA8LSB0aGUuYmVhdGxlcy5zb25nc1tzYW1wbGUobnJvdyh0aGUuYmVhdGxlcy5zb25ncykpLCBdDQp0aGUuYmVhdGxlcy5zb25ncyA8LSB0aGUuYmVhdGxlcy5zb25nc1ssIHNhbXBsZShuY29sKHRoZS5iZWF0bGVzLnNvbmdzKSldDQpgYGANCg0KIyMjIFJlcGxhY2luZyBzZWxlY3RlZCB2YWx1ZXMgaW4gYSBjb2x1bW4NCmA8c2VsZWN0ZWQgdmFyIG5hbWU+IDwtIDxkYXRhZnJhbWU+JDxjb2x1bW4+ID09IDxzZWxlY3RlZCB2YWx1ZT5gICANCmA8ZGF0YWZyYW1lPiQ8Y29sdW1uPls8c2VsZWN0ZWQgdmFyIG5hbWU+XSA8LSA8bmV3IHZhbHVlPmAgIA0KDQpgYGB7cn0NCmVtcHR5LmFsYnVtLmRlYnV0IDwtIHRoZS5iZWF0bGVzLnNvbmdzJEFsYnVtLmRlYnV0ID09ICIiDQplbXB0eS5hbGJ1bS5kZWJ1dA0KdGhlLmJlYXRsZXMuc29uZ3MkQWxidW0uZGVidXRbZW1wdHkuYWxidW0uZGVidXRdIDwtICJlbXB0eSINCnRoZS5iZWF0bGVzLnNvbmdzJEFsYnVtLmRlYnV0W2VtcHR5LmFsYnVtLmRlYnV0XSA8LSAiIg0KYGBgDQoNCiMjIyBBcHBseWluZyBmdW5jdGlvbnMgdG8gYWxsIGVsZW1lbnRzIGluIHJvd3MvY29sdW1ucyBvZiBhIGRhdGFmcmFtZQ0KDQpgYXBwbHkoPGRhdGFmcmFtZT4sIDwxIHwgMj4sIDxmdW5jdGlvbih4KSB7Li4ufT4pICAjIDEgfCAyOiBhcHBseSBmdW5jdGlvbih4KSBieSByb3cgfCBjb2x1bW5gICANCmBtYXBwbHkoZnVuY3Rpb24oeCwgeSwgLi4uKSB7Li4ufSwgPGRhdGFmcmFtZT4kPGNvbHVtbiAxPiwgPGRhdGFmcmFtZT4kPGNvbHVtbiAyPiwgLi4uKWAgIA0KYCsgICMgPGRhdGFmcmFtZT4kPGNvbHVtbiAxPiBjb3JyZXNwb25kcyB0byB4LCA8ZGF0YWZyYW1lPiQ8Y29sdW1uIDI+IGNvcnJlc3BvbmRzIHRvIHksIC4uLmAgIA0KYCsgICMgYWx0ZXJuYXRpdmVseTogIDxmPiA8LSBmdW5jdGlvbih4LCB5LCAuLi4pIHsuLi59YCAgDQpgKyAgICAgICAgICAgICAgICAgICAgbWFwcGx5KDxmPiwgPGRhdGFmcmFtZT4kPGNvbHVtbiAxPiwgPGRhdGFmcmFtZT4kPGNvbHVtbiAyPiwgLi4uKWAgIA0KYCsgICAgICAgICAgICAgICAgICAgICAgIyA8Zj4gaXMganVzdCB0aGUgZnVuY3Rpb24gbmFtZSAoISlgICANCmArICAgICAgICAgICAgICAgICAgICAgICMgPGRhdGFmcmFtZT4kPGNvbHVtbiAxPiBjb3JyZXNwb25kcyB0byB4LCA8ZGF0YWZyYW1lPiQ8Y29sdW1uIDI+IGNvcnJlc3BvbmRzIHRvIHksIC4uLixgICANCmArICAgICAgICAgICAgICAgICAgICAgICMgb3IgY2FuIGJlIGNvbHVtbnMgZnJvbSBkaWZmZXJlbnQgZGF0YWZyYW1lcywgImluZGVwZW5kZW50IiB2ZWN0b3JzLC4uLiAob2YgdGhlIHNhbWUgbGVuZ3RoKWAgIA0KYHNhcHBseSg8dmVjdG9yPiwgRlVOID0gZnVuY3Rpb24oeCkgey4uLn0pICAgIyBmdW5jdGlvbih4KTogZnVuY3Rpb24gdG8gYmUgYXBwbGllZCB0byBlYWNoIGVsZW1lbnQgb2YgPHZlY3Rvcj5gDQoNCmBgYHtyfQ0KYXBwbHkodGhlLmJlYXRsZXMuc29uZ3NbMSwgXSwgMSwgZnVuY3Rpb24oeCkge3ByaW50KHgpfSkNCmFwcGx5KHRoZS5iZWF0bGVzLnNvbmdzWzEsIF0sIDIsIGZ1bmN0aW9uKHgpIHtwcmludCh4KX0pDQptYXBwbHkoZnVuY3Rpb24oeCwgeSkge3ByaW50KHgpOyBwcmludCh5KX0sIA0KICAgICAgIHRoZS5iZWF0bGVzLnNvbmdzWzExMToxMTMsIF0kVGl0bGUsIA0KICAgICAgIHRoZS5iZWF0bGVzLnNvbmdzWzExMToxMTMsIF0kWWVhcikNCnNhcHBseSh0aGUuYmVhdGxlcy5zb25nc1sxLCBdLCBGVU4gPSBmdW5jdGlvbih4KSB7cHJpbnQoeCl9KQ0KYGBgDQoNCiMjIyBQYXJ0aXRpb25pbmcgYSBkYXRhZnJhbWUNCmAjIGluc3RhbGwucGFja2FnZXMoJ2NhcmV0JylgICANCmBsaWJyYXJ5KGNhcmV0KWAgIA0KYHNldC5zZWVkKDxhbnkgc3BlY2lmaWMgaW50PikgICMgYWxsb3dzIGZvciByZXBlYXRpbmcgdGhlIHJhbmRvbWl6YXRpb24gcHJvY2VzcyBleGFjdGx5YCAgDQpgPGluZGV4ZXM+IDwtIGNyZWF0ZURhdGFQYXJ0aXRpb24oPGRhdGFmcmFtZT4kPGNvbHVtbj4sIHAgPSAwLjgsIGxpc3QgPSBGQUxTRSlgICANCmA8cGFydGl0aW9uIDE+IDwtIDxkYXRhZnJhbWU+WzxpbmRleGVzPiwgXWAgIA0KYDxwYXJ0aXRpb24gMj4gPC0gPGRhdGFmcmFtZT5bLTxpbmRleGVzPiwgXWAgIA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFfQ0KbGlicmFyeShjYXJldCkNCnNldC5zZWVkKDIyMikNCmluZGV4ZXMgPC0gY3JlYXRlRGF0YVBhcnRpdGlvbih0aGUuYmVhdGxlcy5zb25ncyRZZWFyLCBwID0gMC44LCBsaXN0ID0gRkFMU0UpDQp0aGUuYmVhdGxlcy5zb25ncy5wMSA8LSB0aGUuYmVhdGxlcy5zb25nc1tpbmRleGVzLCBdDQp0aGUuYmVhdGxlcy5zb25ncy5wMiA8LSB0aGUuYmVhdGxlcy5zb25nc1staW5kZXhlcywgXQ0KYGBgDQoNCiMjIyBTYXZpbmcgYSBkYXRhc2V0IChtb2RpZmllZCBvciBuZXdseSBjcmVhdGVkIGRhdGFzZXQpDQpgd3JpdGUuY3N2KHggPSA8ZGF0YWZyYW1lPiwgZmlsZSA9ICI8ZmlsZW5hbWU+Iiwgcm93Lm5hbWVzID0gRikgICMgZG8gbm90IGluY2x1ZGUgdGhlIHJvdyBuYW1lcyAocm93IG51bWJlcnMpIGNvbHVtbmAgIA0KYHNhdmVSRFMob2JqZWN0ID0gPGRhdGFmcmFtZSBvciBhbm90aGVyIFIgb2JqZWN0PiwgZmlsZSA9ICI8ZmlsZW5hbWU+IikgICMgc2F2ZSBSIG9iamVjdCBmb3IgdGhlIG5leHQgc2Vzc2lvbmAgIA0KYDxkYXRhZnJhbWUgb3IgYW5vdGhlciBSIG9iamVjdD4gPC0gcmVhZFJEUyhmaWxlID0gIjxmaWxlbmFtZT4iKSAgICAgICAgICMgcmVzdG9yZSBSIG9iamVjdCBpbiB0aGUgbmV4dCBzZXNzaW9uYA0KDQpgYGB7cn0NCndyaXRlLmNzdih0aGUuYmVhdGxlcy5zb25ncy5wMiwgInAyLmNzdiIsIHJvdy5uYW1lcyA9IEYpDQpzYXZlUkRTKHRoZS5iZWF0bGVzLnNvbmdzLnAyLCAicDIuUkRhdGEiKQ0KcDIgPC0gcmVhZFJEUygicDIuUkRhdGEiKQ0KYGBgDQoNCiMjIERhdGEgdHlwZSBjb252ZXJzaW9uDQpgIyBDb3ZlcmVkIGFib3ZlOmAgIA0KYCMgYiA8LSBjKDEsIDIsIDIsIDIsIDMsIDEsIDEsIDQsIDUsIDQpYCAgDQpgIyBiLmFzLmZhY3RvciA8LSBhcy5mYWN0b3IoYilgICANCmAjIGxldmVscyhiLmFzLmZhY3RvcilgICANCmAjIGUuZy4sIDxkYXRhZnJhbWU+IDwtIGFzLmRhdGEuZnJhbWUoPG1hdHJpeD4pYCAgDQpgIyBzdHIoPGRhdGFmcmFtZT4pYCAgDQoNCiMjIyBEaWZmZXJlbmNlIGJldHdlZW4gY2hhcmFjdGVyIGFuZCBmYWN0b3IgdmVjdG9ycw0KYHN1bW1hcnkoPGNoYXJhY3RlciB2ZWN0b3I+KWAgIA0KYHN1bW1hcnkoYXMuZmFjdG9yKDxjaGFyYWN0ZXIgdmVjdG9yPikpYCAgDQpgYGB7cn0NCmNsYXNzKHRoZS5iZWF0bGVzLnNvbmdzJFllYXIpDQpzdW1tYXJ5KHRoZS5iZWF0bGVzLnNvbmdzJFllYXIpDQpzdW1tYXJ5KGFzLmZhY3Rvcih0aGUuYmVhdGxlcy5zb25ncyRZZWFyKSkNCmBgYA0KDQojIyMgQ29udmVydCBudW1lcmljIHRvIGZhY3Rvcg0KYDxkYXRhZnJhbWU+JDxudW1lcmljIGNvbHVtbiB3aXRoIGZldyBkaWZmZXJlbnQgdmFsdWVzPiA8LWAgIA0KYCsgZmFjdG9yKDxkYXRhZnJhbWU+JDxudW1lcmljIGNvbHVtbiB3aXRoIGZldyBkaWZmZXJlbnQgdmFsdWVzPixgICANCmArICAgICAgICBsZXZlbHMgPSBjKDAsIDEsIC4uLiwgayksIGxhYmVscyA9IGMoIjxsMT4iLCAiPGwyPiIsIC4uLiwgIjxsaz4iKSlgDQpgYGB7cn0NCnRoZS5iZWF0bGVzLnNvbmdzMSA8LSB0aGUuYmVhdGxlcy5zb25ncw0KdGhlLmJlYXRsZXMuc29uZ3MxJEJpbGxib2FyZC5oaXQgPC0gMA0KdGhlLmJlYXRsZXMuc29uZ3MxJEJpbGxib2FyZC5oaXRbIWlzLm5hKHRoZS5iZWF0bGVzLnNvbmdzMSRUb3AuNTAuQmlsbGJvYXJkKV0gPC0gMQ0KdGhlLmJlYXRsZXMuc29uZ3MxJEJpbGxib2FyZC5oaXQgPC0gDQogIGZhY3Rvcih0aGUuYmVhdGxlcy5zb25nczEkQmlsbGJvYXJkLmhpdCwgbGV2ZWxzID0gYygwLDEpLCBsYWJlbHMgPSBjKCJOIiwgIlkiKSkNCmNsYXNzKHRoZS5iZWF0bGVzLnNvbmdzMSRCaWxsYm9hcmQuaGl0KQ0Kc3VtbWFyeSh0aGUuYmVhdGxlcy5zb25nczEkQmlsbGJvYXJkLmhpdCkNCmxldmVscyh0aGUuYmVhdGxlcy5zb25nczEkQmlsbGJvYXJkLmhpdCkNCmBgYA0KDQojIyMgRXhhbXBsZXMNCiMjIyMgRml4aW5nIHNvbWUgdmFsdWVzIGluIHRoZS5iZWF0bGVzLnNvbmdzJFllYXINCmBgYHtyfQ0Kc3VtbWFyeSh0aGUuYmVhdGxlcy5zb25ncyRZZWFyKQ0Kc3VtbWFyeShhcy5mYWN0b3IodGhlLmJlYXRsZXMuc29uZ3MkWWVhcikpDQp0aGUuYmVhdGxlcy5zb25ncyRZZWFyW3RoZS5iZWF0bGVzLnNvbmdzJFllYXIgPT0gIjE5Nj8iXSA8LSAiMTk2OSINCnRoZS5iZWF0bGVzLnNvbmdzJFllYXJbdGhlLmJlYXRsZXMuc29uZ3MkWWVhciA9PSAiMTk3Ny8xOTk0Il0gPC0gIjE5NzciDQp0aGUuYmVhdGxlcy5zb25ncyRZZWFyW3RoZS5iZWF0bGVzLnNvbmdzJFllYXIgPT0gIjE5ODAvMTk5NSJdIDwtICIxOTgwIg0Kc3VtbWFyeShhcy5mYWN0b3IodGhlLmJlYXRsZXMuc29uZ3MkWWVhcikpDQpgYGANCg0KIyMjIyBDcmVhdGluZyB0aGUuYmVhdGxlcy5zb25ncyRCaWxsYm9hcmQuaGl0IGNvbHVtbiBhcyBmYWN0b3INCmBgYHtyfQ0KdGhlLmJlYXRsZXMuc29uZ3MkQmlsbGJvYXJkLmhpdCA8LSAwDQp0aGUuYmVhdGxlcy5zb25ncyRCaWxsYm9hcmQuaGl0WyFpcy5uYSh0aGUuYmVhdGxlcy5zb25ncyRUb3AuNTAuQmlsbGJvYXJkKV0gPC0gMQ0KdGhlLmJlYXRsZXMuc29uZ3MkQmlsbGJvYXJkLmhpdCA8LSANCiAgZmFjdG9yKHRoZS5iZWF0bGVzLnNvbmdzJEJpbGxib2FyZC5oaXQsIGxldmVscyA9IGMoMCwxKSwgbGFiZWxzID0gYygiTiIsICJZIikpDQpgYGANCg0KIyMgV29ya2luZyB3aXRoIHRhYmxlcw0KDQojIyMgVGhlIHRhYmxlKCkgZnVuY3Rpb24NCmB0YWJsZSg8dmFyPikgICMgdHlwaWNhbGx5IGEgZmFjdG9yIG9yIGFuIGludGVnZXIgdmFyYCAgDQpgYGB7cn0NCnRhYmxlKHRoZS5iZWF0bGVzLnNvbmdzMSRZZWFyKQ0KdGFibGUodGhlLmJlYXRsZXMuc29uZ3MxJFRvcC41MC5CaWxsYm9hcmQpDQp0YWJsZSh0aGUuYmVhdGxlcy5zb25nczEkQmlsbGJvYXJkLmhpdCkNCnRhYmxlKHRoZS5iZWF0bGVzLnNvbmdzMSRCaWxsYm9hcmQuaGl0KVsxXQ0KeCA8LSB0YWJsZSh0aGUuYmVhdGxlcy5zb25nczEkQmlsbGJvYXJkLmhpdClbMV0NCngNCnkgPC0gYXMubnVtZXJpYyh4KQ0KeQ0KYGBgDQoNCiMjIyBUaGUgcHJvcC50YWJsZSgpIGZ1bmN0aW9uDQpgcHJvcC50YWJsZSh0YWJsZSg8dmFyPikpYCAgDQpgcm91bmQocHJvcC50YWJsZSh0YWJsZSg8dmFyPikpLCBkaWdpdHMgPSA8bj4pYCAgDQpgYGB7cn0NCnByb3AudGFibGUodGFibGUodGhlLmJlYXRsZXMuc29uZ3MxJEJpbGxib2FyZC5oaXQpKQ0Kcm91bmQocHJvcC50YWJsZSh0YWJsZSh0aGUuYmVhdGxlcy5zb25nczEkQmlsbGJvYXJkLmhpdCkpLCBkaWdpdHMgPSAyKQ0KYGBgDQojIyMgUm93IGFuZCBjb2x1bW4gbWFyZ2lucw0KYHRhYmxlKDx2YXIxPiwgPHZhcjI+KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyA8dmFyMT4sIDx2YXIyPjogdXN1YWxseSBmYWN0b3JzIG9yIGludGVnZXJzYCAgDQpgdGFibGUoPHJvd3MgdGl0bGU+ID0gPHZhcjE+LCA8Y29sdW1ucyB0aXRsZT4gPSA8dmFyMj4pICAjIGFkZCBjb21tb24gdGl0bGVzIGZvciByb3dzL2NvbHVtbnNgICANCmBwcm9wLnRhYmxlKHRhYmxlKDx2YXIxPiwgPHZhcjI+KSwgbWFyZ2luID0gMSkgICAgICAgICAgICMgYWxsIHJvdyBtYXJnaW5zIGFyZSAxLjBgICANCmBwcm9wLnRhYmxlKHRhYmxlKDx2YXIxPiwgPHZhcjI+KSwgbWFyZ2luID0gMikgICAgICAgICAgICMgYWxsIGNvbHVtbiBtYXJnaW5zIGFyZSAxLjBgICANCg0KYGBge3J9DQp0YWJsZSh0aGUuYmVhdGxlcy5zb25ncyRCaWxsYm9hcmQuaGl0LCB0aGUuYmVhdGxlcy5zb25ncyRZZWFyKQ0KdGFibGUoSGl0ID0gdGhlLmJlYXRsZXMuc29uZ3MkQmlsbGJvYXJkLmhpdCwgWWVhciA9IHRoZS5iZWF0bGVzLnNvbmdzJFllYXIpDQpyb3VuZChwcm9wLnRhYmxlKHRhYmxlKHRoZS5iZWF0bGVzLnNvbmdzJEJpbGxib2FyZC5oaXQsIHRoZS5iZWF0bGVzLnNvbmdzJFllYXIpLCAxKSwgZGlnaXRzID0gMikNCnJvdW5kKHByb3AudGFibGUodGFibGUodGhlLmJlYXRsZXMuc29uZ3MkQmlsbGJvYXJkLmhpdCwgdGhlLmJlYXRsZXMuc29uZ3MkWWVhciksIDIpLCBkaWdpdHMgPSAyKQ0Kcm91bmQocHJvcC50YWJsZSh0YWJsZSh0aGUuYmVhdGxlcy5zb25ncyRCaWxsYm9hcmQuaGl0LCB0aGUuYmVhdGxlcy5zb25ncyRZZWFyKSksIGRpZ2l0cyA9IDIpDQpgYGANCg0KIyMjIEV4YW1wbGU6IGNvbnZlcnRpbmcgdGhlLmJlYXRsZXMuc29uZ3MkWWVhciB0byBmYWN0b3IgYW5kIHNob3dpbmcgaXQgaW4gdGFibGVzDQpgYGB7cn0NCmZhY3Rvcih0aGUuYmVhdGxlcy5zb25ncyRZZWFyKQ0KdGhlLmJlYXRsZXMuc29uZ3MkWWVhciA8LSBhcy5mYWN0b3IodGhlLmJlYXRsZXMuc29uZ3MkWWVhcikNCmNsYXNzKHRoZS5iZWF0bGVzLnNvbmdzJFllYXIpDQpzdW1tYXJ5KHRoZS5iZWF0bGVzLnNvbmdzJFllYXIpDQpwcm9wLnRhYmxlKCh0YWJsZSh0aGUuYmVhdGxlcy5zb25ncyRZZWFyKSkpDQpyb3VuZChwcm9wLnRhYmxlKCh0YWJsZSh0aGUuYmVhdGxlcy5zb25ncyRZZWFyKSkpLCBkaWdpdHMgPSAyKQ0KYGBgDQoNCiMjIyBUaGUgeHRhYnMoKSBmdW5jdGlvbg0KYHh0YWJzKH48Y29sdW1uIDE+ICsgPGNvbHVtbiAyPiwgPGRhdGFmcmFtZT4pYCAgDQpgYGB7cn0NCnh0YWJzKH5CaWxsYm9hcmQuaGl0ICsgWWVhciwgdGhlLmJlYXRsZXMuc29uZ3MpDQpgYGANCg0KIyMgV29ya2luZyB3aXRoIHZlY3RvcnMNCg0KIyMjIERpZmZlcmVuY2VzIGluIGluaXRpYWxpemluZyB2ZWN0b3JzIGFuZCBkYXRhZnJhbWUgY29sdW1ucw0KYDx2ZWN0b3I+IDwtIHJlcCg8dmFsdWU+LCA8dGltZXM+KWAgIA0KYDx2ZWN0b3I+IDwtIDx2YWx1ZT5gICANCmA8ZGF0YWZyYW1lPiQ8Y29sdW1uPiA8LSByZXAoPHZhbHVlPiwgPHRpbWVzPilgICANCmA8ZGF0YWZyYW1lPiQ8Y29sdW1uPiA8LSA8dmFsdWU+YCAgDQpgYGB7cn0NCnYgPC0gcmVwKDAsIDUpDQp2DQp2IDwtIDINCnYNCmRmIDwtIGRhdGEuZnJhbWUoYSA9IGMoMSwgMiwgMyksIGIgPSBjKDQsIDUsIDYpKQ0KZGYNCmRmJGEgPC0gcmVwKDEsIDMpDQpkZg0KZGYkYSA8LSAwDQpkZg0KYGBgDQoNCiMjIyBDb3VudGluZyB0aGUgbnVtYmVyIG9mIGVsZW1lbnRzIHdpdGggdGhlIHZhbHVlcyBvZiBgPHg+YCBpbiBhIHZlY3Rvcg0KMS4gIGA8dGFibGU+IDwtIHRhYmxlKDx2ZWN0b3I+KWAgIA0KYCAgPHRhYmxlPmAgICANCmAgIDx0YWJsZT5bbmFtZXMoPHRhYmxlPikgPT0gPHg+XWAgIA0KMi4gIGBzdW0oPHZlY3Rvcj4gPT0gPHg+KWANCjMuICBgbGVuZ3RoKHdoaWNoKDx2ZWN0b3I+ID09IDx4PikpICAgICMgd2hpY2goKSBpcyBsaWtlIFdIRVJFIGluIFNRTGANCmBgYHtyfQ0KdiA8LSBjKDEsIDIsIDEsIDMsIDIsIDQsIDUsIDEsIDMsIDEpDQp0IDwtIHRhYmxlKHYpDQp0DQp0W25hbWVzKHQpID09IDFdDQp0W25hbWVzKHQpID09ICIxIl0NCnN1bSh2ID09IDEpDQpsZW5ndGgod2hpY2godiA9PSAxKSkNCmBgYA0KDQojIyMgQXBwZW5kaW5nIGFuIGVsZW1lbnQgdG8gYSB2ZWN0b3INCmA8dmVjdG9yPiA8LSBhcHBlbmQoPHZlY3Rvcj4sIDxlbGVtZW50PikgICAjIHR5cGUgY29udmVyc2lvbiBvY2N1cnMgaWYgPGVsZW1lbnQ+IGlzIG9mIGRpZmZlcmVudCB0eXBlIHRoYW4gdltpXWAgIA0KYDx2ZWN0b3I+IDwtIGFwcGVuZCg8dmVjdG9yPiwgPGVsZW1lbnQ+LCBhZnRlciA9IDxuPikgICAjIGluc2VydCA8PT4gYXBwZW5kIGF0IGEgZGVzaXJlZCBsb2NhdGlvbmAgIA0KYDx2ZWN0b3I+IDwtIGFwcGVuZCg8dmVjdG9yPiwgTkEpYCAgDQpgYGB7cn0NCnYgPC0gYygxLCAyLCAxLCAzLCAyLCA0LCA1LCAxLCAzLCAxKQ0Kdg0KdiA8LSBhcHBlbmQodiwgTkEpDQp2IDwtIGFwcGVuZCh2LCBOQSwgYWZ0ZXIgPSA1KQ0Kdg0KdiA8LSBhcHBlbmQodiwgInMiKQ0Kdg0KYGBgDQoNCiMjIyBSZW1vdmluZyBOQXMgZnJvbSBhIHZlY3RvciBpbiBOQS1zZW5zaXRpdmUgZnVuY3Rpb25zDQpgPGZ1bmN0aW9uPig8dmVjdG9yPiwgbmEucm0gPSBUUlVFKWAgIA0KYGBge3J9DQp2IDwtIGMoMSwgMiwgMSwgMywgMiwgNCwgNSwgMSwgMywgMSkNCnYNCnYgPC0gYXBwZW5kKHYsIE5BKQ0KdiA8LSBhcHBlbmQodiwgTkEpDQptZWFuKHYpDQptZWFuKHYsIG5hLnJtID0gVFJVRSkNCmBgYA0KDQojIyMgU2VsZWN0aW5nIHZlY3RvciBlbGVtZW50cyB0aGF0IG1hdGNoIGNyaXRlcmlhLCB3aXRoIGNvbnRyb2xsaW5nIGZvciBOQXMgYW5kIE5hTnMNCg0KYDxudW1lcmljIHZlY3Rvcj4gPC0gYyg8bjE+LCA8bjI+LCA8bjM+LCAuLi4sIE5BLCAuLi5OYU4pYCAgDQpgPHNlbGVjdGVkPiA8LSA8bnVtZXJpYyB2ZWN0b3I+Wzxsb2dpY2FsIGNyaXRlcmlvbj4gJiAhaXMubmEoPG51bWVyaWMgdmVjdG9yPildICAjIGlzLm5hKCkgaXMgVFJVRSBmb3IgYm90aCBOQSBhbmQgTmFOYCAgDQpVc2luZyBgaXMubmEoKWAgaXMgdGhlIG9ubHkgd2F5IHRvIHRlc3QgaWYgYDxzb21ldGhpbmc+YCBpcyBOQSAoYDxzb21ldGhpbmc+ID09IE5BYCBkb2VzIG5vdCB3b3JrKS4NCmBgYHtyfQ0KdiA8LSBjKDEsIDIsIDEsIDMsIE5BLCA0LCA1LCAxLCAzLCBOYU4sIDEpDQp2DQp2IDwtIHZbdiA+IDEgJiAhaXMubmEodildDQp2DQpgYGANCg0KIyMgV29ya2luZyB3aXRoIHN0cmluZ3MNCg0KIyMjIFNvbWUgb2YgdGhlIGJhc2ljIHN0cmluZyBmdW5jdGlvbnMNCmAjIGluc3RhbGwucGFja2FnZXMoInN0cmluZ3IiKWAgIA0KYGxpYnJhcnkoc3RyaW5ncilgICANCmBuY2hhcig8cz4pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBzdHJpbmcgbGVuZ3RoYCAgDQpgc3RyX2xlbmd0aCg8cz4pICAgICAgICAgICAgICAgICAgICAgICAgICMgc3RyaW5nIGxlbmd0aDsgc3RyX2xlbmd0aCgpIGlzIGZyb20gc3RyaW5ncmAgIA0KYHN1YnN0cig8cz4sIDxzdGFydCBpbmRleD4sIDxlbmQgaW5kZXg+KSAjIHN1YnN0cmluZ2AgIA0KYHRvdXBwZXIoPHM+KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIHVwcGVyIGNhc2UgbGV0dGVyc2AgIA0KYHRvbG93ZXIoPHM+KSAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIGxvd2VyIGNhc2UgbGV0dGVyc2AgIA0KYGdyZXBsKDxzMT4sIDxzMj4pICAgICAgICAgICAgICAgICAgICAgICAjIGNvbnRhaW5zOyBUUlVFIGlmIDxzMj4gY29udGFpbnMgPHMxPmAgIA0KYHN0cl9kZXRlY3QoPHMxPiwgPHMyPikgICAgICAgICAgICAgICAgICAjIGNvbnRhaW5zOyBUUlVFIGlmIDxzMT4gY29udGFpbnMgPHMyPjsgc3RyX2RldGVjdCgpIGlzIGZyb20gc3RyaW5ncmAgIA0KYHBhc3RlKDxzMT4sIDxzMj4sIHNlcCA9ICIiKSAgICAgICAgICAgICAjIGNvbmNhdGVuYXRlIChyZXN1bHQ6IDxzMT48czI+OyA8czE+IDxzMj4sIGlmIHNlcCA9ICIiIG9taXR0ZWQpYCAgDQpgc3ViKDxzMT4sIDxzMj4sIDxzPikgICAgICAgICAgICAgICAgICAgICMgc3Vic3RyaW5nIHJlcGxhY2VtZW50OiByZXBsYWNlIDxzMT4gaW4gPHM+IHdpdGggPHMyPmAgIA0KYHN0cnNwbGl0KDxzPiwgPHJlZ2V4PikgICAgICAgICAgICAgICAgICAjIHNwbGl0ICh0aGUgdHlwZSBvZiB0aGUgcmVzdWx0IGlzIGxpc3QpYCAgDQpgYGB7cn0NCiMgaW5zdGFsbC5wYWNrYWdlcygic3RyaW5nciIpDQpsaWJyYXJ5KHN0cmluZ3IpDQp0aXRsZSA8LSB0aGUuYmVhdGxlcy5zb25ncyRUaXRsZVsxM10NCnRpdGxlDQpuY2hhcih0aXRsZSkNCnN0cl9sZW5ndGgodGl0bGUpDQpncmVwbCgiWW91IiwgdGl0bGUpDQpzdHJfZGV0ZWN0KHRpdGxlLCAiWW91IikNCmBgYA0KDQojIyMgU3BsaXR0aW5nIHN0cmluZ3MgdG8gd29yZHMNCmBzdHJzcGxpdCg8cz4sIDxyZWdleD4pICAgICAgICAgICAgICAgICAgIyBzcGxpdCAodGhlIHR5cGUgb2YgdGhlIHJlc3VsdCBpcyBsaXN0KWAgIA0KYGBge3J9DQp0aXRsZSA8LSB0aGUuYmVhdGxlcy5zb25ncyRUaXRsZVsxM10NCndvcmRzLmluLnRpdGxlIDwtIHN0cnNwbGl0KHRpdGxlLCAiICIpDQp3b3Jkcy5pbi50aXRsZQ0Kd29yZHMuaW4udGl0bGUgPC0gc3Ryc3BsaXQodGl0bGUsICIgIikNCndvcmRzLmluLnRpdGxlDQp3b3Jkcy5pbi50aXRsZSA8LSBzdHJzcGxpdCgiQWxsICBNeSAgICBMb3ZpbmciLCAiICIpDQp3b3Jkcy5pbi50aXRsZQ0Kd29yZHMuaW4udGl0bGUgPC0gdW5saXN0KHdvcmRzLmluLnRpdGxlKQ0Kd29yZHMuaW4udGl0bGUgPC0gd29yZHMuaW4udGl0bGVbd29yZHMuaW4udGl0bGUgIT0gIiJdDQp3b3Jkcy5pbi50aXRsZQ0KdGl0bGUgPC0gcGFzdGUod29yZHMuaW4udGl0bGVbMV0sIHdvcmRzLmluLnRpdGxlWzJdLCB3b3Jkcy5pbi50aXRsZVszXSkNCnRpdGxlDQp0aXRsZSA8LSBwYXN0ZSh3b3Jkcy5pbi50aXRsZVsxXSwgd29yZHMuaW4udGl0bGVbMl0sIHdvcmRzLmluLnRpdGxlWzNdLCBzZXAgPSAiIikNCnRpdGxlDQpgYGANCg0KIyMgUmVzb3VyY2VzLCByZWFkaW5ncywgcmVmZXJlbmNlcw0KDQpSIFR1dG9yaWFscywgPGh0dHA6Ly93d3cuZW5kbWVtby5jb20vcHJvZ3JhbS9SLz4gIA0KUjogQSBCZWdpbm5lcidzIEd1aWRlIChieSBTaGFyb24gTWFjaGxpcyksIDxodHRwOi8vd3d3LnRmcmVjLndzdS5lZHUvVEZSRUNvbmx5L3I0YmVnaW5uZXJzX3YzLnBkZj4gIA0KR3JhcGhzIHdpdGggZ2dwbG90MiwgPGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzLz4NCg==