Functions and Loops
- Use these when you want to run the same code many times on different things.
Syntax for functions:
function_name <- function(inputs,inputs,inputs){
# do stuff
.
.
.
return(output)
}
Syntax for loops:
for(A in B){
# do something
}
Usage example:
# example of a function to get the hypotenuse of a triangle from the short and middle side
get_hypotenuse <- function(shortside, middleside) {
# shortside and middleside are the inputs (arguments)
hypotenuse_squared<-shortside**2+middleside**2
hypotenuse<-sqrt(hypotenuse_squared)
return(hypotenuse)# hypotenuse is the output
}
shortsides <- c(3,4,5,8,1,3)
middlesides <- c(8,10,12,14,2,1)
# now lets run it multiple times in a loop
# make an empty vector to store the hypotenuses
hypotenuses <- c()
# we use these 'i' values as indexes to refer to items in our two vectors shortsides and middlesides
for(i in 1:length(shortsides)){
# run our function
hypotenuse <- get_hypotenuse(shortsides[i],middlesides[i])
# update the hypotenuse vector, so it contains the hypotenuses we've already calculated, plus the one we just calculated in this iteration of the loop
hypotenuses <- c(hypotenuses,hypotenuse)
}
# after the loop the hypotenuses vector will be full
Categorising functions
A function for categorising the environment, based on the length of the maximum growing season (mgs).
environment <- function(mgs) {
if(mgs < 4) {
return("dry")
} else if(mgs < 8) {
return("typical")
} else {
return("fertile")
}
}
Data visualisation
We use ggplot()
. These plots have a basic structure like this:
data %>% ggplot(aes(x=colA,y=colB,colour=colC)) +geom_point()/geom_line()/geom_col()
Optional extras:
+labs(x="X axis title",y="Y axis title",colour="Legend title",title="Plot title",caption="Caption for plot")
[if you use fill
instead of colour
, then you will use fill="Legend title"
]
+theme_classic()
# this is a nice theme
- If you are printing in black and white, you can use
shape
instead of colour
When to use which type of graph?
If you have a lot of x values:
geom_point()
is good when your observations come from different sources, e.g. the number of covid deaths in people of different ages, and you want to communicate the relationship between your x and y values
geom_line()
and geom_smooth()
are better for plotting multiple observations from a single source, e.g. the number of covid cases in a country over multiple weeks, where you want to communicate the change in your y value over time
If you have only a few x values:
geom_col()
is good for when you have only one y observation per x value
geom_violin()
is good when your x values are larger categories with multiple y observations per x value, and you want to compare the distribution of the y values between the different groups
geom_histogram()
is used to examine the distribution of a single variable. It’s more used in the methods section of papers rather than the results, for example to show what your sample of data looks like.
geom_histogram()
Below is a histogram showing the distribution of diamonds in the diamond dataset by their carat.
diamonds%>%
# just put the variable you want to examine the distribution of
ggplot(aes(carat))+
geom_histogram()+
theme_classic()+
labs(title="Distribution of diamonds by carat")

Most of the diamonds in the dataset are less than 1 carat.
geom_point()
With colours:
MGS_nettle%>%ggplot(aes(x=Population,y=Langs,colour=MGS_category))+
geom_point()+
labs(x='Population (in millions)',y='Number of languages',colour="Environment")+
theme_classic() # a nicer presentation than the default ggplot theme

With shapes:
MGS_nettle%>%ggplot(aes(x=Population,y=Langs,shape=MGS_category))+
geom_point()+
labs(x='Population (in millions)',y='Number of languages',shape="Environment")+
theme_classic() # a nicer presentation than the default ggplot theme

Adding labels to points
geom_text()
and geom_text_repel()
can be used to add labels to points on your plot. You will need to install and load the library ggrepel
to use geom_text_repel
, which positions the labels nicely so they don’t overlap with anything, and you’ll need to add an argument label
to your aes()
function in ggplot()
, to specify which column to get the labels from.
library(ggrepel)
MGS_nettle%>%
sample_n(15)%>%
ggplot(aes(x=Population,y=log10(Langs),label=Country)) + geom_point(aes(colour=MGS_category)) + geom_text_repel()+theme_classic()

geom_line() and geom_smooth()
Use geom_line()
for a jagged line:
weird_names%>%
ggplot(aes(x=year, y=TotalN)) +
geom_line()+
labs(x='Year',y='Total number of weird names',title='Popularity of weird names over time')+
theme_classic()

Use geom_smooth()
to get a smoothed line:
weird_names%>%
ggplot(aes(x=year, y=TotalN)) +
geom_smooth()+
labs(x='Year',y='Total number of weird names',title='Popularity of weird names over time')+
theme_classic()

geom_col() - bar plots
barnnamn%>%
filter(name=='Lee')%>%
ggplot(aes(x=year,y=n))+geom_col()+
labs(title="Babies named Lee",x="Year",y="Number of babies")+
scale_x_continuous(breaks=c(2010:2017))+ # to specify the breaks on the x-axis
theme_classic()

Reordering items on the x-axis
- add a
reorder()
function to the aes()
for the x-axis
- works the same way as
arrange()
MGS_nettle%>%
sample_n(6)%>%
ggplot(aes(x=reorder(Country,Langs),y=Langs,fill=MGS_category))+
geom_col()+
labs(x='Country',y='Number of languages')+
scale_fill_discrete("Environment",c("dry","fertile"))+
theme_classic()

Grouped bar plots
- Use
fill=VarX
in the aes()
of your ggplot call to colour the bars by a third variable.
- Use
position="dodge()"
in your geom_col()
to have the bars side-by-side
- Use
scale_x_continuous
(for continuous variables) to specify the breaks on the x-axis; if you have discrete (e.g. categorical) variables, the function is scale_x_discrete
barnnamn%>%
filter(name=='Lee')%>%
ggplot(aes(x=year,y=n,fill=sex))+
geom_col(position="dodge")+ # bars side-by-side
labs(title="Babies named Lee",x="Year",y="Number of babies",fill="Sex")+
scale_x_continuous(breaks=c(2010:2017))+ # to specify the breaks on the x-axis
theme_classic()

If you don’t use position="dodge"
, the different coloured bars will be stacked on top of eachother.
barnnamn%>%
filter(name=='Lee')%>%
ggplot(aes(x=year,y=n,fill=sex))+
geom_col()+
labs(title="Babies named Lee",x="Year",y="Number of babies",fill="Sex")+
scale_x_continuous(breaks=c(2010:2017))+ # to specify the breaks on the x-axis
theme_classic()

Percent stacked bar plots
Use position="fill"
to show proportions instead of counts:
barnnamn%>%
filter(name=='Lee')%>%
ggplot(aes(x=year,y=n,fill=sex))+
geom_col(position="fill")+ # to show proportions
labs(title="Babies named Lee",x="Year",y="Proportion",fill="Sex")+
scale_x_continuous(breaks=c(2010:2017))+ # to specify the breaks on the x-axis
theme_classic()

geom_violin()
This is good to compare the distribution of the data between groups.
# mpg is a dataset of cars
mpg %>%
# the hwy column tells you how many miles a car can go on 1 gallon of petrol
# class is the type of car
ggplot( aes(x=reorder(class,hwy), y=hwy)) +
geom_violin() +
theme_classic()+
# we can use the stat_summary point to add points for summary statistics, here we show the mean
stat_summary(fun=mean, geom="point")+
labs(x="Car type",y="miles per gallon",title="Efficiency of cars on the highway",caption = "The mean miles per gallon for each car is indicated with a point")

Making data points less clumped or less spread apart
- Log transformations (as in the graph above) are used to make data that is very spread apart/clumped together easier to visualise.
- The most common base is 10. When we take the log10 of a number, \(x\), we are trying to find a number \(y\) such that \(10^y=x\). The function for this in R is just
log10(x)
. For very far apart values of \(x\), taking \(log10(x)\) will give you values that are closer together, while for very close together values of \(x\), \(log10(x)\) will give you values that are further apart. This means that \(log10(x)\) is often nicer to plot than \(x\).
- It is perfectly acceptable to do these kinds of transformations to your data, in order to make it easier to visualise.
Making multiple plots
facet_wrap()
and facet_grid()
let you produce multiple different plots for each value in a column/columns
facet_wrap(~ColA)
–> will make different versions of the same graph for every different value in colA, all wrapped around each other (works well when you have a lot of different values of a variable)
facet_grid(colA~colB)
–> makes a grid of graphs, where the different values of colA are the rows in the grid, and the different values of colB are the columns in the grid. This works best when colA and colB don’t have too many different values.
+theme_minimal()
is a nice theme to use with faceted plots (compared to our usual +theme_classic()
)
barnnamn %>% filter(name=="Lee") %>% ggplot(aes(x=year, y=n)) + geom_line() + facet_wrap("sex")+theme_minimal()

barnnamn %>%
mutate(FinalLetter = str_sub(name, -1, -1)) %>%
mutate(Final = if_else(FinalLetter %in% c("a",'ä','ö','å', "e", "i", "o", "u", "y"), "vowel", "consonant")) %>%
group_by(Final,year,sex) %>%
summarise(total=sum(n)) %>%
ggplot(aes(x=year, y=total)) + geom_line() + facet_grid(sex ~ Final)+theme_minimal()

Changing the number and look of ticks on the x axis
You can add more ticks to a continuous x-axis using the function +scale_x_continuous(n.breaks=num_ticks)
. The number of ticks that you enter won’t always be the exact number of labels it makes, but it will try to get it as close as possible while still ensuring nice break labels.
We can also make the break labels nicer by changing their angle and position. This is done with the function +theme(axis.text.x = element_text(angle=45,vjust=0.5))
, and again, just play around with the values for angle
and vjust
(vertical adjustment) until you get something that looks right. If vjust
doesn’t work for you, there is also an argument hjust
(horizontal adjustment) that you can add.
barnnamn %>%
mutate(FinalLetter = str_sub(name, -1, -1)) %>%
mutate(Final = if_else(FinalLetter %in% c("a",'ä','ö','å', "e", "i", "o", "u", "y"), "vowel", "consonant")) %>%
group_by(Final,year,sex) %>%
summarise(total=sum(n)) %>%
ggplot(aes(x=year, y=total)) + geom_line() + facet_grid(sex ~ Final)+theme_minimal()+scale_x_continuous(n.breaks=16)+theme(axis.text.x = element_text(angle=45,vjust=0.5))

Mapping
We can get maps from the packages rnaturalearth
and rnaturalearthdata
, and plot them using the ggplot
function geom_sf()
. sf
is a file format for storing maps.
# required packages
library(rnaturalearth)
library(rnaturalearthdata)
library(ggrepel)
# these packages may not function properly if you don't also have rgeos installed; if you get an error, run install.packages('rgeos') in your console and then try again
# the function ne_countries will give you country maps, if you don't specify which countries, it gives you the whole world.
# Use returnclass="sf" to make sure you get an sf format back, as this is what geom_sf() works with
world <- ne_countries(returnclass = "sf")
# you need to have latitude and longitude information for the things you want to plot -- I get this from google maps -- just right click on a place in google maps (on a computer), and the latitude and longitude will show up and you can copy them
type <- c("rental","parent's house","in-law's house","rental","rental")
latitude <- c(59.865,-33.879,50.874,35.664,-35.284)
longitude <- c(17.641,151.112,6.037,139.482,149.136)
places <- data.frame(type,latitude,longitude)
# start by plotting the map as a base layer
# this always has to come first -- it won't work if you start with the points and try to add the map after
world%>%
ggplot()+
geom_sf()+
# then you can add points on top of it -- note that you need to specify the data, because this comes from a different dataset to the one at the top of the pipe
geom_point(data = places,aes(x=longitude,y=latitude,colour=type))+
theme_void()+ # this is a nice theme for maps; it gets rid of the axes
labs(title="Places I've Lived")

You can use the ‘country’ argument in ne_countries()
to get a map of a single country.
Australia <- ne_countries(country="Australia",returnclass = "sf")
australian_places <- places%>%filter(latitude<0)
Australia%>%
ggplot()+
geom_sf()+
# since the data isn't coming from the top of the pipe, you need to tell it the data and aesthetics
geom_point(data = australian_places,aes(x=longitude,y=latitude))+
# you could use labels instead of colour -- but again you need to tell it the aesthesics and data to use
geom_text_repel(data = australian_places,aes(x=longitude,y=latitude,label=type))+
labs(title="Places I've Lived")+
theme_void()

You can add multiple countries if you like:
map <- ne_countries(country=c("Australia","New Zealand"),returnclass = "sf")
australian_places <- places%>%filter(latitude<0)
map%>%
ggplot()+
geom_sf()+
# since the data isn't coming from the top of the pipe, you need to tell it the data and aesthetics
geom_point(data = australian_places,aes(x=longitude,y=latitude))+
# you could use labels instead of colour -- but again you need to tell it the aesthesics and data to use
geom_text_repel(data = australian_places,aes(x=longitude,y=latitude,label=type))+
labs(title="Places I've Lived")+
theme_void()

Or you can use the argument continent
to specify an entire continent:
Asia <- ne_countries(continent="Asia",returnclass = "sf")
Asia%>%
ggplot()+
geom_sf()+
theme_void()

A full list of available countries and continents is shown below:
library(reactable)
data <- countries50
countries <- data@data
countries%>%
select(name,continent)%>%
unique()%>%
arrange(continent,name)%>%
rename(country=name)%>%
reactable(searchable = TRUE,filterable = TRUE)
Visualing variation across multiple variables
If you have 3 or 4 variables, I would try to visualise the third and fourth variables by using features like colour, or by making multiple plots (with factet_wrap()
and facet_grid()
).
However, if you have a whole bunch of variables that are all related/form a cohesive set, and you want to get an idea of how similar or different items are overall, based on their values for all these variables, you can do this by creating a distance matrix. A distance matrix shows the overall distances between all the items in your data, by taking the sum of their distances based on each variable (for more explanation see the notes from lecture 8).
Note that this only works if your data is numerical, or if it has been binarised. For numerical data, make sure you normalise the data so that values of each variable are comparable between different items (e.g. instead of using raw counts, use proportions).
There are two different kinds of visualisations you can make from a distance matrix.
- You can attempt to plot these distances, by transforming them into 2 or 3 dimensional coordinates, and plotting the items as points along these dimensions.
- You can cluster items that are close to eachother together repeatedly, until you get a branching tree.
The first method is called multidimensional scaling, and the second is called cluster analysis.
Multidimensional scaling works well if the data is easily reduced to two or three dimensions, but this isn’t always possible. Cluster analysis works better when you have complicated data which varies along multiple dimensions/with many distinct groups.
Multidimensional scaling
- Convert the data into a distance matrix–make sure it is appropriately normalised/binarised first–using the function
dist()
. Add method="binary"
if the data is binarised. If it’s numerical, the default method (euclidean
) is appropriate.
- Find coordinates for these distances using
cmdscale(distances, k=2)
. k sets the number of dimensions to use; the default is two.
cmdscale()
will make coordinates for your distances as well as it can, but it can’t always represent the distances perfectly, so you should check how closely the distances between the coordinates matches the actual distances. This is called calculating the stress on the coordinates, and we will make our own stress()
function to do this.
# d is the original distances, D is the distances between the coordinates made by cmdscale()
stress <- function(d, D){
sqrt(sum((d - D) ^ 2) / sum(d ^ 2))
}
This is how stress values are interpreted:
Stress >= 0.2 |
Poor |
0.1 <= Stress < 0.2 |
Fair |
0.05 <= Stress < 0.1 |
Good |
Stress < 0.05 |
Excellent |
- If the stress is okay, then you can plot the coordinates in two dimensions. If it’s not okay, then probably a three-dimensional or higher solution is needed to accurately represent the data. You can check how many dimesions are needed by playing around with different values of k in the
cmdscale()
function, and then checking the stress on those coordinates (see notes from lecture 8).
You will also need the functions column_to_rownames()
and as_dataframe(rownames="column_name")
to go between the data format required by dist()
, and that needed for plotting.
Below is an example using euclidean distances (i.e. with numerical data), to visualise distances between different Japanese case particles in terms of their functions.
library(ggrepel)
There were 26 warnings (use warnings() to see them)
# toy data about the frequency of use (as a proportion of the total frequency for each particle) with different cases for some Japanese particles
particle <- c("ga","ni","de","wa","o")
nominative <- c(0.95,0,0,0.5,0)
genitive <- c(0.05,0,0,0,0)
locative <- c(0,0.6,0.7,0.1,0)
instrumental <- c(0,0,0.3,0.05,0)
allative <- c(0,0.3,0,0.1,0)
accusative <- c(0,0.1,0,0.35,1)
particles <- data.frame(particle,nominative,genitive,locative,instrumental,allative,accusative)
# 1. Make the distance matrix
particles%>%
# turn the first column into rownames first -- your columns should only contain measurements of your variables of interest
column_to_rownames("particle")%>%
dist()-> distances
# 2. Make coordinates
# use k=3, 4 etc. for higher-dimensional solutions
coordinates <- cmdscale(distances,k=2)
# 3. Check stress on coordinates
stress(distances,dist(coordinates))
[1] 0.09332946
Here, the stress is less than 0.1 so the two-dimensional coordinates represent the distances well, and the data can be easily plotted
# 3. Plot coordinates
coordinates%>%
# put the rownames into a column first
as_data_frame(rownames="particle")%>%
ggplot(aes(x=V1,y=V2,label=particle))+geom_point()+geom_text_repel()+theme_classic()->plot1
plot1

You can see that de and ni have similar functions, as they group together, while ga and o have opposite functions, with wa falling in between ga and o, but closer to ga. This is because de and ni both have to do with location, while ga is nominative and o is accusative. But wa can take both nominative and accusative case, because it’s a topic marker–that’s why it falls in between ga and o. However, nominatively marked nouns are more often topics than accusatively marked nouns, which is why it is closer to ga than to o.
Below is an example with binary distances, using cognacy data to determine similarities between different dialects of Japanese.
japonic <- read_csv("data/japvocabmatrix.csv")
japonic%>%
rename(language=X)%>%
# excluded dead languages and Okinawa because it is not a dialect and will mess up the scale on the map by being super far away
filter(language!="Old_Japanese",language!="Middle_Japanese",language!="Okinawa")%>%
column_to_rownames("language")%>%
dist(method = "binary")->distances
coordinates <- cmdscale(distances,k=2)
coordinates%>%
as_data_frame(rownames="Location")%>%
ggplot(aes(x=V2,y=-V1,label=Location))+
geom_point()+
geom_text_repel()+
theme_classic()+
labs(x="Conservative versus progressive",y="South to north")

Here, instead of having distinct groups, we actually have a continuum which we can label as reflecting the spatial location (south to north) on the one hand, as well as the conservativeness of the dialect on the other hand–if you know about Japanese dialectology.
The stress on this representation is actually fairly high
stress(distances,dist(coordinates))
[1] 0.5535626
However, a three-dimensional solution is only marginally better:
higher_solution <- cmdscale(distances,k=3)
stress(distances,dist(higher_solution))
[1] 0.4809971
Here, the high stress value is likely because we have a lot of languages, so it may be that it is hard to get the correct positioning of the languages within each small group. That is, the spatial relationship between languages that are close to eachother may not be very accurate. However, the main dialect groups and the large distances in this figure are well-represented (based on what we already know about Japonic), so I think it is still a good representation–as long as you warn people not to look at the small distances. If this two-dimensional representation was really a bad represenation, we would expect a bigger difference between the stress levels of a two-dimensional versus three-dimensional solution.
Cluster analysis
If you want to know about the close relationships (small distances) as well, you could use a cluster analysis
clusters <- hclust(distances,method="ward.D2") # method="ward.D2" usually produces good results
plot(clusters)

Compared to multidimensional scaling, cluster analyses are better at showing distinct groups when you have a lot of data points. From the first two splits in the tree here, we already have four distinct groups–they basically represent the north and east of country, versus the south and west of the country. However, in the multidimensional scaling representation the western and eastern groups ended up squished together (it was hard to clearly separate them), so that could also contribute to high stress levels. This is probably because the eastern and western groups are less distinct from eachother than the northern and southern groups (which were preserved), and like we said the multidimensional scaling representation just shows the general trends and not the small differences.
So, if I were to present this data, I would include both the multidimensional scaling analysis (to show the overall trends), as well as the cluster analysis (to show distinct, including smaller-level, groupings).
When to use which method?
If you only have a few data points (like in the first example with the Japanese particles), multidimensional scaling and cluster analyses are usually equally good at representing the distances, because there’s enough space on the multidimensional scaled plot to keep all the groups distinct. So it’s just a matter of personal preference as to which visualisation you prefer, although if you think the data is hierarchically structured the cluster analysis would represent this hierarchical structure better.
If you have a lot of data points, however, it is hard to keep groups distinct on multidimensional scaling plots (as we saw with the Japanese data). In this case, the cluster analysis is better for showing the groupings, and it is only worth still using the multidimensional scaling plot if you think the dimensions are meaningful and represent some overall trends in the data (e.g. the north-south, conservative-progressive trends we saw in the Japanese data). If you cannot think of any meaningful labels for your dimensions, then probably all the multidimensional scaling analysis is doing is trying to represent distinct groups–rather than any kind of continuum–in which case I would just use a cluster analysis because that does grouping better when there’s lots of data points.
For instance, below is an example, plotting words based on their sensory modality ratings:
norms <- read_csv("data/lynott_connell_2009_modality.csv")
norms%>%
select(-PropertyBritish,-DominantModality)%>%
column_to_rownames("Word")%>%
sample_n(50)%>%
dist()->distances
coordinates <- cmdscale(distances)
coordinates%>%
as_data_frame(rownames="Word")%>%
ggplot(aes(x=V1,y=V2,label=Word))+
geom_point()+
geom_text_repel()+
theme_classic()

There groupings are not very clear, nor can we come up with any meaningful labels for these dimensions. In this case, I would just use a cluster analysis because at least then the groupings are clear:
clusters <- hclust(distances,method="ward.D2") # method="ward.D2" usually produces good results
plot(clusters)

We can see that the groupings are not well preserved in the multidimensional scaling plot from the high stress on the coordinates:
stress(distances,dist(coordinates))
[1] 0.2605559
A three-dimensional solution is able to represent the groupings better:
newcoordinates <- cmdscale(distances,k=3)
stress(distances,dist(newcoordinates))
[1] 0.09953989
We could extract the different groupings by plotting the dimensions one after the other... But this is a lot more work (and still it’s easier to see the groups in the cluster dendrogram), so I would just use the cluster analysis instead. I would only try plotting all the dimensions if you think they’re all meaningful.
plot(clusters)

LS0tDQp0aXRsZTogIlIgQ2hlYXRzaGVldCINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgDQotLS0NCg0KYGBge3IgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSx3YXJuaW5nID0gRkFMU0UsbWVzc2FnZSA9IEZBTFNFKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJhcm5uYW1uKQ0KYGBgDQoNCg0KIyBJbnN0YWxsaW5nIGFuZCBsb2FkaW5nIHBhY2thZ2VzDQoNCllvdSBvbmx5IG5lZWQgdG8gaW5zdGFsbCBwYWNrYWdlcyBvbmNlLCBidXQgeW91IG5lZWQgdG8gbG9hZCB0aGVtIGZvciBldmVyeSBuZXcgUiBzZXNzaW9uLiAgIA0KWW91IHNob3VsZCBkbyB0aGlzIGluIHRoZSBmaXJzdCBjaHVuayBpbiB5b3VyIFIgbWFya2Rvd24gZG9jdW1lbnQuDQoNCnwgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwqaW5zdGFsbGluZyp8YGluc3RhbGwucGFja2FnZXMoInBhY2thZ2VfbmFtZSIpYHwNCnwqbG9hZGluZyogICB8YGxpYnJhcnkocGFja2FnZV9uYW1lKWAgICAgICAgICAgIHwNCg0KVGhlIG1haW4gcGFja2FnZSB3ZSB1c2UgaXMgdGhlIGB0aWR5dmVyc2VgLiAgIA0KU28geW91IHNob3VsZCBzdGFydCBhbGwgeW91ciAuUm1kIHdpdGggYSBjaHVuayBvZiBjb2RlIGNvbnRhaW5pbmcgYGxpYnJhcnkodGlkeXZlcnNlKWAgKGFuZCBhbnkgb3RoZXIgbGliYXJpZXMvcGFja2FnZXMgeW91IG1heSBiZSB1c2luZykuDQoNCiMgUmVhZGluZyBpbiBkYXRhDQoNClRoZSBmdW5jdGlvbiB5b3UgdXNlIGRlcGVuZHMgb24gdGhlIHR5cGUgb2YgZmlsZSBiZWluZyByZWFkOg0KDQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgICAgICAgIHxgcmVhZF9jc3YoImNzdl9maWxlLmNzdiIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8dGFiIHNlcGFyYXRlZCB2YWx1ZXMgICAgICAgICAgfGByZWFkX3RzdigidHN2X2ZpbGUudHh0IilgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnxvdGhlciBzZXBhcmF0b3JzICAgICAgICAgICAgICB8YHJlYWRfZGVsaW0oImFueWZpbGUudHh0IixzZXA9IjsiKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfGZvciBub24gdXRmLTggZW5jb2RpbmdzICAgICAgIHxgcmVhZF9jc3YoImNzdl9maWxlLmNzdiIsbG9jYWxlID0gbG9jYWxlKGVuY29kaW5nID0gIlNoaWZ0X0pJUyIpKWB8DQp8aWYgeW91IGRvbid0IGtub3cgdGhlIGVuY29kaW5nfGBndWVzc19lbmNvZGluZygiY3N2X2ZpbGUuY3N2IilgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KQWx3YXlzIHVzZSAqKnBsYWluIHRleHQgZm9ybWF0cyoqICguY3N2IG9yIC50eHQgZmlsZXMpIGFuZCBub3QgZXhjZWwgKC54bHN4KSBmaWxlcy4gIA0KVXNlICoqVVRGLTggZW5jb2RpbmcqKiAoZXNwZWNpYWxseSBpZiB5b3UgaGF2ZSBub24tcm9tYW4gY2hhcmFjdGVycyEpLiAgDQpUaGUgYHJlYWRfYCBmdW5jdGlvbnMgYXNzdW1lIHlvdXIgZW5jb2RpbmcgaXMgVVRGLTguIEJ1dCBzb21ldGltZXMgeW91IGdldCBkYXRhIGZyb20gb3RoZXIgcGVvcGxlIHRoYXQgaXNuJ3QgVVRGLTgsIHRoZW4geW91IG11c3Qgc3BlY2lmeSB0aGUgZW5jb2Rpbmcgd2l0aCB0aGUgYGxvY2FsZWAgYXJndW1lbnQgKHlvdSBjYW4gdXNlIGl0IGluIGFueSByZWFkIGZ1bmN0aW9uKS4gSWYgeW91IGRvbid0IGtub3cgdGhlIGVuY29kaW5nLCBgZ3Vlc3NfZW5jb2RpbmdgIHdpbGwgZ3Vlc3MgaXQgZm9yIHlvdS4gIA0KRm9yIGhvdyB0byBjb252ZXJ0IGV4Y2VsIGRvY3VtZW50cyB0byBwbGFpbiB0ZXh0IGZvcm1hdHMsIGFuZCBjaGFuZ2UgdGhlIGVuY29kaW5nIHRvIFVURi04LCBzZWUgdGhlIG5vdGVzIGZyb20gTGVjdHVyZSAxLg0KDQojIEV4cGxvcmluZyBkYXRhc2V0cw0KDQpFeGFtcGxlcyB3aXRoIHRoZSBkYXRhc2V0IGBjYXJzYCwgdGhhdCBjb21lcyB3aXRoIFIuDQoNCnwqdG8gc2VlLi4uKiAgICAgICAgICAgICAgICAgICAgfCp1c2UuLi4qICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnx0aGUgZmlyc3QgZmV3IHJvd3MgICAgICAgICAgICAgfGBoZWFkKGNhcnMpYCAgICAgICAgICAgICAgIHwNCnx0aGUgbnVtYmVyIG9mIGNvbHVtbnMvdmFyaWFibGVzfGBsZW5ndGgoY2FycylgL2BuY29sKGNhcnMpYHwNCnx0aGUgbnVtYmVyIG9mIHJvd3MgICAgICAgICAgICAgfGBucm93KGNhcnMpYCAgICAgICAgICAgICAgIHwNCnx1bmlxdWUgcm93cyAgICAgICAgICAgICAgICAgICAgfGB1bmlxdWUoY2FycylgICAgICAgICAgICAgIHwNCnx1bmlxdWUgdmFscyBpbiBjb2x1bW4gICAgICAgICAgfGB1bmlxdWUoY2FycyRzcGVlZClgICAgICAgIHwNCnxyYW5nZSBvZiBudW1iZXJzIGluIGEgY29sdW1uICAgfGByYW5nZShjYXJzJHNwZWVkKWAgICAgICAgIHwNCnxtaW4gdmFsdWUgaW4gY29sdW1uICAgICAgICAgICAgfGBtaW4oY2FycyRzcGVlZClgICAgICAgICAgIHwNCnxtYXggdmFsdWUgaW4gY29sdW1uICAgICAgICAgICAgfGBtYXgoY2FycyRzcGVlZClgICAgICAgICAgIHwNCg0KIyBEYXRhIHdyYW5nbGluZw0KDQojIyBEYXRhZnJhbWVzDQoNCkl0IGlzIGVhc2llciBpZiB5b3UgZG8gdGhpcyB1c2luZyBwaXBlcy4gVGhlIHBpcGUgZnVuY3Rpb24gaXMgYCU+JWAuICANClRoZSBwaXBlIGZ1bmN0aW9uIHRlbGxzIFIgdG8gdXNlIHRoZSBvdXRwdXQgZnJvbSB0aGUgcHJldmlvdXMgZnVuY3Rpb24gYXMgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uICANCkl0IG1lYW5zIHlvdSBkb24ndCBoYXZlIHRvIGtlZXAgdHlwaW5nIHRoZSBuYW1lIG9mIHRoZSBkYXRhc2V0IGV2ZXJ5IHRpbWUgeW91IHVzZSB0aGUgZnVuY3Rpb24uICANClRvIHNhdmUgdGhlIG91dHB1dCBmcm9tIGEgcGlwZSwgYWRkIGFuIGFzc2lnbm1lbnQgb3BlcmF0b3IgYC0+YCBhdCB0aGUgZW5kLCBvciBgPC1gIGF0IHRoZSBiZWdpbm5pbmcuIEJlbG93IGFyZSB0d28gd2F5cyBvZiBkb2luZyB0aGUgc2FtZSB0aGluZzoNCg0KYGBgDQpjYXJzJT4lDQogIGZpbHRlcihzcGVlZDw0KSU+JQ0KICBzZWxlY3Qoc3BlZWQsZGlzdCktPnNsb3dfY2Fycw0KICANCnNsb3dfY2FycyA8LSBjYXJzJT4lDQogICAgICAgICAgICAgICBmaWx0ZXIoc3BlZWQ8NCklPiUNCiAgICAgICAgICAgICAgIHNlbGVjdChzcGVlZCxkaXN0KQ0KYGBgDQoNCnwqdG8uLi4qICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCp1c2UqLi4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8Tm90ZXMvRXhhbXBsZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwgDQp8KipmaWx0ZXIgcm93cyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JWZpbHRlcihzcGVlZD4yKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipzZWxlY3QgY29sdW1ucyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXNlbGVjdChzcGVlZCxkaXN0KWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Z2V0IG9ubHkgKip1bmlxdWUgcm93cyoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXVuaXF1ZSgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipzYW1wbGUqKiBpICoqcmFuZG9tIHJvd3MqKiAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXNhbXBsZV9uKGkpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipwdWxsKiogb3V0IGEgc2luZ2xlIGNvbHVtbiAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JXB1bGwoc3BlZWQpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfFlvdSBjYW4gdXNlIHRoZSB2ZWN0b3JzIGZ1bmN0aW9uczxiciAvPmluIHRoZSBuZXh0IHNlY3Rpb24gb24gdGhpcyBjb2x1bW4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KiphcnJhbmdlKiogYnkgYSBjb2x1bW4sIGluIGFzY2VuZGluZyBvcmRlciAgICAgICAgICAgIHxgY2FycyU+JWFycmFuZ2Uoc3BlZWQpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8YXJyYW5nZSBieSBhIGNvbHVtbiwgaW4gZGVzY2VuZGluZyBvcmRlciAgICAgICAgICAgICAgIHxgY2FycyU+JWFycmFuZ2UoLXNwZWVkKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipyZW5hbWUgYSBjb2x1bW4qKiwgb2xkbmFtZSwgdG8gbmV3bmFtZSAgICAgICAgICAgICAgIHxgY2FycyU+JXJlbmFtZShuZXduYW1lPW9sZG5hbWVgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KiphZGQgYSBuZXcgY29sdW1uKiosIG5ld2NvbCAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JW11dGF0ZShuZXdjb2w9IC4uLilgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHBlcmZvcm0gc29tZSBvcGVyYXRpb25zIHRvIGdlbmVyYXRlIGRhdGEgZm9yIG5ld2NvbCBpbiAuLi4gKHNlZSB0aGUgVmVjdG9ycyBzZWN0aW9uIGZvciBpZGVhcykgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8cmVmZXIgdG8gdGhlIHZhcmlhYmxlIGJlaW5nIHBpcGVkICAgICAgICAgICAgICAgICAgICAgIHxgLmAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiVtdXRhdGUoSUQ9MTpucm93KC4pKSB3aGVyZSBgLmAgaXMgYGNhcnNgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipjYXRlZ29yaXNlIGRhdGEqKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyU+JW11dGF0ZShjYXQ9aWZfZWxzZShjLHYxLHYyKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIG11dGF0ZShzcGVlZF9jYXQ9aWZfZWxzZShzcGVlZDw1LCdzbG93JywnZmFzdCcpICMgZm9yID4gMiBjYXRlZ29yaWVzLCB3cml0ZSBhIGZ1bmN0aW9uIHRvIGNhdGVnb3Jpc2UgdGhlbSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8YWRkIG5ldyB2YWx1ZXMsICoqd29ya2luZyBvbiBncm91cHMgd2l0aGluIGNvbHVtbnMqKiAgIHxgbXV0YXRlKClgIHdpdGggYFtdYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIG11dGF0ZShzbG93X2Rpc3Q9c3VtKGRpc3Rbc3BlZWRfY2F0PT0nc2xvdyddKSxmYXN0X2Rpc3Q9c3VtKGRpc3Rbc3BlZWRfY2F0PSdmYXN0J10pICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8YWRkIG5ldyB2YWx1ZXMsICoqd29ya2luZyBvbiBncm91cHMgd2l0aGluIGNvbHVtbnMqKiAgIHxgbXV0YXRlKClgIHdpdGggYGdyb3VwX2J5KClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIGdyb3VwX2J5KHNwZWVkX2NhdCklPiU8YnIgLz4gIG11dGF0ZSh0b3RhbF9kaXN0PXN1bShkaXN0KSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KipjYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzKiogb3ZlciBjb2x1bW5zICAgICAgICAgIHxgc3VtbWFyaXNlKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIHN1bW1hcmlzZShtZWFuKHNwZWVkKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Y2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljcyBvdmVyIGdyb3VwcyB3aXRoaW4gY29sdW1uc3xgZ3JvdXBfYnkoKWAgKyBgc3VtbWFyaXNlKClgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGNhcnMlPiU8YnIgLz4gIGdyb3VwX2J5KHNwZWVkX2NhdCklPiU8YnIgLz4gIHN1bW1hcmlzZShtZWFuKHNwZWVkKSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Y2FsY3VsYXRlIG9uIGVhY2ggcm93IHNlcGFyYXRlbHkgICAgICAgICAgICAgICAgICAgICAgIHxgcm93d2lzZSgpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfG5ldHRsZSU+JTxiciAvPiAgcm93d2lzZSgpJT4lPGJyIC8+ICBtdXRhdGUoZW52aXJvbm1lbnQ9TUdTX2NhdGVnb3Jpc2UoTUdTKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8KiphZGQgZGF0YSBmcm9tIGRhdEIgdG8gZGF0QSoqLCBtYXRjaGluZyBvbiBjb2xBICsgY29sQnxgbGVmdF9qb2luKGRhdEEsZGF0QixieT1jKCJjb2xBIiwiY29sQiIpKWAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHJvd3MgaW4gZGF0QiB0aGF0IGRvbid0IGhhdmUgYSBtYXRjaCBpbiBkYXRBIHdpbGwgYmUgbG9zdC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgcmlnaHRfam9pbihkYXRCLGRhdEEsYnk9YygiY29sQSIsImNvbEIiKSlgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHJvd3MgaW4gZGF0QiB0aGF0IGRvbid0IGhhdmUgYSBtYXRjaCBpbiBkYXRBIHdpbGwgYmUgbG9zdC4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Kipjb21iaW5lIEFMTCBkYXRhIGluIGRhdEEgYW5kIGRhdEIqKiAgICAgICAgICAgICAgICAgIHxgZnVsbF9qb2luKGRhdEEsZGF0QixieT0iY29sQSIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfHRoZSBgYnlgIGFyZ3VtZW50IGlzIG9wdGlvbmFsOyB1c2UgaXQgdG8gc3BlY2lmeSBjb2x1bW5zIHRoYXQgeW91IHdhbnQgbWVyZ2VkLiBBbGwgcm93cyBpbiBib3RoIGRhdGFzZXRzIGFyZSBrZXB0LiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICANCnwqKmdldCBPTkxZIGRhdGEgaW4gYm90aCBkYXRBIGFuZCBkYXRCKiogICAgICAgICAgICAgICAgfGBpbm5lcl9qb2luKGRhdEEsZGF0QixieT0iY29sQSIpYCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8cm93cyB0aGF0IGhhdmUgYSB2YWx1ZSBpbiBjb2xBIGluIG9uZSBkYXRhc2V0LCBidXQgbm90IHRoZSBvdGhlciwgd2lsbCBiZSBsb3N0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnxtYWtlIGRhdGEgKip3aWRlcioqIChhZGQgbW9yZSBjb2x1bW5zKSAgICAgICAgICAgICAgICAgfGBkYXQlPiVwaXZvdF93aWRlcihuYW1lc19mcm9tPWNvbEEsdmFsdWVzX2Zyb209Y29sQix2YWx1ZXNfZmlsbD0wYCAgICB8TmFtZXMgZm9yIHRoZSBuZXcgY29scyBjb21lIGZyb20gY29sQSwgYW5kIHZhbHVlcyB0byBmaWxsIHRoZW0gY29tZSBmcm9tIGNvbEIuIFVzZSB2YWx1ZXNfZmlsbCB0byBzcGVjaWZ5IGhvdyBOQXMgc2hvdWxkIGJlIHJlcHJlc2VudGVkIChkZWZhdWx0IGlzIE5BKXwNCnxtYWtlIGRhdGEgKipsb25nZXIqKiAocmVtb3ZlIGNvbHVtbnMpICAgICAgICAgICAgICAgICAgfGBkYXQlPiVwaXZvdF9sb25nZXIoYygiY29sQSIsImNvbEIiKSxuYW1lc190bz0iY29sRCIsdmFsdWVzX3RvPSJjb2xFImB8Y29sQSArIGNvbEIgYXJlIGNvbGxhcHNlZCBpbnRvIGEgbmV3IGNvbCwgY29sRCwgYW5kIHRoZWlyIHZhbHVlcyB3aWxsIGdvIHRvIGEgbmV3IGNvbCwgImNvbEUiICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgDQoNCiMjIFZlY3RvcnMNCg0KWW91IGNhbiB0cnkgb3V0IHRoZXNlIGZ1bmN0aW9ucyB5b3Vyc2VsZjsgdGhlIGBjYXJzYCBhbmQgYGlyaXNgIGRhdGFzZXRzIGJvdGggY29tZSB3aXRoIFIuDQoNCnwqdG8uLi4qICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwqdXNlKi4uLiAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfHNvcnQgaXRlbXMgKGFscGhhYmV0aWNhbGx5IG9yIG51bWVyaWNhbGx5KSAgICAgICAgICAgICAgICAgfGBzb3J0KGNhcnMkc3BlZWQpYCAgICAgICAgIHwNCnxhZGQgMTAgdG8gZXZlcnkgaXRlbSBpbiBhIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyRzcGVlZCArIDEwYCAgICAgICAgICB8DQp8c2VlIGhvdyBtYW55IHZhbHVlcyB0aGVyZSBhcmUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YGxlbmd0aChjYXJzJHNwZWVkKWAgICAgICAgfA0KfGFkZCB1cCBhbGwgdGhlIHZhbHVlcyAoZm9yIG51bWJlcnMpICAgICAgICAgICAgICAgICAgICAgICAgfGBzdW0oY2FycyRzcGVlZClgICAgICAgICAgIHwNCnxjb3VudCB0aGUgdmFsdWVzIChmb3IgY2hhcmFjdGVycykgICAgICAgICAgICAgICAgICAgICAgICAgIHxgY291bnQoaXJpcyxzcGVjaWVzKWAgICAgICB8DQp8Z2V0IGFuIGlkZWEgb2YgJ3R5cGljYWwnIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YG1lYW4oY2FycyRzcGVlZClgICAgICAgICAgfA0KfGdldCBhbiBpZGVhIG9mICd0eXBpY2FsJyB2YWx1ZXMgd2hlbiB5b3UgaGF2ZSBvdXRsaWVycyAgICAgfGBtZWRpYW4oY2FycyRzcGVlZClgICAgICAgIHwNCnxlc3RpbWF0ZSB0aGUgJ3NwcmVhZCcgKGRldmlhdGlvbikgYXJvdW5kIHRoaXMgdHlwaWNhbCB2YWx1ZXxgc2QoY2FycyRzcGVlZClgICAgICAgICAgICB8DQp8Z2V0IHRoZSBtYXggdmFsdWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YG1heChjYXJzJHNwZWVkKWAgICAgICAgICAgfA0KfGdldCB0aGUgbWluIHZhbHVlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGBtaW4oY2FycyRzcGVlZClgICAgICAgICAgIHwNCnxnZXQgdGhlIHJhbmdlIG9mIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgcmFuZ2UoY2FycyRzcGVlZClgICAgICAgICB8DQp8Z2V0IG9ubHkgdW5pcXVlIHZhbHVlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YHVuaXF1ZShjYXJzJHNwZWVkKWAgICAgICAgfA0KfGdldCB0aGUgaXRoIGl0ZW0gaW4gdGhlIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfGBjYXJzJHNwZWVkW2ldYCAgICAgICAgICAgIHwNCnxyZW1vdmUgdGhlIGl0aCBpdGVtIGZyb20gdGhlIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgIHxgY2FycyRzcGVlZFstaV1gICAgICAgICAgICB8DQp8Z2V0IGFsbCBpdGVtcyBiZXR3ZWVuIGkgYW5kIGogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YGNhcnMkc3BlZWRbaTpqXWAgICAgICAgICAgfA0KfHJlbW92ZSBhbGwgaXRlbXMgYmV0d2VlbiBpIGFuZCBqICAgICAgICAgICAgICAgICAgICAgICAgICAgfGBjYXJzJHNwZWVkWy0oaTpqKV1gICAgICAgIHwNCnxzYW1wbGUgbiByYW5kb20gdmFsdWVzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgc2FtcGxlKGNhcnMkc3BlZWQsbilgICAgICB8DQp8Y2hlY2sgaWYgYSBjaGFyYWN0ZXIgaXMgaW4gYSB2ZWN0b3IgICAgICAgICAgICAgICAgICAgICAgICB8YCJjaHIiICVpbiUgdmVjdG9yYCAgICAgICAgfA0KfGNoZWNrIGlmIGEgY2hhcmFjdGVyIGlzIE5PVCBpbiBhIHZlY3RvciAgICAgICAgICAgICAgICAgICAgfGAhKCJjaHIiICVpbiUgdmVjdG9yKWAgICAgIHwNCnxjaGVjayBpZiBhIG51bWJlciBpcyBpbiBhIHZlY3RvciAgICAgICAgICAgICAgICAgICAgICAgICAgIHxgbnVtICVpbiUgdmVjdG9yYCAgICAgICAgICB8DQoNCiMjIENoYXJhY3RlcnMvU3RyaW5ncw0KDQpVc2UgdGhlIHBhY2thZ2UgYHN0cmluZ3JgIChpdCBjb21lcyB3aXRoIHRoZSBgdGlkeXZlcnNlYCwgc28geW91IHNob3VsZG4ndCBoYXZlIHRvIGxvYWQgaXQgc2VwYXJhdGVseSkNCg0KfHRvLi4uICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8dXNlLi4uICAgICAgICAgICAgICAgICAgICAgICAgIHxlLmcuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnxzdWJzZXQgYSBzdHJpbmcgICAgICAgICAgICAgICAgICAgfGBzdHJfc3ViKHN0cixzdGFydCxzdG9wKWAgICAgICB8YHN0cl9zdWIoIldlZG5lc2RheSIsMSwzKWAgPSAiV2VkIiAgfA0KfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8YWRkIGAtYCB0byBzdGFydCBmcm9tIHRoZSBlbmQgIHxgc3RyX3N1YigiV2VkbmVzZGF5IiwtMywtMSlgID0gImRheSJ8DQp8Y29udmVydCB0byBsb3dlcmNhc2UgICAgICAgICAgICAgIHxgc3RyX3RvX2xvd2VyKClgICAgICAgICAgICAgICAgfGBzdHJfdG9fbG93ZXIoIkJvYiIpYCA9ICJib2IiICAgICAgIHwNCnxjb252ZXJ0IHRvIHVwcGVyY2FzZSAgICAgICAgICAgICAgfGBzdHJfdG9fdXBwZXIoKWAgICAgICAgICAgICAgICB8YHN0cl90b191cHBlcigiYm9iIilgID0gIkJPQiIgICAgICAgfA0KfGNvbnZlcnQgdG8gdGl0bGUgY2FzZSAgICAgICAgICAgICB8YHN0cl90b190aXRsZSgpYCAgICAgICAgICAgICAgIHxgc3RyX3RvX3RpdGxlKCJib2IiKWAgPSAiQm9iIiAgICAgICB8DQp8ZmluZCBhbmQgcmVwbGFjZSAgICAgICAgICAgICAgICAgIHxgc3RyX3JlcGxhY2Uoc3RyLGZpbmQscmVwbGFjZSlgfGBzdHJfcmVwbGFjZSgiZnVuIiwiZiIsInAiKWA9InB1biIgIHwNCnxyZW1vdmUgbGVhZGluZy90cmFpbGluZyB3aGl0ZXNwYWNlfGBzdHJfdHJpbShzdHIpYCAgICAgICAgICAgICAgICB8YHN0cl90cmltKCIgaGVsbG8gIilgPSJoZWxsbyIgICAgICAgfA0KDQpUaGUgYHN0cmluZ19yYCBmdW5jdGlvbnMgYXJlIG1vc3QgcG93ZXJmdWwgd2hlbiB5b3UgY29tYmluZSB0aGVtIHdpdGggcmVndWxhciBleHByZXNzaW9ucy4gQmVsb3cgYXJlIHNvbWUgb2YgdGhlIG1vc3QgdXNlZnVsIG9uZXM6DQoNCnxSZWd1bGFyIGV4cHJlc3Npb258TWVhbmluZyAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8Ii4iICAgICAgICAgICAgICAgfGFueSBjaGFyYWN0ZXIgICAgICAgICAgICAgIHwNCnwiLioiICAgICAgICAgICAgICB8YW55IG51bWJlciBvZiBhbnkgY2hhcmFjdGVyfA0KfCJcXGIiICAgICAgICAgICAgIHxhIHdvcmQgYm91bmRhcnkgICAgICAgICAgICB8DQp8IlxcZCIgICAgICAgICAgICAgfGFueSBudW1iZXIgKGRpZ2l0KSAgICAgICAgIHwNCnwiWzpwdW5jdDpdIiAgICAgICB8cHVuY3R1YXRpb24gbWFya3MgICAgICAgICAgfA0KDQoqKkltcG9ydGFudCoqOiBCZWNhdXNlIGNoYXJhY3RlcnMgbGlrZSAiLiIgaGF2ZSBhIHNwZWNpYWwgbWVhbmluZyBpbiByZWd1bGFyIGV4cHJlc3Npb25zLCBpZiB5b3Ugd2FudCB0byBzZWFyY2ggZm9yIGEgbGl0ZXJhbCBwZXJpb2QgKC4pLCB5b3Ugd2lsbCBuZWVkIHRvIHVzZSAiXFwuIi4gVGhlIHR3byBzbGFzaGVzIGFyZSBjYWxsZWQgZXNjYXBlIGNoYXJhY3RlcnMsIHRoZXkgdGVsbCBSIG5vdCB0byB0cmVhdCB3aGF0ZXZlciBmb2xsb3dzIGFzIGEgcmVndWxhciBleHByZXNzaW9uLg0KDQpZb3UgY2FuIGZpbmQgYWxsIHRoZSByZWd1bGFyIGV4cHJlc3Npb25zIG9uIHRoZSBjaGVhdHNoZWV0IFtoZXJlXShodHRwczovL2V2b2xkeW4uZ2l0bGFiLmlvL2V2b21pY3MtMjAxOC9yZWYtc2hlZXRzL1Jfc3RyaW5ncy5wZGYpLg0KDQojIEZ1bmN0aW9ucyBhbmQgTG9vcHMNCg0KLSBVc2UgdGhlc2Ugd2hlbiB5b3Ugd2FudCB0byBydW4gdGhlIHNhbWUgY29kZSBtYW55IHRpbWVzIG9uIGRpZmZlcmVudCB0aGluZ3MuDQoNCioqU3ludGF4IGZvciBmdW5jdGlvbnMqKjoNCmBgYA0KZnVuY3Rpb25fbmFtZSA8LSBmdW5jdGlvbihpbnB1dHMsaW5wdXRzLGlucHV0cyl7DQojIGRvIHN0dWZmDQouDQouDQouDQpyZXR1cm4ob3V0cHV0KQ0KfQ0KYGBgDQoNCioqU3ludGF4IGZvciBsb29wcyoqOg0KYGBgDQpmb3IoQSBpbiBCKXsNCiAgIyBkbyBzb21ldGhpbmcNCn0NCmBgYA0KDQoqKlVzYWdlIGV4YW1wbGUqKjoNCg0KYGBgDQojIGV4YW1wbGUgb2YgYSBmdW5jdGlvbiB0byBnZXQgdGhlIGh5cG90ZW51c2Ugb2YgYSB0cmlhbmdsZSBmcm9tIHRoZSBzaG9ydCBhbmQgbWlkZGxlIHNpZGUNCg0KZ2V0X2h5cG90ZW51c2UgPC0gZnVuY3Rpb24oc2hvcnRzaWRlLCBtaWRkbGVzaWRlKSB7IA0KICAjIHNob3J0c2lkZSBhbmQgbWlkZGxlc2lkZSBhcmUgdGhlIGlucHV0cyAoYXJndW1lbnRzKQ0KICBoeXBvdGVudXNlX3NxdWFyZWQ8LXNob3J0c2lkZSoqMittaWRkbGVzaWRlKioyDQogIGh5cG90ZW51c2U8LXNxcnQoaHlwb3RlbnVzZV9zcXVhcmVkKQ0KICByZXR1cm4oaHlwb3RlbnVzZSkjIGh5cG90ZW51c2UgaXMgdGhlIG91dHB1dA0KfQ0KDQpzaG9ydHNpZGVzIDwtIGMoMyw0LDUsOCwxLDMpDQptaWRkbGVzaWRlcyA8LSBjKDgsMTAsMTIsMTQsMiwxKQ0KDQojIG5vdyBsZXRzIHJ1biBpdCBtdWx0aXBsZSB0aW1lcyBpbiBhIGxvb3ANCg0KIyBtYWtlIGFuIGVtcHR5IHZlY3RvciB0byBzdG9yZSB0aGUgaHlwb3RlbnVzZXMNCmh5cG90ZW51c2VzIDwtIGMoKQ0KDQojIHdlIHVzZSB0aGVzZSAnaScgdmFsdWVzIGFzIGluZGV4ZXMgdG8gcmVmZXIgdG8gaXRlbXMgaW4gb3VyIHR3byB2ZWN0b3JzIHNob3J0c2lkZXMgYW5kIG1pZGRsZXNpZGVzDQpmb3IoaSBpbiAxOmxlbmd0aChzaG9ydHNpZGVzKSl7DQogICMgcnVuIG91ciBmdW5jdGlvbg0KICBoeXBvdGVudXNlIDwtIGdldF9oeXBvdGVudXNlKHNob3J0c2lkZXNbaV0sbWlkZGxlc2lkZXNbaV0pDQogICMgdXBkYXRlIHRoZSBoeXBvdGVudXNlIHZlY3Rvciwgc28gaXQgY29udGFpbnMgdGhlIGh5cG90ZW51c2VzIHdlJ3ZlIGFscmVhZHkgY2FsY3VsYXRlZCwgcGx1cyB0aGUgb25lIHdlIGp1c3QgY2FsY3VsYXRlZCBpbiB0aGlzIGl0ZXJhdGlvbiBvZiB0aGUgbG9vcA0KICBoeXBvdGVudXNlcyA8LSBjKGh5cG90ZW51c2VzLGh5cG90ZW51c2UpDQp9DQoNCiMgYWZ0ZXIgdGhlIGxvb3AgdGhlIGh5cG90ZW51c2VzIHZlY3RvciB3aWxsIGJlIGZ1bGwgDQpgYGANCg0KIyMgQ2F0ZWdvcmlzaW5nIGZ1bmN0aW9ucw0KDQpBIGZ1bmN0aW9uIGZvciBjYXRlZ29yaXNpbmcgdGhlIGVudmlyb25tZW50LCBiYXNlZCBvbiB0aGUgbGVuZ3RoIG9mIHRoZSBtYXhpbXVtIGdyb3dpbmcgc2Vhc29uIChtZ3MpLg0KDQpgYGANCmVudmlyb25tZW50IDwtIGZ1bmN0aW9uKG1ncykgew0KICBpZihtZ3MgPCA0KSB7DQogICAgcmV0dXJuKCJkcnkiKQ0KICB9IGVsc2UgaWYobWdzIDwgOCkgew0KICAgIHJldHVybigidHlwaWNhbCIpDQogIH0gZWxzZSB7DQogICAgcmV0dXJuKCJmZXJ0aWxlIikNCiAgfQ0KfQ0KYGBgDQoNCiMgQ29uZGl0aW9ucw0KDQp8RXhwcmVzc2lvbiB8TWVhbmluZyAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwtLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfFg9PVkgICAgICAgfFggZXF1YWxzIFkgICAgICAgICAgICAgICAgICAgICB8DQp8WCE9WSAgICAgICB8WCBpcyBub3QgZXF1YWwgdG8gWSAgICAgICAgICAgIHwNCnxYICVpbiUgWSAgIHxYIGlzIGluIFkgICAgICAgICAgICAgICAgICAgICAgfA0KfCEoWCAlaW4lIFkpfFggaXMgbm90IGluIFkgICAgICAgICAgICAgICAgICB8DQp8WDxZICAgICAgICB8WCBpcyBsZXNzIHRoYW4gWSAgICAgICAgICAgICAgIHwNCnxYPlkgICAgICAgIHxYIGlzIGdyZWF0ZXIgdGhhbiBZICAgICAgICAgICAgfA0KfFg+PVkgICAgICAgfFggaXMgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIFl8DQp8WDw9IFkgICAgICB8WCBpcyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gWSAgIHwNCg0KIyMgaWYsIGVsc2UgaWYsIGVsc2Ugc3RhdGVtZW50cw0KDQohW10oaW1hZ2VzL2lmX2lmZWxzZS5QTkcpe3dpZHRoPTcwJX0NCg0KT25seSB0aGUgYmxvY2sgb2YgY29kZSBmb3IgdGhlIEZJUlNUIGNvbmRpdGlvbiB0aGF0IGV2YWx1YXRlcyB0byBUUlVFIHdpbGwgYmUgcnVuOyBzbyB0aGUgb3JkZXIgb2YgeW91ciBjb25kaXRpb25zIG1hdHRlcnMuDQoNCiMgRGF0YSB2aXN1YWxpc2F0aW9uDQoNCldlIHVzZSBgZ2dwbG90KClgLiBUaGVzZSBwbG90cyBoYXZlIGEgYmFzaWMgc3RydWN0dXJlIGxpa2UgdGhpczoNCg0KYGRhdGEgJT4lIGdncGxvdChhZXMoeD1jb2xBLHk9Y29sQixjb2xvdXI9Y29sQykpICtnZW9tX3BvaW50KCkvZ2VvbV9saW5lKCkvZ2VvbV9jb2woKWANCg0KT3B0aW9uYWwgZXh0cmFzOg0KDQoqIGArbGFicyh4PSJYIGF4aXMgdGl0bGUiLHk9IlkgYXhpcyB0aXRsZSIsY29sb3VyPSJMZWdlbmQgdGl0bGUiLHRpdGxlPSJQbG90IHRpdGxlIixjYXB0aW9uPSJDYXB0aW9uIGZvciBwbG90IilgIFtpZiB5b3UgdXNlIGBmaWxsYCBpbnN0ZWFkIG9mIGBjb2xvdXJgLCB0aGVuIHlvdSB3aWxsIHVzZSBgZmlsbD0iTGVnZW5kIHRpdGxlImBdDQoqIGArdGhlbWVfY2xhc3NpYygpYCAgICMgdGhpcyBpcyBhIG5pY2UgdGhlbWUNCiogSWYgeW91IGFyZSBwcmludGluZyBpbiBibGFjayBhbmQgd2hpdGUsIHlvdSBjYW4gdXNlIGBzaGFwZWAgaW5zdGVhZCBvZiBjb2xvdXINCg0KIyMgV2hlbiB0byB1c2Ugd2hpY2ggdHlwZSBvZiBncmFwaD8NCg0KSWYgeW91IGhhdmUgYSBsb3Qgb2YgeCB2YWx1ZXM6DQoNCiogYGdlb21fcG9pbnQoKWAgaXMgZ29vZCB3aGVuIHlvdXIgb2JzZXJ2YXRpb25zIGNvbWUgZnJvbSBkaWZmZXJlbnQgc291cmNlcywgZS5nLiB0aGUgbnVtYmVyIG9mIGNvdmlkIGRlYXRocyBpbiBwZW9wbGUgb2YgZGlmZmVyZW50IGFnZXMsIGFuZCB5b3Ugd2FudCB0byBjb21tdW5pY2F0ZSB0aGUgKnJlbGF0aW9uc2hpcCBiZXR3ZWVuIHlvdXIgeCBhbmQgeSB2YWx1ZXMqDQoqIGBnZW9tX2xpbmUoKWAgYW5kIGBnZW9tX3Ntb290aCgpYCBhcmUgYmV0dGVyIGZvciBwbG90dGluZyBtdWx0aXBsZSBvYnNlcnZhdGlvbnMgZnJvbSBhIHNpbmdsZSBzb3VyY2UsIGUuZy4gdGhlIG51bWJlciBvZiBjb3ZpZCBjYXNlcyBpbiBhIGNvdW50cnkgb3ZlciBtdWx0aXBsZSB3ZWVrcywgd2hlcmUgeW91IHdhbnQgdG8gY29tbXVuaWNhdGUgdGhlICpjaGFuZ2UgaW4geW91ciB5IHZhbHVlIG92ZXIgdGltZSoNCg0KSWYgeW91IGhhdmUgb25seSBhIGZldyB4IHZhbHVlczoNCg0KKiBgZ2VvbV9jb2woKWAgaXMgZ29vZCBmb3Igd2hlbiB5b3UgaGF2ZSBvbmx5IG9uZSB5IG9ic2VydmF0aW9uIHBlciB4IHZhbHVlDQoqIGBnZW9tX3Zpb2xpbigpYCBpcyBnb29kIHdoZW4geW91ciB4IHZhbHVlcyBhcmUgbGFyZ2VyIGNhdGVnb3JpZXMgd2l0aCBtdWx0aXBsZSB5IG9ic2VydmF0aW9ucyBwZXIgeCB2YWx1ZSwgYW5kIHlvdSB3YW50IHRvICpjb21wYXJlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHkgdmFsdWVzIGJldHdlZW4gdGhlIGRpZmZlcmVudCBncm91cHMqDQoNCmBnZW9tX2hpc3RvZ3JhbSgpYCBpcyB1c2VkIHRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHNpbmdsZSB2YXJpYWJsZS4gSXQncyBtb3JlIHVzZWQgaW4gdGhlIG1ldGhvZHMgc2VjdGlvbiBvZiBwYXBlcnMgcmF0aGVyIHRoYW4gdGhlIHJlc3VsdHMsIGZvciBleGFtcGxlIHRvIHNob3cgd2hhdCB5b3VyIHNhbXBsZSBvZiBkYXRhIGxvb2tzIGxpa2UuDQoNCiMjIGdlb21faGlzdG9ncmFtKCkNCg0KQmVsb3cgaXMgYSBoaXN0b2dyYW0gc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGRpYW1vbmRzIGluIHRoZSBkaWFtb25kIGRhdGFzZXQgYnkgdGhlaXIgY2FyYXQuDQoNCmBgYHtyfQ0KZGlhbW9uZHMlPiUNCiAgICAgICAjIGp1c3QgcHV0IHRoZSB2YXJpYWJsZSB5b3Ugd2FudCB0byBleGFtaW5lIHRoZSBkaXN0cmlidXRpb24gb2YNCiAgZ2dwbG90KGFlcyhjYXJhdCkpKw0KICBnZW9tX2hpc3RvZ3JhbSgpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIGxhYnModGl0bGU9IkRpc3RyaWJ1dGlvbiBvZiBkaWFtb25kcyBieSBjYXJhdCIpDQpgYGANCg0KTW9zdCBvZiB0aGUgZGlhbW9uZHMgaW4gdGhlIGRhdGFzZXQgYXJlIGxlc3MgdGhhbiAxIGNhcmF0Lg0KDQojIyBnZW9tX3BvaW50KCkNCg0KYGBge3IsaW5jbHVkZT1GQUxTRX0NCm5ldHRsZSA8LSByZWFkX2NzdigiZGF0YS9uZXR0bGVfMTk5OV9jbGltYXRlLmNzdiIpDQpuZXR0bGUgJT4lIA0KICBtdXRhdGUoTUdTX2NhdGVnb3J5ID0gaWZfZWxzZShNR1MgPCA2LCAiZHJ5IiwgImZlcnRpbGUiKSktPk1HU19uZXR0bGUNCmBgYA0KDQpXaXRoIGNvbG91cnM6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCk1HU19uZXR0bGUlPiVnZ3Bsb3QoYWVzKHg9UG9wdWxhdGlvbix5PUxhbmdzLGNvbG91cj1NR1NfY2F0ZWdvcnkpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBsYWJzKHg9J1BvcHVsYXRpb24gKGluIG1pbGxpb25zKScseT0nTnVtYmVyIG9mIGxhbmd1YWdlcycsY29sb3VyPSJFbnZpcm9ubWVudCIpKw0KICB0aGVtZV9jbGFzc2ljKCkgICAjIGEgbmljZXIgcHJlc2VudGF0aW9uIHRoYW4gdGhlIGRlZmF1bHQgZ2dwbG90IHRoZW1lDQpgYGANCg0KV2l0aCBzaGFwZXM6DQoNCmBgYHtyLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCk1HU19uZXR0bGUlPiVnZ3Bsb3QoYWVzKHg9UG9wdWxhdGlvbix5PUxhbmdzLHNoYXBlPU1HU19jYXRlZ29yeSkpKw0KICBnZW9tX3BvaW50KCkrDQogIGxhYnMoeD0nUG9wdWxhdGlvbiAoaW4gbWlsbGlvbnMpJyx5PSdOdW1iZXIgb2YgbGFuZ3VhZ2VzJyxzaGFwZT0iRW52aXJvbm1lbnQiKSsNCiAgdGhlbWVfY2xhc3NpYygpICAgIyBhIG5pY2VyIHByZXNlbnRhdGlvbiB0aGFuIHRoZSBkZWZhdWx0IGdncGxvdCB0aGVtZQ0KYGBgDQoNCiMjIEFkZGluZyBsYWJlbHMgdG8gcG9pbnRzDQoNCmBnZW9tX3RleHQoKWAgYW5kIGBnZW9tX3RleHRfcmVwZWwoKWAgY2FuIGJlIHVzZWQgdG8gYWRkIGxhYmVscyB0byBwb2ludHMgb24geW91ciBwbG90LiBZb3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIGxpYnJhcnkgYGdncmVwZWxgIHRvIHVzZSBgZ2VvbV90ZXh0X3JlcGVsYCwgd2hpY2ggcG9zaXRpb25zIHRoZSBsYWJlbHMgbmljZWx5IHNvIHRoZXkgZG9uJ3Qgb3ZlcmxhcCB3aXRoIGFueXRoaW5nLCBhbmQgeW91J2xsIG5lZWQgdG8gYWRkIGFuIGFyZ3VtZW50IGBsYWJlbGAgdG8geW91ciBgYWVzKClgIGZ1bmN0aW9uIGluIGBnZ3Bsb3QoKWAsIHRvIHNwZWNpZnkgd2hpY2ggY29sdW1uIHRvIGdldCB0aGUgbGFiZWxzIGZyb20uDQoNCmBgYHtyfQ0KbGlicmFyeShnZ3JlcGVsKQ0KTUdTX25ldHRsZSU+JQ0KICBzYW1wbGVfbigxNSklPiUNCiAgZ2dwbG90KGFlcyh4PVBvcHVsYXRpb24seT1sb2cxMChMYW5ncyksbGFiZWw9Q291bnRyeSkpICsgZ2VvbV9wb2ludChhZXMoY29sb3VyPU1HU19jYXRlZ29yeSkpICsgZ2VvbV90ZXh0X3JlcGVsKCkrdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KDQoNCiMjIGdlb21fbGluZSgpIGFuZCBnZW9tX3Ntb290aCgpDQoNClVzZSBgZ2VvbV9saW5lKClgIGZvciBhIGphZ2dlZCBsaW5lOg0KDQpgYGB7cixpbmNsdWRlPUZBTFNFfQ0KYmFybm5hbW4gJT4lIA0KICBtdXRhdGUoRmlyc3RMZXR0ZXI9c3RyX3N1YihuYW1lLDEsMSkpICU+JSANCiAgZmlsdGVyKEZpcnN0TGV0dGVyID09ICJYIiB8IEZpcnN0TGV0dGVyID09ICJZIiB8IEZpcnN0TGV0dGVyID09ICJaIikgJT4lIA0KICBncm91cF9ieSh5ZWFyKSAgJT4lIA0KICBzdW1tYXJpc2UoVG90YWxOPXN1bShuKSktPndlaXJkX25hbWVzDQpgYGANCg0KDQpgYGB7cn0NCndlaXJkX25hbWVzJT4lDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PVRvdGFsTikpICsgDQogIGdlb21fbGluZSgpKw0KbGFicyh4PSdZZWFyJyx5PSdUb3RhbCBudW1iZXIgb2Ygd2VpcmQgbmFtZXMnLHRpdGxlPSdQb3B1bGFyaXR5IG9mIHdlaXJkIG5hbWVzIG92ZXIgdGltZScpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpVc2UgYGdlb21fc21vb3RoKClgIHRvIGdldCBhIHNtb290aGVkIGxpbmU6DQoNCmBgYHtyLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCndlaXJkX25hbWVzJT4lDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PVRvdGFsTikpICsgDQogIGdlb21fc21vb3RoKCkrDQpsYWJzKHg9J1llYXInLHk9J1RvdGFsIG51bWJlciBvZiB3ZWlyZCBuYW1lcycsdGl0bGU9J1BvcHVsYXJpdHkgb2Ygd2VpcmQgbmFtZXMgb3ZlciB0aW1lJykrDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCiMjIGdlb21fY29sKCkgLSBiYXIgcGxvdHMNCg0KYGBge3J9DQpiYXJubmFtbiU+JQ0KICBmaWx0ZXIobmFtZT09J0xlZScpJT4lDQogIGdncGxvdChhZXMoeD15ZWFyLHk9bikpK2dlb21fY29sKCkrDQogIGxhYnModGl0bGU9IkJhYmllcyBuYW1lZCBMZWUiLHg9IlllYXIiLHk9Ik51bWJlciBvZiBiYWJpZXMiKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDIwMTA6MjAxNykpKyAgIyB0byBzcGVjaWZ5IHRoZSBicmVha3Mgb24gdGhlIHgtYXhpcw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyBSZW9yZGVyaW5nIGl0ZW1zIG9uIHRoZSB4LWF4aXMNCg0KKiBhZGQgYSBgcmVvcmRlcigpYCBmdW5jdGlvbiB0byB0aGUgYGFlcygpYCBmb3IgdGhlIHgtYXhpcw0KKiB3b3JrcyB0aGUgc2FtZSB3YXkgYXMgYGFycmFuZ2UoKWANCg0KYGBge3J9DQpNR1NfbmV0dGxlJT4lDQogIHNhbXBsZV9uKDYpJT4lDQogIGdncGxvdChhZXMoeD1yZW9yZGVyKENvdW50cnksTGFuZ3MpLHk9TGFuZ3MsZmlsbD1NR1NfY2F0ZWdvcnkpKSsNCiAgZ2VvbV9jb2woKSsNCiAgbGFicyh4PSdDb3VudHJ5Jyx5PSdOdW1iZXIgb2YgbGFuZ3VhZ2VzJykrDQogIHNjYWxlX2ZpbGxfZGlzY3JldGUoIkVudmlyb25tZW50IixjKCJkcnkiLCJmZXJ0aWxlIikpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyBHcm91cGVkIGJhciBwbG90cw0KDQoqIFVzZSBgZmlsbD1WYXJYYCBpbiB0aGUgYGFlcygpYCBvZiB5b3VyIGdncGxvdCBjYWxsIHRvIGNvbG91ciB0aGUgYmFycyBieSBhIHRoaXJkIHZhcmlhYmxlLg0KKiBVc2UgYHBvc2l0aW9uPSJkb2RnZSgpImAgaW4geW91ciBgZ2VvbV9jb2woKWAgdG8gaGF2ZSB0aGUgYmFycyBzaWRlLWJ5LXNpZGUNCiogVXNlIGBzY2FsZV94X2NvbnRpbnVvdXNgIChmb3IgY29udGludW91cyB2YXJpYWJsZXMpIHRvIHNwZWNpZnkgdGhlIGJyZWFrcyBvbiB0aGUgeC1heGlzOyBpZiB5b3UgaGF2ZSBkaXNjcmV0ZSAoZS5nLiBjYXRlZ29yaWNhbCkgdmFyaWFibGVzLCB0aGUgZnVuY3Rpb24gaXMgYHNjYWxlX3hfZGlzY3JldGVgDQoNCmBgYHtyfQ0KYmFybm5hbW4lPiUNCiAgZmlsdGVyKG5hbWU9PSdMZWUnKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9eWVhcix5PW4sZmlsbD1zZXgpKSsNCiAgZ2VvbV9jb2wocG9zaXRpb249ImRvZGdlIikrICAjIGJhcnMgc2lkZS1ieS1zaWRlDQogIGxhYnModGl0bGU9IkJhYmllcyBuYW1lZCBMZWUiLHg9IlllYXIiLHk9Ik51bWJlciBvZiBiYWJpZXMiLGZpbGw9IlNleCIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMjAxMDoyMDE3KSkrICAjIHRvIHNwZWNpZnkgdGhlIGJyZWFrcyBvbiB0aGUgeC1heGlzDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCklmIHlvdSBkb24ndCB1c2UgYHBvc2l0aW9uPSJkb2RnZSJgLCB0aGUgZGlmZmVyZW50IGNvbG91cmVkIGJhcnMgd2lsbCBiZSBzdGFja2VkIG9uIHRvcCBvZiBlYWNob3RoZXIuDQoNCmBgYHtyfQ0KYmFybm5hbW4lPiUNCiAgZmlsdGVyKG5hbWU9PSdMZWUnKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9eWVhcix5PW4sZmlsbD1zZXgpKSsNCiAgZ2VvbV9jb2woKSsgIA0KICBsYWJzKHRpdGxlPSJCYWJpZXMgbmFtZWQgTGVlIix4PSJZZWFyIix5PSJOdW1iZXIgb2YgYmFiaWVzIixmaWxsPSJTZXgiKSsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDIwMTA6MjAxNykpKyAgIyB0byBzcGVjaWZ5IHRoZSBicmVha3Mgb24gdGhlIHgtYXhpcw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQojIyBQZXJjZW50IHN0YWNrZWQgYmFyIHBsb3RzDQoNClVzZSBgcG9zaXRpb249ImZpbGwiYCB0byBzaG93IHByb3BvcnRpb25zIGluc3RlYWQgb2YgY291bnRzOg0KDQpgYGB7cn0NCmJhcm5uYW1uJT4lDQogIGZpbHRlcihuYW1lPT0nTGVlJyklPiUNCiAgZ2dwbG90KGFlcyh4PXllYXIseT1uLGZpbGw9c2V4KSkrDQogIGdlb21fY29sKHBvc2l0aW9uPSJmaWxsIikrICAjIHRvIHNob3cgcHJvcG9ydGlvbnMgDQogIGxhYnModGl0bGU9IkJhYmllcyBuYW1lZCBMZWUiLHg9IlllYXIiLHk9IlByb3BvcnRpb24iLGZpbGw9IlNleCIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMjAxMDoyMDE3KSkrICAjIHRvIHNwZWNpZnkgdGhlIGJyZWFrcyBvbiB0aGUgeC1heGlzDQogIHRoZW1lX2NsYXNzaWMoKQ0KYGBgDQoNCiMjIGdlb21fdmlvbGluKCkNCg0KVGhpcyBpcyBnb29kIHRvIGNvbXBhcmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSBiZXR3ZWVuIGdyb3Vwcy4NCg0KYGBge3J9DQojIG1wZyBpcyBhIGRhdGFzZXQgb2YgY2Fycw0KbXBnICU+JQ0KICAjIHRoZSBod3kgY29sdW1uIHRlbGxzIHlvdSBob3cgbWFueSBtaWxlcyBhIGNhciBjYW4gZ28gb24gMSBnYWxsb24gb2YgcGV0cm9sDQogICMgY2xhc3MgaXMgdGhlIHR5cGUgb2YgY2FyDQogIGdncGxvdCggYWVzKHg9cmVvcmRlcihjbGFzcyxod3kpLCB5PWh3eSkpICsgDQogIGdlb21fdmlvbGluKCkgKw0KICB0aGVtZV9jbGFzc2ljKCkrDQojIHdlIGNhbiB1c2UgdGhlIHN0YXRfc3VtbWFyeSBwb2ludCB0byBhZGQgcG9pbnRzIGZvciBzdW1tYXJ5IHN0YXRpc3RpY3MsIGhlcmUgd2Ugc2hvdyB0aGUgbWVhbg0KICBzdGF0X3N1bW1hcnkoZnVuPW1lYW4sIGdlb209InBvaW50IikrDQogIGxhYnMoeD0iQ2FyIHR5cGUiLHk9Im1pbGVzIHBlciBnYWxsb24iLHRpdGxlPSJFZmZpY2llbmN5IG9mIGNhcnMgb24gdGhlIGhpZ2h3YXkiLGNhcHRpb24gPSAiVGhlIG1lYW4gbWlsZXMgcGVyIGdhbGxvbiBmb3IgZWFjaCBjYXIgaXMgaW5kaWNhdGVkIHdpdGggYSBwb2ludCIpICANCg0KYGBgDQoNCg0KDQojIyBNYWtpbmcgZGF0YSBwb2ludHMgbGVzcyBjbHVtcGVkIG9yIGxlc3Mgc3ByZWFkIGFwYXJ0DQoNCiogTG9nIHRyYW5zZm9ybWF0aW9ucyAoYXMgaW4gdGhlIGdyYXBoIGFib3ZlKSBhcmUgdXNlZCB0byBtYWtlIGRhdGEgdGhhdCBpcyB2ZXJ5IHNwcmVhZCBhcGFydC9jbHVtcGVkIHRvZ2V0aGVyIGVhc2llciB0byB2aXN1YWxpc2UuDQoqIFRoZSBtb3N0IGNvbW1vbiBiYXNlIGlzIDEwLiBXaGVuIHdlIHRha2UgdGhlIGxvZzEwIG9mIGEgbnVtYmVyLCAkeCQsIHdlIGFyZSB0cnlpbmcgdG8gZmluZCBhIG51bWJlciAkeSQgc3VjaCB0aGF0ICQxMF55PXgkLiBUaGUgZnVuY3Rpb24gZm9yIHRoaXMgaW4gUiBpcyBqdXN0IGBsb2cxMCh4KWAuIEZvciB2ZXJ5IGZhciBhcGFydCB2YWx1ZXMgb2YgJHgkLCB0YWtpbmcgJGxvZzEwKHgpJCB3aWxsIGdpdmUgeW91IHZhbHVlcyB0aGF0IGFyZSBjbG9zZXIgdG9nZXRoZXIsIHdoaWxlIGZvciB2ZXJ5IGNsb3NlIHRvZ2V0aGVyIHZhbHVlcyBvZiAkeCQsICRsb2cxMCh4KSQgd2lsbCBnaXZlIHlvdSB2YWx1ZXMgdGhhdCBhcmUgZnVydGhlciBhcGFydC4gVGhpcyBtZWFucyB0aGF0ICRsb2cxMCh4KSQgaXMgb2Z0ZW4gbmljZXIgdG8gcGxvdCB0aGFuICR4JC4NCiogSXQgaXMgcGVyZmVjdGx5IGFjY2VwdGFibGUgdG8gZG8gdGhlc2Uga2luZHMgb2YgdHJhbnNmb3JtYXRpb25zIHRvIHlvdXIgZGF0YSwgaW4gb3JkZXIgdG8gbWFrZSBpdCBlYXNpZXIgdG8gdmlzdWFsaXNlLg0KDQojIyBNYWtpbmcgbXVsdGlwbGUgcGxvdHMgDQoNCmBmYWNldF93cmFwKClgIGFuZCBgZmFjZXRfZ3JpZCgpYCBsZXQgeW91IHByb2R1Y2UgbXVsdGlwbGUgZGlmZmVyZW50IHBsb3RzIGZvciBlYWNoIHZhbHVlIGluIGEgY29sdW1uL2NvbHVtbnMNCiANCiogYGZhY2V0X3dyYXAofkNvbEEpYCAtLT4gd2lsbCBtYWtlIGRpZmZlcmVudCB2ZXJzaW9ucyBvZiB0aGUgc2FtZSBncmFwaCBmb3IgZXZlcnkgZGlmZmVyZW50IHZhbHVlIGluIGNvbEEsIGFsbCB3cmFwcGVkIGFyb3VuZCBlYWNoIG90aGVyICh3b3JrcyB3ZWxsIHdoZW4geW91IGhhdmUgYSBsb3Qgb2YgZGlmZmVyZW50IHZhbHVlcyBvZiBhIHZhcmlhYmxlKQ0KKiBgZmFjZXRfZ3JpZChjb2xBfmNvbEIpYCAtLT4gbWFrZXMgYSBncmlkIG9mIGdyYXBocywgd2hlcmUgdGhlIGRpZmZlcmVudCB2YWx1ZXMgb2YgY29sQSBhcmUgdGhlIHJvd3MgaW4gdGhlIGdyaWQsIGFuZCB0aGUgZGlmZmVyZW50IHZhbHVlcyBvZiBjb2xCIGFyZSB0aGUgY29sdW1ucyBpbiB0aGUgZ3JpZC4gVGhpcyB3b3JrcyBiZXN0IHdoZW4gY29sQSBhbmQgY29sQiBkb24ndCBoYXZlIHRvbyBtYW55IGRpZmZlcmVudCB2YWx1ZXMuDQoqIGArdGhlbWVfbWluaW1hbCgpYCBpcyBhIG5pY2UgdGhlbWUgdG8gdXNlIHdpdGggZmFjZXRlZCBwbG90cyAoY29tcGFyZWQgdG8gb3VyIHVzdWFsIGArdGhlbWVfY2xhc3NpYygpYCkNCg0KYGBge3J9DQpiYXJubmFtbiAlPiUgZmlsdGVyKG5hbWU9PSJMZWUiKSAlPiUgZ2dwbG90KGFlcyh4PXllYXIsIHk9bikpICsgZ2VvbV9saW5lKCkgKyBmYWNldF93cmFwKCJzZXgiKSt0aGVtZV9taW5pbWFsKCkNCmBgYA0KDQpgYGB7cixtZXNzYWdlPUZBTFNFfQ0KYmFybm5hbW4gJT4lIA0KICBtdXRhdGUoRmluYWxMZXR0ZXIgPSBzdHJfc3ViKG5hbWUsIC0xLCAtMSkpICAlPiUgIA0KICBtdXRhdGUoRmluYWwgPSBpZl9lbHNlKEZpbmFsTGV0dGVyICVpbiUgYygiYSIsJ8OkJywnw7YnLCfDpScsICJlIiwgImkiLCAibyIsICJ1IiwgInkiKSwgInZvd2VsIiwgImNvbnNvbmFudCIpKSAlPiUgDQogIGdyb3VwX2J5KEZpbmFsLHllYXIsc2V4KSAlPiUgDQogIHN1bW1hcmlzZSh0b3RhbD1zdW0obikpICAlPiUgDQogIGdncGxvdChhZXMoeD15ZWFyLCB5PXRvdGFsKSkgKyBnZW9tX2xpbmUoKSArIGZhY2V0X2dyaWQoc2V4IH4gRmluYWwpK3RoZW1lX21pbmltYWwoKQ0KYGBgDQoNCiMjIENoYW5naW5nIHRoZSBudW1iZXIgYW5kIGxvb2sgb2YgdGlja3Mgb24gdGhlIHggYXhpcw0KDQpZb3UgY2FuIGFkZCBtb3JlIHRpY2tzIHRvIGEgY29udGludW91cyB4LWF4aXMgdXNpbmcgdGhlIGZ1bmN0aW9uIGArc2NhbGVfeF9jb250aW51b3VzKG4uYnJlYWtzPW51bV90aWNrcylgLiBUaGUgbnVtYmVyIG9mIHRpY2tzIHRoYXQgeW91IGVudGVyIHdvbid0IGFsd2F5cyBiZSB0aGUgZXhhY3QgbnVtYmVyIG9mIGxhYmVscyBpdCBtYWtlcywgYnV0IGl0IHdpbGwgdHJ5IHRvIGdldCBpdCBhcyBjbG9zZSBhcyBwb3NzaWJsZSB3aGlsZSBzdGlsbCBlbnN1cmluZyBuaWNlIGJyZWFrIGxhYmVscy4NCg0KV2UgY2FuIGFsc28gbWFrZSB0aGUgYnJlYWsgbGFiZWxzIG5pY2VyIGJ5IGNoYW5naW5nIHRoZWlyIGFuZ2xlIGFuZCBwb3NpdGlvbi4gVGhpcyBpcyBkb25lIHdpdGggdGhlIGZ1bmN0aW9uIGArdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsdmp1c3Q9MC41KSlgLCBhbmQgYWdhaW4sIGp1c3QgcGxheSBhcm91bmQgd2l0aCB0aGUgdmFsdWVzIGZvciBgYW5nbGVgIGFuZCBgdmp1c3RgICh2ZXJ0aWNhbCBhZGp1c3RtZW50KSB1bnRpbCB5b3UgZ2V0IHNvbWV0aGluZyB0aGF0IGxvb2tzIHJpZ2h0LiBJZiBgdmp1c3RgIGRvZXNuJ3Qgd29yayBmb3IgeW91LCB0aGVyZSBpcyBhbHNvIGFuIGFyZ3VtZW50IGBoanVzdGAgKGhvcml6b250YWwgYWRqdXN0bWVudCkgdGhhdCB5b3UgY2FuIGFkZC4NCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCmJhcm5uYW1uICU+JSANCiAgbXV0YXRlKEZpbmFsTGV0dGVyID0gc3RyX3N1YihuYW1lLCAtMSwgLTEpKSAgJT4lICANCiAgbXV0YXRlKEZpbmFsID0gaWZfZWxzZShGaW5hbExldHRlciAlaW4lIGMoImEiLCfDpCcsJ8O2Jywnw6UnLCAiZSIsICJpIiwgIm8iLCAidSIsICJ5IiksICJ2b3dlbCIsICJjb25zb25hbnQiKSkgJT4lIA0KICBncm91cF9ieShGaW5hbCx5ZWFyLHNleCkgJT4lIA0KICBzdW1tYXJpc2UodG90YWw9c3VtKG4pKSAgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9eWVhciwgeT10b3RhbCkpICsgZ2VvbV9saW5lKCkgKyBmYWNldF9ncmlkKHNleCB+IEZpbmFsKSt0aGVtZV9taW5pbWFsKCkrc2NhbGVfeF9jb250aW51b3VzKG4uYnJlYWtzPTE2KSt0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSx2anVzdD0wLjUpKQ0KYGBgDQoNCiMjIE1hcHBpbmcNCg0KV2UgY2FuIGdldCBtYXBzIGZyb20gdGhlIHBhY2thZ2VzIGBybmF0dXJhbGVhcnRoYCBhbmQgYHJuYXR1cmFsZWFydGhkYXRhYCwgYW5kIHBsb3QgdGhlbSB1c2luZyB0aGUgYGdncGxvdGAgZnVuY3Rpb24gYGdlb21fc2YoKWAuIGBzZmAgaXMgYSBmaWxlIGZvcm1hdCBmb3Igc3RvcmluZyBtYXBzLiANCg0KYGBge3IgbWFwcGluZ30NCiMgcmVxdWlyZWQgcGFja2FnZXMNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aCkNCmxpYnJhcnkocm5hdHVyYWxlYXJ0aGRhdGEpDQpsaWJyYXJ5KGdncmVwZWwpDQoNCiMgdGhlc2UgcGFja2FnZXMgbWF5IG5vdCBmdW5jdGlvbiBwcm9wZXJseSBpZiB5b3UgZG9uJ3QgYWxzbyBoYXZlIHJnZW9zIGluc3RhbGxlZDsgaWYgeW91IGdldCBhbiBlcnJvciwgcnVuIGluc3RhbGwucGFja2FnZXMoJ3JnZW9zJykgaW4geW91ciBjb25zb2xlIGFuZCB0aGVuIHRyeSBhZ2Fpbg0KDQojIHRoZSBmdW5jdGlvbiBuZV9jb3VudHJpZXMgd2lsbCBnaXZlIHlvdSBjb3VudHJ5IG1hcHMsIGlmIHlvdSBkb24ndCBzcGVjaWZ5IHdoaWNoIGNvdW50cmllcywgaXQgZ2l2ZXMgeW91IHRoZSB3aG9sZSB3b3JsZC4gDQojIFVzZSByZXR1cm5jbGFzcz0ic2YiIHRvIG1ha2Ugc3VyZSB5b3UgZ2V0IGFuIHNmIGZvcm1hdCBiYWNrLCBhcyB0aGlzIGlzIHdoYXQgZ2VvbV9zZigpIHdvcmtzIHdpdGgNCndvcmxkIDwtIG5lX2NvdW50cmllcyhyZXR1cm5jbGFzcyA9ICJzZiIpDQoNCiMgeW91IG5lZWQgdG8gaGF2ZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGluZm9ybWF0aW9uIGZvciB0aGUgdGhpbmdzIHlvdSB3YW50IHRvIHBsb3QgLS0gSSBnZXQgdGhpcyBmcm9tIGdvb2dsZSBtYXBzIC0tIGp1c3QgcmlnaHQgY2xpY2sgb24gYSBwbGFjZSBpbiBnb29nbGUgbWFwcyAob24gYSBjb21wdXRlciksIGFuZCB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB3aWxsIHNob3cgdXAgYW5kIHlvdSBjYW4gY29weSB0aGVtDQp0eXBlIDwtIGMoInJlbnRhbCIsInBhcmVudCdzIGhvdXNlIiwiaW4tbGF3J3MgaG91c2UiLCJyZW50YWwiLCJyZW50YWwiKQ0KbGF0aXR1ZGUgPC0gYyg1OS44NjUsLTMzLjg3OSw1MC44NzQsMzUuNjY0LC0zNS4yODQpDQpsb25naXR1ZGUgPC0gYygxNy42NDEsMTUxLjExMiw2LjAzNywxMzkuNDgyLDE0OS4xMzYpDQpwbGFjZXMgPC0gZGF0YS5mcmFtZSh0eXBlLGxhdGl0dWRlLGxvbmdpdHVkZSkNCg0KIyBzdGFydCBieSBwbG90dGluZyB0aGUgbWFwIGFzIGEgYmFzZSBsYXllcg0KIyB0aGlzIGFsd2F5cyBoYXMgdG8gY29tZSBmaXJzdCAtLSBpdCB3b24ndCB3b3JrIGlmIHlvdSBzdGFydCB3aXRoIHRoZSBwb2ludHMgYW5kIHRyeSB0byBhZGQgdGhlIG1hcCBhZnRlcg0Kd29ybGQlPiUNCiAgZ2dwbG90KCkrDQogIGdlb21fc2YoKSsNCiAgIyB0aGVuIHlvdSBjYW4gYWRkIHBvaW50cyBvbiB0b3Agb2YgaXQgLS0gbm90ZSB0aGF0IHlvdSBuZWVkIHRvIHNwZWNpZnkgdGhlIGRhdGEsIGJlY2F1c2UgdGhpcyBjb21lcyBmcm9tIGEgZGlmZmVyZW50IGRhdGFzZXQgdG8gdGhlIG9uZSBhdCB0aGUgdG9wIG9mIHRoZSBwaXBlDQogIGdlb21fcG9pbnQoZGF0YSA9IHBsYWNlcyxhZXMoeD1sb25naXR1ZGUseT1sYXRpdHVkZSxjb2xvdXI9dHlwZSkpKw0KICB0aGVtZV92b2lkKCkrICAgICMgdGhpcyBpcyBhIG5pY2UgdGhlbWUgZm9yIG1hcHM7IGl0IGdldHMgcmlkIG9mIHRoZSBheGVzDQogIGxhYnModGl0bGU9IlBsYWNlcyBJJ3ZlIExpdmVkIikNCg0KYGBgDQoNCllvdSBjYW4gdXNlIHRoZSAnY291bnRyeScgYXJndW1lbnQgaW4gYG5lX2NvdW50cmllcygpYCB0byBnZXQgYSBtYXAgb2YgYSBzaW5nbGUgY291bnRyeS4NCg0KYGBge3J9DQoNCkF1c3RyYWxpYSA8LSBuZV9jb3VudHJpZXMoY291bnRyeT0iQXVzdHJhbGlhIixyZXR1cm5jbGFzcyA9ICJzZiIpDQoNCmF1c3RyYWxpYW5fcGxhY2VzIDwtIHBsYWNlcyU+JWZpbHRlcihsYXRpdHVkZTwwKQ0KDQpBdXN0cmFsaWElPiUNCiAgZ2dwbG90KCkrDQogIGdlb21fc2YoKSsNCiAgIyBzaW5jZSB0aGUgZGF0YSBpc24ndCBjb21pbmcgZnJvbSB0aGUgdG9wIG9mIHRoZSBwaXBlLCB5b3UgbmVlZCB0byB0ZWxsIGl0IHRoZSBkYXRhIGFuZCBhZXN0aGV0aWNzDQogIGdlb21fcG9pbnQoZGF0YSA9IGF1c3RyYWxpYW5fcGxhY2VzLGFlcyh4PWxvbmdpdHVkZSx5PWxhdGl0dWRlKSkrDQogICMgeW91IGNvdWxkIHVzZSBsYWJlbHMgaW5zdGVhZCBvZiBjb2xvdXIgLS0gYnV0IGFnYWluIHlvdSBuZWVkIHRvIHRlbGwgaXQgdGhlIGFlc3RoZXNpY3MgYW5kIGRhdGEgdG8gdXNlDQogIGdlb21fdGV4dF9yZXBlbChkYXRhID0gYXVzdHJhbGlhbl9wbGFjZXMsYWVzKHg9bG9uZ2l0dWRlLHk9bGF0aXR1ZGUsbGFiZWw9dHlwZSkpKw0KICBsYWJzKHRpdGxlPSJQbGFjZXMgSSd2ZSBMaXZlZCIpKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQpZb3UgY2FuIGFkZCBtdWx0aXBsZSBjb3VudHJpZXMgaWYgeW91IGxpa2U6DQoNCmBgYHtyfQ0KbWFwIDwtIG5lX2NvdW50cmllcyhjb3VudHJ5PWMoIkF1c3RyYWxpYSIsIk5ldyBaZWFsYW5kIikscmV0dXJuY2xhc3MgPSAic2YiKQ0KDQphdXN0cmFsaWFuX3BsYWNlcyA8LSBwbGFjZXMlPiVmaWx0ZXIobGF0aXR1ZGU8MCkNCg0KbWFwJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX3NmKCkrDQogICMgc2luY2UgdGhlIGRhdGEgaXNuJ3QgY29taW5nIGZyb20gdGhlIHRvcCBvZiB0aGUgcGlwZSwgeW91IG5lZWQgdG8gdGVsbCBpdCB0aGUgZGF0YSBhbmQgYWVzdGhldGljcw0KICBnZW9tX3BvaW50KGRhdGEgPSBhdXN0cmFsaWFuX3BsYWNlcyxhZXMoeD1sb25naXR1ZGUseT1sYXRpdHVkZSkpKw0KICAjIHlvdSBjb3VsZCB1c2UgbGFiZWxzIGluc3RlYWQgb2YgY29sb3VyIC0tIGJ1dCBhZ2FpbiB5b3UgbmVlZCB0byB0ZWxsIGl0IHRoZSBhZXN0aGVzaWNzIGFuZCBkYXRhIHRvIHVzZQ0KICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGF1c3RyYWxpYW5fcGxhY2VzLGFlcyh4PWxvbmdpdHVkZSx5PWxhdGl0dWRlLGxhYmVsPXR5cGUpKSsNCiAgbGFicyh0aXRsZT0iUGxhY2VzIEkndmUgTGl2ZWQiKSsNCiAgdGhlbWVfdm9pZCgpDQoNCmBgYA0KDQpPciB5b3UgY2FuIHVzZSB0aGUgYXJndW1lbnQgYGNvbnRpbmVudGAgdG8gc3BlY2lmeSBhbiBlbnRpcmUgY29udGluZW50Og0KDQpgYGB7cn0NCkFzaWEgPC0gbmVfY291bnRyaWVzKGNvbnRpbmVudD0iQXNpYSIscmV0dXJuY2xhc3MgPSAic2YiKQ0KQXNpYSU+JQ0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZigpKw0KICB0aGVtZV92b2lkKCkNCmBgYA0KDQpBIGZ1bGwgbGlzdCBvZiBhdmFpbGFibGUgY291bnRyaWVzIGFuZCBjb250aW5lbnRzIGlzIHNob3duIGJlbG93Og0KDQpgYGB7cn0NCmxpYnJhcnkocmVhY3RhYmxlKQ0KZGF0YSA8LSBjb3VudHJpZXM1MA0KY291bnRyaWVzIDwtIGRhdGFAZGF0YQ0KY291bnRyaWVzJT4lDQogIHNlbGVjdChuYW1lLGNvbnRpbmVudCklPiUNCiAgdW5pcXVlKCklPiUNCiAgYXJyYW5nZShjb250aW5lbnQsbmFtZSklPiUNCiAgcmVuYW1lKGNvdW50cnk9bmFtZSklPiUNCiAgcmVhY3RhYmxlKHNlYXJjaGFibGUgPSBUUlVFLGZpbHRlcmFibGUgPSBUUlVFKQ0KYGBgDQoNCiMjIFZpc3VhbGluZyB2YXJpYXRpb24gYWNyb3NzIG11bHRpcGxlIHZhcmlhYmxlcw0KDQpJZiB5b3UgaGF2ZSAzIG9yIDQgdmFyaWFibGVzLCBJIHdvdWxkIHRyeSB0byB2aXN1YWxpc2UgdGhlIHRoaXJkIGFuZCBmb3VydGggdmFyaWFibGVzIGJ5IHVzaW5nIGZlYXR1cmVzIGxpa2UgY29sb3VyLCBvciBieSBtYWtpbmcgbXVsdGlwbGUgcGxvdHMgKHdpdGggYGZhY3RldF93cmFwKClgIGFuZCBgZmFjZXRfZ3JpZCgpYCkuDQoNCkhvd2V2ZXIsIGlmIHlvdSBoYXZlIGEgd2hvbGUgYnVuY2ggb2YgdmFyaWFibGVzIHRoYXQgYXJlIGFsbCByZWxhdGVkL2Zvcm0gYSBjb2hlc2l2ZSBzZXQsIGFuZCB5b3Ugd2FudCB0byBnZXQgYW4gaWRlYSBvZiBob3cgc2ltaWxhciBvciBkaWZmZXJlbnQgaXRlbXMgYXJlIG92ZXJhbGwsIGJhc2VkIG9uIHRoZWlyIHZhbHVlcyBmb3IgKmFsbCogdGhlc2UgdmFyaWFibGVzLCB5b3UgY2FuIGRvIHRoaXMgYnkgY3JlYXRpbmcgYSAqZGlzdGFuY2UgbWF0cml4Ki4gQSBkaXN0YW5jZSBtYXRyaXggc2hvd3MgdGhlICpvdmVyYWxsKiBkaXN0YW5jZXMgYmV0d2VlbiBhbGwgdGhlIGl0ZW1zIGluIHlvdXIgZGF0YSwgYnkgdGFraW5nIHRoZSBzdW0gb2YgdGhlaXIgZGlzdGFuY2VzIGJhc2VkIG9uIGVhY2ggdmFyaWFibGUgKGZvciBtb3JlIGV4cGxhbmF0aW9uIHNlZSB0aGUgbm90ZXMgZnJvbSBsZWN0dXJlIDgpLg0KDQpOb3RlIHRoYXQgdGhpcyBvbmx5IHdvcmtzIGlmIHlvdXIgZGF0YSBpcyBudW1lcmljYWwsIG9yIGlmIGl0IGhhcyBiZWVuIGJpbmFyaXNlZC4gRm9yIG51bWVyaWNhbCBkYXRhLCBtYWtlIHN1cmUgeW91IG5vcm1hbGlzZSB0aGUgZGF0YSBzbyB0aGF0IHZhbHVlcyBvZiBlYWNoIHZhcmlhYmxlIGFyZSBjb21wYXJhYmxlIGJldHdlZW4gZGlmZmVyZW50IGl0ZW1zIChlLmcuIGluc3RlYWQgb2YgdXNpbmcgcmF3IGNvdW50cywgdXNlIHByb3BvcnRpb25zKS4NCg0KVGhlcmUgYXJlIHR3byBkaWZmZXJlbnQga2luZHMgb2YgdmlzdWFsaXNhdGlvbnMgeW91IGNhbiBtYWtlIGZyb20gYSBkaXN0YW5jZSBtYXRyaXguIA0KDQoxLiBZb3UgY2FuIGF0dGVtcHQgdG8gKnBsb3QqIHRoZXNlIGRpc3RhbmNlcywgYnkgdHJhbnNmb3JtaW5nIHRoZW0gaW50byAyIG9yIDMgZGltZW5zaW9uYWwgY29vcmRpbmF0ZXMsIGFuZCBwbG90dGluZyB0aGUgaXRlbXMgYXMgcG9pbnRzIGFsb25nIHRoZXNlIGRpbWVuc2lvbnMuDQoyLiBZb3UgY2FuIGNsdXN0ZXIgaXRlbXMgdGhhdCBhcmUgY2xvc2UgdG8gZWFjaG90aGVyIHRvZ2V0aGVyIHJlcGVhdGVkbHksIHVudGlsIHlvdSBnZXQgYSBicmFuY2hpbmcgdHJlZS4NCg0KVGhlIGZpcnN0IG1ldGhvZCBpcyBjYWxsZWQgKm11bHRpZGltZW5zaW9uYWwgc2NhbGluZyosIGFuZCB0aGUgc2Vjb25kIGlzIGNhbGxlZCAqY2x1c3RlciBhbmFseXNpcyouDQoNCk11bHRpZGltZW5zaW9uYWwgc2NhbGluZyB3b3JrcyB3ZWxsIGlmIHRoZSBkYXRhIGlzIGVhc2lseSByZWR1Y2VkIHRvIHR3byBvciB0aHJlZSBkaW1lbnNpb25zLCBidXQgdGhpcyBpc24ndCBhbHdheXMgcG9zc2libGUuIENsdXN0ZXIgYW5hbHlzaXMgd29ya3MgYmV0dGVyIHdoZW4geW91IGhhdmUgY29tcGxpY2F0ZWQgZGF0YSB3aGljaCB2YXJpZXMgYWxvbmcgbXVsdGlwbGUgZGltZW5zaW9ucy93aXRoIG1hbnkgZGlzdGluY3QgZ3JvdXBzLg0KDQojIyMgTXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nDQoNCjEuIENvbnZlcnQgdGhlIGRhdGEgaW50byBhIGRpc3RhbmNlIG1hdHJpeC0tbWFrZSBzdXJlIGl0IGlzIGFwcHJvcHJpYXRlbHkgbm9ybWFsaXNlZC9iaW5hcmlzZWQgZmlyc3QtLXVzaW5nIHRoZSBmdW5jdGlvbiBgZGlzdCgpYC4gQWRkIGBtZXRob2Q9ImJpbmFyeSJgIGlmIHRoZSBkYXRhIGlzIGJpbmFyaXNlZC4gSWYgaXQncyBudW1lcmljYWwsIHRoZSBkZWZhdWx0IG1ldGhvZCAoYGV1Y2xpZGVhbmApIGlzIGFwcHJvcHJpYXRlLg0KMi4gRmluZCBjb29yZGluYXRlcyBmb3IgdGhlc2UgZGlzdGFuY2VzIHVzaW5nIGBjbWRzY2FsZShkaXN0YW5jZXMsIGs9MilgLiBrIHNldHMgdGhlIG51bWJlciBvZiBkaW1lbnNpb25zIHRvIHVzZTsgdGhlIGRlZmF1bHQgaXMgdHdvLiANCjMuIGBjbWRzY2FsZSgpYCB3aWxsIG1ha2UgY29vcmRpbmF0ZXMgZm9yIHlvdXIgZGlzdGFuY2VzIGFzIHdlbGwgYXMgaXQgY2FuLCBidXQgaXQgY2FuJ3QgYWx3YXlzIHJlcHJlc2VudCB0aGUgZGlzdGFuY2VzIHBlcmZlY3RseSwgc28geW91IHNob3VsZCBjaGVjayBob3cgY2xvc2VseSB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdGhlIGNvb3JkaW5hdGVzIG1hdGNoZXMgdGhlIGFjdHVhbCBkaXN0YW5jZXMuIFRoaXMgaXMgY2FsbGVkIGNhbGN1bGF0aW5nIHRoZSAqc3RyZXNzKiBvbiB0aGUgY29vcmRpbmF0ZXMsIGFuZCB3ZSB3aWxsIG1ha2Ugb3VyIG93biBgc3RyZXNzKClgIGZ1bmN0aW9uIHRvIGRvIHRoaXMuDQoNCmBgYHtyLGluY2x1ZGU9VFJVRX0NCiMgZCBpcyB0aGUgb3JpZ2luYWwgZGlzdGFuY2VzLCBEIGlzIHRoZSBkaXN0YW5jZXMgYmV0d2VlbiB0aGUgY29vcmRpbmF0ZXMgbWFkZSBieSBjbWRzY2FsZSgpDQpzdHJlc3MgPC0gZnVuY3Rpb24oZCwgRCl7DQogIHNxcnQoc3VtKChkIC0gRCkgXiAyKSAvIHN1bShkIF4gMikpDQp9DQpgYGANCg0KVGhpcyBpcyBob3cgc3RyZXNzIHZhbHVlcyBhcmUgaW50ZXJwcmV0ZWQ6DQoNClZhbHVlICAgICAgICAgICAgICAgIHwgSW50ZXJwcmV0YXRpb24NCi0tLS0tLS0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tDQpTdHJlc3MgPj0gMC4yICAgICAgICB8IFBvb3INCjAuMSA8PSBTdHJlc3MgPCAwLjIgIHwgRmFpcg0KMC4wNSA8PSBTdHJlc3MgPCAwLjEgfCBHb29kDQpTdHJlc3MgPCAwLjA1ICAgICAgICB8IEV4Y2VsbGVudA0KDQo0LiBJZiB0aGUgc3RyZXNzIGlzIG9rYXksIHRoZW4geW91IGNhbiBwbG90IHRoZSBjb29yZGluYXRlcyBpbiB0d28gZGltZW5zaW9ucy4gSWYgaXQncyBub3Qgb2theSwgdGhlbiBwcm9iYWJseSBhIHRocmVlLWRpbWVuc2lvbmFsIG9yIGhpZ2hlciBzb2x1dGlvbiBpcyBuZWVkZWQgdG8gYWNjdXJhdGVseSByZXByZXNlbnQgdGhlIGRhdGEuIFlvdSBjYW4gY2hlY2sgaG93IG1hbnkgZGltZXNpb25zIGFyZSBuZWVkZWQgYnkgcGxheWluZyBhcm91bmQgd2l0aCBkaWZmZXJlbnQgdmFsdWVzIG9mIGsgaW4gdGhlIGBjbWRzY2FsZSgpYCBmdW5jdGlvbiwgYW5kIHRoZW4gY2hlY2tpbmcgdGhlIHN0cmVzcyBvbiB0aG9zZSBjb29yZGluYXRlcyAoc2VlIG5vdGVzIGZyb20gbGVjdHVyZSA4KS4gDQoNCllvdSB3aWxsIGFsc28gbmVlZCB0aGUgZnVuY3Rpb25zIGBjb2x1bW5fdG9fcm93bmFtZXMoKWAgYW5kIGBhc19kYXRhZnJhbWUocm93bmFtZXM9ImNvbHVtbl9uYW1lIilgIHRvIGdvIGJldHdlZW4gdGhlIGRhdGEgZm9ybWF0IHJlcXVpcmVkIGJ5IGBkaXN0KClgLCBhbmQgdGhhdCBuZWVkZWQgZm9yIHBsb3R0aW5nLiANCg0KQmVsb3cgaXMgYW4gZXhhbXBsZSB1c2luZyBldWNsaWRlYW4gZGlzdGFuY2VzIChpLmUuIHdpdGggbnVtZXJpY2FsIGRhdGEpLCB0byB2aXN1YWxpc2UgZGlzdGFuY2VzIGJldHdlZW4gZGlmZmVyZW50IEphcGFuZXNlIGNhc2UgcGFydGljbGVzIGluIHRlcm1zIG9mIHRoZWlyIGZ1bmN0aW9ucy4NCg0KYGBge3IgZXVjbGlkZWFufQ0KbGlicmFyeShnZ3JlcGVsKQ0KDQojIHRveSBkYXRhIGFib3V0IHRoZSBmcmVxdWVuY3kgb2YgdXNlIChhcyBhIHByb3BvcnRpb24gb2YgdGhlIHRvdGFsIGZyZXF1ZW5jeSBmb3IgZWFjaCBwYXJ0aWNsZSkgd2l0aCBkaWZmZXJlbnQgY2FzZXMgZm9yIHNvbWUgSmFwYW5lc2UgcGFydGljbGVzDQpwYXJ0aWNsZSA8LSBjKCJnYSIsIm5pIiwiZGUiLCJ3YSIsIm8iKQ0Kbm9taW5hdGl2ZSA8LSBjKDAuOTUsMCwwLDAuNSwwKQ0KZ2VuaXRpdmUgPC0gYygwLjA1LDAsMCwwLDApDQpsb2NhdGl2ZSA8LSBjKDAsMC42LDAuNywwLjEsMCkNCmluc3RydW1lbnRhbCA8LSBjKDAsMCwwLjMsMC4wNSwwKQ0KYWxsYXRpdmUgPC0gYygwLDAuMywwLDAuMSwwKQ0KYWNjdXNhdGl2ZSA8LSBjKDAsMC4xLDAsMC4zNSwxKQ0KDQpwYXJ0aWNsZXMgPC0gZGF0YS5mcmFtZShwYXJ0aWNsZSxub21pbmF0aXZlLGdlbml0aXZlLGxvY2F0aXZlLGluc3RydW1lbnRhbCxhbGxhdGl2ZSxhY2N1c2F0aXZlKQ0KDQojIDEuIE1ha2UgdGhlIGRpc3RhbmNlIG1hdHJpeA0KcGFydGljbGVzJT4lDQogICMgdHVybiB0aGUgZmlyc3QgY29sdW1uIGludG8gcm93bmFtZXMgZmlyc3QgLS0geW91ciBjb2x1bW5zIHNob3VsZCBvbmx5IGNvbnRhaW4gbWVhc3VyZW1lbnRzIG9mIHlvdXIgdmFyaWFibGVzIG9mIGludGVyZXN0DQogIGNvbHVtbl90b19yb3duYW1lcygicGFydGljbGUiKSU+JQ0KICBkaXN0KCktPiBkaXN0YW5jZXMNCg0KIyAyLiBNYWtlIGNvb3JkaW5hdGVzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdXNlIGs9MywgNCBldGMuIGZvciBoaWdoZXItZGltZW5zaW9uYWwgc29sdXRpb25zDQpjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMsaz0yKQ0KDQojIDMuIENoZWNrIHN0cmVzcyBvbiBjb29yZGluYXRlcw0KDQpzdHJlc3MoZGlzdGFuY2VzLGRpc3QoY29vcmRpbmF0ZXMpKQ0KYGBgDQoNCkhlcmUsIHRoZSBzdHJlc3MgaXMgbGVzcyB0aGFuIDAuMSBzbyB0aGUgdHdvLWRpbWVuc2lvbmFsIGNvb3JkaW5hdGVzIHJlcHJlc2VudCB0aGUgZGlzdGFuY2VzIHdlbGwsIGFuZCB0aGUgZGF0YSBjYW4gYmUgZWFzaWx5IHBsb3R0ZWQNCg0KYGBge3IgZXVjbGlkZWFuIHBsb3R9DQojIDMuIFBsb3QgY29vcmRpbmF0ZXMNCg0KY29vcmRpbmF0ZXMlPiUNCiAgICAgICAgIyBwdXQgdGhlIHJvd25hbWVzIGludG8gYSBjb2x1bW4gZmlyc3QNCiAgYXNfZGF0YV9mcmFtZShyb3duYW1lcz0icGFydGljbGUiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VjEseT1WMixsYWJlbD1wYXJ0aWNsZSkpK2dlb21fcG9pbnQoKStnZW9tX3RleHRfcmVwZWwoKSt0aGVtZV9jbGFzc2ljKCktPnBsb3QxDQpwbG90MQ0KYGBgDQoNCllvdSBjYW4gc2VlIHRoYXQgKmRlKiBhbmQgKm5pKiBoYXZlIHNpbWlsYXIgZnVuY3Rpb25zLCBhcyB0aGV5IGdyb3VwIHRvZ2V0aGVyLCB3aGlsZSAqZ2EqIGFuZCAqbyogaGF2ZSBvcHBvc2l0ZSBmdW5jdGlvbnMsIHdpdGggKndhKiBmYWxsaW5nIGluIGJldHdlZW4gKmdhKiBhbmQgKm8qLCBidXQgY2xvc2VyIHRvICpnYSouIFRoaXMgaXMgYmVjYXVzZSAqZGUqIGFuZCAqbmkqIGJvdGggaGF2ZSB0byBkbyB3aXRoIGxvY2F0aW9uLCB3aGlsZSAqZ2EqIGlzIG5vbWluYXRpdmUgYW5kICpvKiBpcyBhY2N1c2F0aXZlLiBCdXQgKndhKiBjYW4gdGFrZSBib3RoIG5vbWluYXRpdmUgYW5kIGFjY3VzYXRpdmUgY2FzZSwgYmVjYXVzZSBpdCdzIGEgdG9waWMgbWFya2VyLS10aGF0J3Mgd2h5IGl0IGZhbGxzIGluIGJldHdlZW4gKmdhKiBhbmQgKm8qLiBIb3dldmVyLCBub21pbmF0aXZlbHkgbWFya2VkIG5vdW5zIGFyZSBtb3JlIG9mdGVuIHRvcGljcyB0aGFuIGFjY3VzYXRpdmVseSBtYXJrZWQgbm91bnMsIHdoaWNoIGlzIHdoeSBpdCBpcyBjbG9zZXIgdG8gKmdhKiB0aGFuIHRvICpvKi4NCg0KQmVsb3cgaXMgYW4gZXhhbXBsZSB3aXRoIGJpbmFyeSBkaXN0YW5jZXMsIHVzaW5nIGNvZ25hY3kgZGF0YSB0byBkZXRlcm1pbmUgc2ltaWxhcml0aWVzIGJldHdlZW4gZGlmZmVyZW50IGRpYWxlY3RzIG9mIEphcGFuZXNlLg0KDQpgYGB7ciBiaW5hcnksbWVzc2FnZT1GQUxTRX0NCmphcG9uaWMgPC0gcmVhZF9jc3YoImRhdGEvamFwdm9jYWJtYXRyaXguY3N2IikNCg0KamFwb25pYyU+JQ0KICByZW5hbWUobGFuZ3VhZ2U9WCklPiUNCiAgIyBleGNsdWRlZCBkZWFkIGxhbmd1YWdlcyBhbmQgT2tpbmF3YSBiZWNhdXNlIGl0IGlzIG5vdCBhIGRpYWxlY3QgYW5kIHdpbGwgbWVzcyB1cCB0aGUgc2NhbGUgb24gdGhlIG1hcCBieSBiZWluZyBzdXBlciBmYXIgYXdheQ0KICBmaWx0ZXIobGFuZ3VhZ2UhPSJPbGRfSmFwYW5lc2UiLGxhbmd1YWdlIT0iTWlkZGxlX0phcGFuZXNlIixsYW5ndWFnZSE9Ik9raW5hd2EiKSU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoImxhbmd1YWdlIiklPiUNCiAgZGlzdChtZXRob2QgPSAiYmluYXJ5IiktPmRpc3RhbmNlcw0KDQpjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMsaz0yKQ0KDQpjb29yZGluYXRlcyU+JQ0KICBhc19kYXRhX2ZyYW1lKHJvd25hbWVzPSJMb2NhdGlvbiIpJT4lDQogIGdncGxvdChhZXMoeD1WMix5PS1WMSxsYWJlbD1Mb2NhdGlvbikpKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fdGV4dF9yZXBlbCgpKw0KICB0aGVtZV9jbGFzc2ljKCkrDQogIGxhYnMoeD0iQ29uc2VydmF0aXZlIHZlcnN1cyBwcm9ncmVzc2l2ZSIseT0iU291dGggdG8gbm9ydGgiKQ0KYGBgDQoNCkhlcmUsIGluc3RlYWQgb2YgaGF2aW5nIGRpc3RpbmN0IGdyb3Vwcywgd2UgYWN0dWFsbHkgaGF2ZSBhIGNvbnRpbnV1bSB3aGljaCB3ZSBjYW4gbGFiZWwgYXMgcmVmbGVjdGluZyB0aGUgc3BhdGlhbCBsb2NhdGlvbiAoc291dGggdG8gbm9ydGgpIG9uIHRoZSBvbmUgaGFuZCwgYXMgd2VsbCBhcyB0aGUgY29uc2VydmF0aXZlbmVzcyBvZiB0aGUgZGlhbGVjdCBvbiB0aGUgb3RoZXIgaGFuZC0taWYgeW91IGtub3cgYWJvdXQgSmFwYW5lc2UgZGlhbGVjdG9sb2d5LiANCg0KVGhlIHN0cmVzcyBvbiB0aGlzIHJlcHJlc2VudGF0aW9uIGlzIGFjdHVhbGx5IGZhaXJseSBoaWdoDQoNCmBgYHtyfQ0Kc3RyZXNzKGRpc3RhbmNlcyxkaXN0KGNvb3JkaW5hdGVzKSkNCmBgYA0KDQpIb3dldmVyLCBhIHRocmVlLWRpbWVuc2lvbmFsIHNvbHV0aW9uIGlzIG9ubHkgbWFyZ2luYWxseSBiZXR0ZXI6DQoNCmBgYHtyfQ0KaGlnaGVyX3NvbHV0aW9uIDwtIGNtZHNjYWxlKGRpc3RhbmNlcyxrPTMpDQpzdHJlc3MoZGlzdGFuY2VzLGRpc3QoaGlnaGVyX3NvbHV0aW9uKSkNCmBgYA0KDQpIZXJlLCB0aGUgaGlnaCBzdHJlc3MgdmFsdWUgaXMgbGlrZWx5IGJlY2F1c2Ugd2UgaGF2ZSAqYSBsb3QqIG9mIGxhbmd1YWdlcywgc28gaXQgbWF5IGJlIHRoYXQgaXQgaXMgaGFyZCB0byBnZXQgdGhlIGNvcnJlY3QgcG9zaXRpb25pbmcgb2YgdGhlIGxhbmd1YWdlcyB3aXRoaW4gZWFjaCBzbWFsbCBncm91cC4gVGhhdCBpcywgdGhlIHNwYXRpYWwgcmVsYXRpb25zaGlwIGJldHdlZW4gbGFuZ3VhZ2VzIHRoYXQgYXJlIGNsb3NlIHRvIGVhY2hvdGhlciBtYXkgbm90IGJlIHZlcnkgYWNjdXJhdGUuIEhvd2V2ZXIsIHRoZSBtYWluIGRpYWxlY3QgZ3JvdXBzIGFuZCB0aGUgbGFyZ2UgZGlzdGFuY2VzIGluIHRoaXMgZmlndXJlIGFyZSB3ZWxsLXJlcHJlc2VudGVkIChiYXNlZCBvbiB3aGF0IHdlIGFscmVhZHkga25vdyBhYm91dCBKYXBvbmljKSwgc28gSSB0aGluayBpdCBpcyBzdGlsbCBhIGdvb2QgcmVwcmVzZW50YXRpb24tLWFzIGxvbmcgYXMgeW91IHdhcm4gcGVvcGxlIG5vdCB0byBsb29rIGF0IHRoZSBzbWFsbCBkaXN0YW5jZXMuIElmIHRoaXMgdHdvLWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIHdhcyByZWFsbHkgYSBiYWQgcmVwcmVzZW5hdGlvbiwgd2Ugd291bGQgZXhwZWN0IGEgYmlnZ2VyIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgc3RyZXNzIGxldmVscyBvZiBhIHR3by1kaW1lbnNpb25hbCB2ZXJzdXMgdGhyZWUtZGltZW5zaW9uYWwgc29sdXRpb24uIA0KDQojIyMgQ2x1c3RlciBhbmFseXNpcw0KDQpJZiB5b3Ugd2FudCB0byBrbm93IGFib3V0IHRoZSBjbG9zZSByZWxhdGlvbnNoaXBzIChzbWFsbCBkaXN0YW5jZXMpIGFzIHdlbGwsIHlvdSBjb3VsZCB1c2UgYSBjbHVzdGVyIGFuYWx5c2lzDQoNCmBgYHtyfQ0KY2x1c3RlcnMgPC0gaGNsdXN0KGRpc3RhbmNlcyxtZXRob2Q9IndhcmQuRDIiKSAgICMgbWV0aG9kPSJ3YXJkLkQyIiB1c3VhbGx5IHByb2R1Y2VzIGdvb2QgcmVzdWx0cw0KcGxvdChjbHVzdGVycykNCmBgYA0KDQpDb21wYXJlZCB0byBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcsIGNsdXN0ZXIgYW5hbHlzZXMgYXJlIGJldHRlciBhdCBzaG93aW5nIGRpc3RpbmN0IGdyb3VwcyB3aGVuIHlvdSBoYXZlIGEgbG90IG9mIGRhdGEgcG9pbnRzLiBGcm9tIHRoZSBmaXJzdCB0d28gc3BsaXRzIGluIHRoZSB0cmVlIGhlcmUsIHdlIGFscmVhZHkgaGF2ZSBmb3VyIGRpc3RpbmN0IGdyb3Vwcy0tdGhleSBiYXNpY2FsbHkgcmVwcmVzZW50IHRoZSBub3J0aCBhbmQgZWFzdCBvZiBjb3VudHJ5LCB2ZXJzdXMgdGhlIHNvdXRoIGFuZCB3ZXN0IG9mIHRoZSBjb3VudHJ5LiBIb3dldmVyLCBpbiB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHJlcHJlc2VudGF0aW9uIHRoZSB3ZXN0ZXJuIGFuZCBlYXN0ZXJuIGdyb3VwcyBlbmRlZCB1cCBzcXVpc2hlZCB0b2dldGhlciAoaXQgd2FzIGhhcmQgdG8gY2xlYXJseSBzZXBhcmF0ZSB0aGVtKSwgc28gdGhhdCBjb3VsZCBhbHNvIGNvbnRyaWJ1dGUgdG8gaGlnaCBzdHJlc3MgbGV2ZWxzLiBUaGlzIGlzIHByb2JhYmx5IGJlY2F1c2UgdGhlIGVhc3Rlcm4gYW5kIHdlc3Rlcm4gZ3JvdXBzIGFyZSBsZXNzIGRpc3RpbmN0IGZyb20gZWFjaG90aGVyIHRoYW4gdGhlIG5vcnRoZXJuIGFuZCBzb3V0aGVybiBncm91cHMgKHdoaWNoIHdlcmUgcHJlc2VydmVkKSwgYW5kIGxpa2Ugd2Ugc2FpZCB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHJlcHJlc2VudGF0aW9uIGp1c3Qgc2hvd3MgdGhlIGdlbmVyYWwgdHJlbmRzIGFuZCBub3QgdGhlIHNtYWxsIGRpZmZlcmVuY2VzLg0KDQpTbywgaWYgSSB3ZXJlIHRvIHByZXNlbnQgdGhpcyBkYXRhLCBJIHdvdWxkIGluY2x1ZGUgYm90aCB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIGFuYWx5c2lzICh0byBzaG93IHRoZSBvdmVyYWxsIHRyZW5kcyksIGFzIHdlbGwgYXMgdGhlIGNsdXN0ZXIgYW5hbHlzaXMgKHRvIHNob3cgZGlzdGluY3QsIGluY2x1ZGluZyBzbWFsbGVyLWxldmVsLCBncm91cGluZ3MpLg0KDQojIyMgV2hlbiB0byB1c2Ugd2hpY2ggbWV0aG9kPw0KDQpJZiB5b3Ugb25seSBoYXZlIGEgZmV3IGRhdGEgcG9pbnRzIChsaWtlIGluIHRoZSBmaXJzdCBleGFtcGxlIHdpdGggdGhlIEphcGFuZXNlIHBhcnRpY2xlcyksIG11bHRpZGltZW5zaW9uYWwgc2NhbGluZyBhbmQgY2x1c3RlciBhbmFseXNlcyBhcmUgdXN1YWxseSBlcXVhbGx5IGdvb2QgYXQgcmVwcmVzZW50aW5nIHRoZSBkaXN0YW5jZXMsIGJlY2F1c2UgdGhlcmUncyBlbm91Z2ggc3BhY2Ugb24gdGhlIG11bHRpZGltZW5zaW9uYWwgc2NhbGVkIHBsb3QgdG8ga2VlcCBhbGwgdGhlIGdyb3VwcyBkaXN0aW5jdC4gU28gaXQncyBqdXN0IGEgbWF0dGVyIG9mIHBlcnNvbmFsIHByZWZlcmVuY2UgYXMgdG8gd2hpY2ggdmlzdWFsaXNhdGlvbiB5b3UgcHJlZmVyLCBhbHRob3VnaCBpZiB5b3UgdGhpbmsgdGhlIGRhdGEgaXMgaGllcmFyY2hpY2FsbHkgc3RydWN0dXJlZCB0aGUgY2x1c3RlciBhbmFseXNpcyB3b3VsZCByZXByZXNlbnQgdGhpcyBoaWVyYXJjaGljYWwgc3RydWN0dXJlIGJldHRlci4NCg0KSWYgeW91IGhhdmUgYSBsb3Qgb2YgZGF0YSBwb2ludHMsIGhvd2V2ZXIsIGl0IGlzIGhhcmQgdG8ga2VlcCBncm91cHMgZGlzdGluY3Qgb24gbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHBsb3RzIChhcyB3ZSBzYXcgd2l0aCB0aGUgSmFwYW5lc2UgZGF0YSkuIEluIHRoaXMgY2FzZSwgdGhlIGNsdXN0ZXIgYW5hbHlzaXMgaXMgYmV0dGVyIGZvciBzaG93aW5nIHRoZSBncm91cGluZ3MsIGFuZCBpdCBpcyBvbmx5IHdvcnRoIHN0aWxsIHVzaW5nIHRoZSBtdWx0aWRpbWVuc2lvbmFsIHNjYWxpbmcgcGxvdCBpZiB5b3UgdGhpbmsgdGhlIGRpbWVuc2lvbnMgYXJlIG1lYW5pbmdmdWwgYW5kIHJlcHJlc2VudCBzb21lIG92ZXJhbGwgdHJlbmRzIGluIHRoZSBkYXRhIChlLmcuIHRoZSBub3J0aC1zb3V0aCwgY29uc2VydmF0aXZlLXByb2dyZXNzaXZlIHRyZW5kcyB3ZSBzYXcgaW4gdGhlIEphcGFuZXNlIGRhdGEpLiBJZiB5b3UgY2Fubm90IHRoaW5rIG9mIGFueSBtZWFuaW5nZnVsIGxhYmVscyBmb3IgeW91ciBkaW1lbnNpb25zLCB0aGVuIHByb2JhYmx5IGFsbCB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIGFuYWx5c2lzIGlzIGRvaW5nIGlzIHRyeWluZyB0byByZXByZXNlbnQgZGlzdGluY3QgZ3JvdXBzLS1yYXRoZXIgdGhhbiBhbnkga2luZCBvZiBjb250aW51dW0tLWluIHdoaWNoIGNhc2UgSSB3b3VsZCBqdXN0IHVzZSBhIGNsdXN0ZXIgYW5hbHlzaXMgYmVjYXVzZSB0aGF0IGRvZXMgZ3JvdXBpbmcgYmV0dGVyIHdoZW4gdGhlcmUncyBsb3RzIG9mIGRhdGEgcG9pbnRzLg0KDQpGb3IgaW5zdGFuY2UsIGJlbG93IGlzIGFuIGV4YW1wbGUsIHBsb3R0aW5nIHdvcmRzIGJhc2VkIG9uIHRoZWlyIHNlbnNvcnkgbW9kYWxpdHkgcmF0aW5nczoNCg0KYGBge3IsbWVzc2FnZT1GQUxTRX0NCm5vcm1zIDwtIHJlYWRfY3N2KCJkYXRhL2x5bm90dF9jb25uZWxsXzIwMDlfbW9kYWxpdHkuY3N2IikNCg0Kbm9ybXMlPiUNCiAgc2VsZWN0KC1Qcm9wZXJ0eUJyaXRpc2gsLURvbWluYW50TW9kYWxpdHkpJT4lDQogIGNvbHVtbl90b19yb3duYW1lcygiV29yZCIpJT4lDQogIHNhbXBsZV9uKDUwKSU+JQ0KICBkaXN0KCktPmRpc3RhbmNlcw0KDQpjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMpDQoNCmNvb3JkaW5hdGVzJT4lDQogIGFzX2RhdGFfZnJhbWUocm93bmFtZXM9IldvcmQiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VjEseT1WMixsYWJlbD1Xb3JkKSkrIA0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fdGV4dF9yZXBlbCgpKw0KICB0aGVtZV9jbGFzc2ljKCkNCmBgYA0KDQpUaGVyZSBncm91cGluZ3MgYXJlIG5vdCB2ZXJ5IGNsZWFyLCBub3IgY2FuIHdlIGNvbWUgdXAgd2l0aCBhbnkgbWVhbmluZ2Z1bCBsYWJlbHMgZm9yIHRoZXNlIGRpbWVuc2lvbnMuIEluIHRoaXMgY2FzZSwgSSB3b3VsZCBqdXN0IHVzZSBhIGNsdXN0ZXIgYW5hbHlzaXMgYmVjYXVzZSBhdCBsZWFzdCB0aGVuIHRoZSBncm91cGluZ3MgYXJlIGNsZWFyOg0KDQpgYGB7cn0NCmNsdXN0ZXJzIDwtIGhjbHVzdChkaXN0YW5jZXMsbWV0aG9kPSJ3YXJkLkQyIikgICAjIG1ldGhvZD0id2FyZC5EMiIgdXN1YWxseSBwcm9kdWNlcyBnb29kIHJlc3VsdHMNCnBsb3QoY2x1c3RlcnMpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSBncm91cGluZ3MgYXJlIG5vdCB3ZWxsIHByZXNlcnZlZCBpbiB0aGUgbXVsdGlkaW1lbnNpb25hbCBzY2FsaW5nIHBsb3QgZnJvbSB0aGUgaGlnaCBzdHJlc3Mgb24gdGhlIGNvb3JkaW5hdGVzOg0KDQpgYGB7cn0NCnN0cmVzcyhkaXN0YW5jZXMsZGlzdChjb29yZGluYXRlcykpDQpgYGANCg0KQSB0aHJlZS1kaW1lbnNpb25hbCBzb2x1dGlvbiBpcyBhYmxlIHRvIHJlcHJlc2VudCB0aGUgZ3JvdXBpbmdzIGJldHRlcjoNCg0KYGBge3J9DQpuZXdjb29yZGluYXRlcyA8LSBjbWRzY2FsZShkaXN0YW5jZXMsaz0zKQ0Kc3RyZXNzKGRpc3RhbmNlcyxkaXN0KG5ld2Nvb3JkaW5hdGVzKSkNCmBgYA0KDQpBbmQgd2UgY291bGQgZXh0cmFjdCB0aGUgZGlmZmVyZW50IGdyb3VwaW5ncyBieSBwbG90dGluZyB0aGUgZGltZW5zaW9ucyBvbmUgYWZ0ZXIgdGhlIG90aGVyOg0KDQpgYGB7cn0NCm5ld2Nvb3JkaW5hdGVzJT4lDQogIGFzX2RhdGFfZnJhbWUocm93bmFtZXM9IldvcmQiKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9VjEseT1WMixsYWJlbD1Xb3JkKSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV90ZXh0X3JlcGVsKCkrDQogIHRoZW1lX2NsYXNzaWMoKQ0KDQpuZXdjb29yZGluYXRlcyU+JQ0KICBhc19kYXRhX2ZyYW1lKHJvd25hbWVzPSJXb3JkIiklPiUNCiAgZ2dwbG90KGFlcyh4PVYxLHk9VjMsbGFiZWw9V29yZCkpKw0KICBnZW9tX3BvaW50KCkrDQogIGdlb21fdGV4dF9yZXBlbCgpKw0KICB0aGVtZV9jbGFzc2ljKCkNCg0KbmV3Y29vcmRpbmF0ZXMlPiUNCiAgYXNfZGF0YV9mcmFtZShyb3duYW1lcz0iV29yZCIpJT4lDQogIGdncGxvdChhZXMoeD1WMix5PVYzLGxhYmVsPVdvcmQpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZW9tX3RleHRfcmVwZWwoKSsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmBgYA0KDQpCdXQgdGhpcyBpcyBhIGxvdCBtb3JlIHdvcmsgKGFuZCBzdGlsbCBpdCdzIGVhc2llciB0byBzZWUgdGhlIGdyb3VwcyBpbiB0aGUgY2x1c3RlciBkZW5kcm9ncmFtKSwgc28gSSB3b3VsZCBqdXN0IHVzZSB0aGUgY2x1c3RlciBhbmFseXNpcyBpbnN0ZWFkLiBJIHdvdWxkIG9ubHkgdHJ5IHBsb3R0aW5nIGFsbCB0aGUgZGltZW5zaW9ucyBpZiB5b3UgdGhpbmsgdGhleSdyZSBhbGwgbWVhbmluZ2Z1bC4NCg0KYGBge3J9DQpwbG90KGNsdXN0ZXJzKQ0KYGBgDQoNCg0KIyBEZWFsaW5nIHdpdGggRXJyb3JzDQoNCnxFcnJvciAgICAgICAgICAgICAgICAgICAgfFBvc3NpYmxlIGV4cGxhbmF0aW9uL2ZpeCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IkNvdWxkIG5vdCBmaW5kIGZ1bmN0aW9uInxZb3UgaGF2ZW4ndCBsb2FkZWQgKG9yIGluc3RhbGxlZCkgdGhlIGxpYnJhcnkgZm9yIHRoZSBmdW5jdGlvbiB5b3UncmUgdHJ5aW5nIHRvIHVzZSwgb3IgeW91J3ZlIHNwZWx0IHRoZSBmdW5jdGlvbiBuYW1lIHdyb25nIXwNCnwib2JqZWN0ICdibGFoJyBub3QgZm91bmQifFlvdSBoYXZlIGEgdHlwbyB3aXRoIHlvdXIgdmFyaWFibGUgbmFtZSAnYmxhaCcsIGkuZS4geW91J3ZlIHNwZWx0IGJsYWggd3Jvbmcgc29tZXdoZXJlLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KDQojIEdldHRpbmcgaGVscA0KDQoqIElmIHlvdSB3YW50IHRvIGtub3cgbW9yZSBhYm91dCBhbnkgZnVuY3Rpb24sIGlmIHlvdSB0eXBlIGA/ZnVuY3Rpb25fbmFtZWAsIFIgd2lsbCBzaG93IHlvdSBpbmZvcm1hdGlvbiBhYm91dCBob3cgdGhlIGZ1bmN0aW9uIHdvcmtzIGluIHRoZSBib3R0b20gcmlnaHQgcGFuZQ0KKiBJZiB5b3Ugc3RpbGwgbmVlZCBoZWxwLCB0cnkgc2VhcmNoaW5nIGZvciB5b3VyIHF1ZXN0aW9uIG9uIFtzdGFjayBleGNoYW5nZV0oaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvdGFnZ2VkL3IpIG9yIGp1c3QgZ29vZ2xlIGFsc28gdXN1YWxseSB3b3JrcyA6KQ0KKiBUaGVyZSBhcmUgYWxzbyB0aGUgW1IgY2hlYXRzaGVldHNdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pDQoqIEFuZCBvZiBjb3Vyc2UsIGFsc28gYXNrIHlvdXIgcXVlc3Rpb24gaW4gdGhlIHJlbGV2YW50IGRpc2N1c3Npb24gYm9hcmQgb24gc3R1ZGl1bSENCg0K