Had an issue changing the working directory errors like Error in file(file, “rt”) : cannot open the connection,, when I attempted to set the working directory using setwd(“E:/School/Summer 2021/Security and Data Governance/Handout/Handout 6/book/ch04/data”) would issue another error that the working directory could not be changed in the chunk.
I fixed by going to Session on the menu and Set working directory there.
# set working directory to chapter location
# (change for where you set up files in ch 2)
#getwd()
#setwd(file.path("E:", "School", "Summer 2021", "Security and Data Governance", "Handout","Handout 6","book","ch04"))
#setwd("~/book/ch04")
# make sure the packages for this chapter
# are installed, install if necessary
pkg <- c("bitops", "ggplot2", "mapproj", "stringr", "maps",
"grid", "gridExtra", "RColorBrewer", "igraph",
"colorspace", "scales", "stringr", "reshape2")
new.pkg <- pkg[!(pkg %in% installed.packages())]
if (length(new.pkg)) {
install.packages(new.pkg)
}
# requires packages: bitops
library(bitops) # load the bitops functions
# Define functions for converting IP addresses to/from integers
# take an IP address string in dotted octets (e.g. "192.168.0.1")
# and convert it to a 32-bit long integer (e.g. 3232235521)
ip2long <- function(ip) {
# convert string into vector of characters
ips <- unlist(strsplit(ip, '.', fixed=TRUE))
# set up a function to bit-shift, then "OR" the octets
octet <- function(x,y) bitOr(bitShiftL(x, 8), y)
# Reduce applys a function cumulatively left to right
Reduce(octet, as.integer(ips))
}
# take an 32-bit integer IP address (e.g. 3232235521)
# and convert it to a (e.g. "192.168.0.1").
long2ip <- function(longip) {
# set up reversing bit manipulation
octet <- function(nbits) bitAnd(bitShiftR(longip, nbits), 0xFF)
# Map applys a function to each element of the argument
# paste converts arguments to character and concatenates them
paste(Map(octet, c(24,16,8,0)), sep="", collapse=".")
}
#Test the functionality by reviewing the output of the following code
long2ip(ip2long("192.168.0.0"))
[1] "192.168.0.0"
#Test the functionality by reviewing the output of the following code
long2ip(ip2long("192.168.100.6"))
[1] "192.168.100.6"
# Define function to test for IP CIDR membership
# take an IP address (string) and a CIDR (string) and
# return whether the given IP address is in the CIDR range
ip.is.in.cidr <- function(ip, cidr) {
long.ip <- ip2long(ip)
cidr.parts <- unlist(strsplit(cidr, "/"))
cidr.range <- ip2long(cidr.parts[1])
cidr.mask <- bitShiftL(bitFlip(0), (32-as.integer(cidr.parts[2])))
return(bitAnd(long.ip, cidr.mask) == bitAnd(cidr.range, cidr.mask))
}
# Let us know that the IP falls within a certain range, in this case it does, comes back as TRUE.
ip.is.in.cidr("10.0.1.15","10.0.1.3/24")
[1] TRUE
# Let us know that the IP falls within a certain range, in this case it does not, comes back as FALSE.
ip.is.in.cidr("10.0.1.15","10.0.2.255/24")
[1] FALSE
Here I received error in strsplit, I put the whole path in the variable avRep. That cleared the problem
# R code to extract longitude/latitude pairs from AlienVault data
# read in the AlienVault reputation data
avRep <- "E:/School/Summer 2021/Security and Data Governance/Handout/Handout 6/book/ch04/data/reputation.data"
av.df <- read.csv(avRep, sep="#", header=FALSE)
colnames(av.df) <- c("IP", "Reliability", "Risk", "Type",
"Country", "Locale", "Coords", "x")
# create a vector of lat/long data by splitting on ","
av.coords.vec <- unlist(strsplit(as.character(av.df$Coords), ","))
# convert the vector in a 2-column matrix
av.coords.mat <- matrix(av.coords.vec, ncol=2, byrow=TRUE)
# project into a data frame
av.coords.df <- as.data.frame(av.coords.mat)
# name the columns
colnames(av.coords.df) <- c("lat","long")
# convert the characters to numeric values
av.coords.df$long <- as.double(as.character(av.coords.df$long))
av.coords.df$lat <- as.double(as.character(av.coords.df$lat))
# requires packages: ggplot2, maps, RColorBrewer, scales
# requires object: av.coords.df (4-3)
# R code to extract longitude/latitude pairs from AlienVault data
# need plotting and mapping functions plus colors
library(ggplot2)
library(maps)
library(RColorBrewer)
library(scales)
Which of the libraries listed above was previously used in this course? # ggplot2
# extract a color palette from the RColorBrewer package
set2 <- brewer.pal(8,"Set2")
# extract the polygon information for the world map, minus Antarctica
world <- map_data('world')
world <- subset(world, region != "Antarctica")
Do you see the utility of the code ran above? Please explain. # Yes, # extract the polygon information for the world map, minus Antarctica # > world <- map_data(‘world’) # > world <- subset(world, region != “Antarctica”) # Create a variable world and creating map_data of the world by using “world” package. # Second line create world map excluding Antarctica.
# plot the map with the points marking lat/lon of the geocoded entries
# plotting ~200K takes a bit of time
c
function (...) .Primitive("c")
Make comments about the output as well as the syntax used in the previous task.
This allows to see an IP address with a physical location. The map is showing high density of plot points in on the eastern side of the United States and some in California, also we seeing the same for Europe and on the coast of Asia,
Created a variable gg to hold the function of ggplot # gg <- ggplot()
Here it is gather the long and groups the plots by use longitude and latitude and group the points filling the inside with white using the geom_polygon function and ggplot and storing in gg. # gg <- gg + geom_polygon(data=world, aes(long, lat, group=group), # fill=“white”) gg variable is going plot the point of the data frame setting the color, the size. # gg <- gg + geom_point(data=av.coords.df, aes(x=long, y=lat),
# color=set2[2], size=1, alpha=0.1) # gg <- gg + labs(x="“, y=”") gg variable is setting the theme and backgroud of the map. # gg <- gg + theme(panel.background=element_rect(fill=alpha(set2[3],0.2), # colour=‘white’)) Displays the gg map. # gg
Had issue with the directory again just added the path from above and ran.
# requires packages: stringr
# requires object: av.df (4-3)
# R code to incporporate IANA IPv4 allocations
# retrieve IANA prefix list
library(stringr)
ianaURL <- "http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.csv"
ianaData <- "E:/School/Summer 2021/Security and Data Governance/Handout/Handout 6/book/ch04/data/ipv4-address-space.csv"
if (file.access(ianaData)) {
download.file(ianaURL, ianaData)
}
# read in the IANA table
iana <- read.csv(ianaData)
# clean up the iana prefix since it uses the old/BSD-
# number formatting (i.e. allows leading zeroes and
# we do not need to know the CIDR component.
iana$Prefix <- sub("^(00|0)", "", iana$Prefix, perl=TRUE)
iana$Prefix <- sub("/8$", "", iana$Prefix, perl=TRUE)
# define function to strip 'n' characters from a string
# (character vector) and return the shortened string.
# note that this function is 'vectorized' (you can pass it a single
# string or a vector of them)
rstrip <- function(x, n){
substr(x, 1, nchar(x)-n)
}
# extract just the prefix from the AlienVault list
av.IP.prefix <- rstrip(str_extract(as.character(av.df$IP),
"^([0-9]+)\\."), 1)
# there are faster ways than 'sapply()' but we wanted you to
# see the general "apply" pattern in action as you will use it
# quite a bit throughout your work in R
av.df$Designation <- sapply(av.IP.prefix, function(ip) {
iana[iana$Prefix == ip, ]$Designation
})
# summarize, order & review the findings
summary(factor(av.df$Designation))
Administered by AFRINIC Administered by APNIC Administered by ARIN Administered by RIPE NCC AFRINIC
322 2615 17974 5893 1896
APNIC ARIN AT&T Bell Laboratories Digital Equipment Corporation Hewlett-Packard Company
93776 42358 24 1 3
LACNIC Level 3 Communications, Inc. PSINet, Inc. RIPE NCC
18914 31 30 74789
# requires packages: ggplot2, maps, RColorBrewer
# requires object: av.coords.df (4-3), iana (4-5)
# Code to extract IANA block assignments & compare w/AlienVault groups
# create a new data frame from the iana designation factors
iana.df <- data.frame(table(iana$Designation))
colnames(iana.df) <- c("Registry", "IANA.Block.Count")
# make a data frame of the counts of the av iana
# designation factor
tmp.df <- data.frame(table(factor(av.df$Designation)))
colnames(tmp.df) <- c("Registry", "AlienVault.IANA.Count")
# merge (join) the data frames on the "reg" column
combined.df <- merge(iana.df, tmp.df)
print(combined.df[with(combined.df, order(-IANA.Block.Count)),],
row.names=FALSE)
# requires packages: reshape, grid, gridExtra, ggplot2, RColorBrewer
# requires object: combined.df (4-6), set2 (4-4)
# generates Figure 4-3
# plot charts from IANA data
# flatten the data frame by making one entry per "count" type
# versus having the counts in individual columns
# need the 'melt()' function from the reshape package
# to transform the data frame shape
library(reshape2)
library(grid)
library(gridExtra)
# normalize the IANA and AV values to % so bar chart scales
# match and make it easier to compare
combined.df$IANA.pct <- 100 * (combined.df$IANA.Block.Count /
sum(combined.df$IANA.Block.Count))
combined.df$AV.pct <- 100 * (combined.df$AlienVault.IANA.Count /
sum(combined.df$AlienVault.IANA.Count))
combined.df$IANA.vs.AV.pct <- combined.df$IANA.pct - combined.df$AV.pct
melted.df <- melt(combined.df)
Using Registry as id variables
# plot the new melted data frame values
gg1 <- ggplot(data=melted.df[melted.df$variable=="IANA.pct",],
aes(x=reorder(Registry, -value), y=value))
# set min/max for axis so scale is same for both charts
gg1 <- gg1 + ylim(0,40)
gg1 <- gg1 + geom_bar(stat="identity", fill=set2[3]) # using bars
# make a better label for the y axis
gg1 <- gg1 + labs(x="Registry", y="%", title="IANA %")
# make bar chart horizontal
gg1 <- gg1 + coord_flip()
# rotate the x-axis labels and remove the legend
gg1 <- gg1 + theme(axis.text.x = element_text(angle = 90, hjust = 1),
panel.background = element_blank(),
legend.position = "none")
gg1

gg2 <- ggplot(data=melted.df[melted.df$variable=="AV.pct",],
aes(x=reorder(Registry,-value), y=value))
gg2 <- gg2 + geom_bar(stat="identity", fill=set2[4]) # using bars
gg2 <- gg2 + ylim(0,40)
gg2 <- gg2 + labs(x="Registry", y="%", title="AlienVault IANA %")
gg2 <- gg2 + coord_flip()
gg2 <- gg2 + theme(axis.text.x = element_text(angle = 90, hjust = 1),
panel.background = element_blank(),
legend.position = "none")
gg2

# grid.arrange makes it possible to do very precise placement of
# multiple ggplot objects
grid.arrange(gg1, gg2, ncol=1, nrow=2)

I would like you to explain the output as well as the syntax.In addition, make comments about the possibility to redefine the functions gg1 and gg2 in such a way that the y-axis shows a more clear description of the problem.
The output are showing there are variation, but there are larger blocks that contribute the majority of malicious host. Such as RIPE NCC, Administered by ARIN and LACNIC.
Plot the new melted data frame values
gg2 <- ggplot(data=melted.df[melted.df$variable==“AV.pct”,], aes(x=reorder(Registry,-value), y=value)) gg2 <- gg2 + geom_bar(stat=“identity”, fill=set2[4]) # using bars
Set the min/max for axis so scale is same for both charts
gg2 <- gg2 + ylim(0,40) gg2 <- gg2 + labs(x=“Registry”, y=“%”, title=“AlienVault IANA %”) gg2 <- gg2 + coord_flip()
Rotate the x-axis labels and remove the legend
gg2 <- gg2 + theme(axis.text.x = element_text(angle = 90, hjust = 1), panel.background = element_blank(), legend.position = “none”) gg2 # Can deduce the same for AlienVaultIANA %, start with Administered by ARIN, LACNIC, ARIN, RIPE NCC APNIC contribute large blocks of malicious hosts.
# requires packages: ggplot2
# requires object: combined.df (4-7), set2 (4-4)
gg <- ggplot(data=combined.df,
aes(x=reorder(Registry, -IANA.Block.Count), y=AV.pct ))
gg <- gg + geom_bar(stat="identity", fill=set2[2])
gg <- gg + labs(x="Registry", y="Count",
title="AlienVault/IANA sorted by IANA (low-to-high")
gg <- gg + coord_flip()
gg <- gg + theme(axis.text.x = element_text(angle = 90, hjust = 1),
panel.background = element_blank(),
legend.position = "none")
gg

Explain the output as well as the syntax used in the task we just completed above.
The output is telling us that AlienVault population does gravitate towards the IANA blocks with the most allocations.
The code is plotting AlienVault population per IANA block sorted by IANA Blocksize
requires packages: ggplot2
requires object: combined.df (4-7), set2 (4-4)
gg <- ggplot(data=combined.df, aes(x=reorder(Registry, -IANA.Block.Count), y=AV.pct )) gg <- gg + geom_bar(stat=“identity”, fill=set2[2]) gg <- gg + labs(x=“Registry”, y=“Count”, title=“AlienVault/IANA sorted by IANA (low-to-high”) gg <- gg + coord_flip() gg <- gg + theme(axis.text.x = element_text(angle = 90, hjust = 1), panel.background = element_blank(), legend.position = “none”) gg
In order to fully understand the task we are performing in the query below as well as the output, we would need to run the code listed in Handout7. Handouts 6 and 7 are to be completed within the same time frame so that our findings and conclusions can be more solid.
# requires packages: ggplot2, scales
# requires object: combined.df (4-7), set2 (4-4)
# generates figure 4-6
library(scales)
gg <- ggplot(data=combined.df)
gg <- gg + geom_point(aes(x=IANA.Block.Count,
y=AlienVault.IANA.Count),
color=set2[2], size=4)
gg <- gg + labs(x="IANA Block Count", y="AlienVault IANA Count",
title="IANA ~ AlienVault")
gg <- gg + theme(axis.text.x = element_text(angle = 90, hjust = 1),
legend.position = "none")
gg <- gg + theme(panel.background=element_rect(fill=alpha(set2[3],0.2),
colour='white'))
gg

Explain the output as well as the syntax we included in the chunk above. Use Handout 7 and class discussion to support your answers. # The scatterplot is showing a positive correlation.
requires packages: ggplot2, scales
requires object: combined.df (4-7), set2 (4-4)
Creating a scatterplot by displaying the relationship between tow continous variables.
gg <- gg + geom_point(aes(x=IANA.Block.Count, y=AlienVault.IANA.Count), color=set2[2], size=4)
Creating labels for the scatterplot diagram.
gg <- gg + labs(x=“IANA Block Count”, y=“AlienVault IANA Count”, title=“IANA ~ AlienVault”)
Create a theme to customize the non-data components of the plots.
gg <- gg + theme(axis.text.x = element_text(angle = 90, hjust = 1), legend.position = “none”) gg <- gg + theme(panel.background=element_rect(fill=alpha(set2[3],0.2), colour=‘white’))
This returning the correlation coeffiecent the results falls close to +1, this indicates a strong positive linear relationship between the two variables.
cor(combined.df$IANA.Block.Count,
combined.df$AlienVault.IANA.Count, method="spearman")
[1] 0.9488598
Make comments about both the output and the syntax. # This returning the correlation coeffiecent the results falls close to +1, this indicates a strong positive linear relationship between the two variables. # Spearman correlation was applied it produces a rank correlation coefficient and is more suited to variables that do not have a normal distribution. Visual patterns are showing that larger blocks of networks will contain more malicious hosts.
LS0tDQp0aXRsZTogIkhhbmRvdXQgNiINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQoNCiMgSGFkIGFuIGlzc3VlIGNoYW5naW5nIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBlcnJvcnMgbGlrZSBFcnJvciBpbiBmaWxlKGZpbGUsICJydCIpIDogY2Fubm90IG9wZW4gdGhlIGNvbm5lY3Rpb24sLCB3aGVuIEkgYXR0ZW1wdGVkIHRvIHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgdXNpbmcgc2V0d2QoIkU6L1NjaG9vbC9TdW1tZXIgMjAyMS9TZWN1cml0eSBhbmQgRGF0YSBHb3Zlcm5hbmNlL0hhbmRvdXQvSGFuZG91dCA2L2Jvb2svY2gwNC9kYXRhIikgd291bGQgaXNzdWUgYW5vdGhlciBlcnJvciB0aGF0IHRoZSB3b3JraW5nIGRpcmVjdG9yeSBjb3VsZCBub3QgYmUgY2hhbmdlZCBpbiB0aGUgY2h1bmsuDQojIEkgZml4ZWQgYnkgZ29pbmcgdG8gU2Vzc2lvbiBvbiB0aGUgbWVudSBhbmQgU2V0IHdvcmtpbmcgZGlyZWN0b3J5IHRoZXJlLg0KYGBge3J9DQojIHNldCB3b3JraW5nIGRpcmVjdG9yeSB0byBjaGFwdGVyIGxvY2F0aW9uDQojIChjaGFuZ2UgZm9yIHdoZXJlIHlvdSBzZXQgdXAgZmlsZXMgaW4gY2ggMikNCiNnZXR3ZCgpDQojc2V0d2QoZmlsZS5wYXRoKCJFOiIsICJTY2hvb2wiLCAiU3VtbWVyIDIwMjEiLCAiU2VjdXJpdHkgYW5kIERhdGEgR292ZXJuYW5jZSIsICJIYW5kb3V0IiwiSGFuZG91dCA2IiwiYm9vayIsImNoMDQiKSkNCiNzZXR3ZCgifi9ib29rL2NoMDQiKQ0KIyBtYWtlIHN1cmUgdGhlIHBhY2thZ2VzIGZvciB0aGlzIGNoYXB0ZXINCiMgYXJlIGluc3RhbGxlZCwgaW5zdGFsbCBpZiBuZWNlc3NhcnkNCnBrZyA8LSBjKCJiaXRvcHMiLCAiZ2dwbG90MiIsICJtYXBwcm9qIiwgInN0cmluZ3IiLCAibWFwcyIsDQogICAgICAgICAiZ3JpZCIsICJncmlkRXh0cmEiLCAiUkNvbG9yQnJld2VyIiwgImlncmFwaCIsDQogICAgICAgICAiY29sb3JzcGFjZSIsICJzY2FsZXMiLCAic3RyaW5nciIsICJyZXNoYXBlMiIpDQpuZXcucGtnIDwtIHBrZ1shKHBrZyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKV0NCmlmIChsZW5ndGgobmV3LnBrZykpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnKSAgDQp9DQpgYGANCg0KDQpgYGB7cn0NCiMgcmVxdWlyZXMgcGFja2FnZXM6IGJpdG9wcw0KbGlicmFyeShiaXRvcHMpICMgbG9hZCB0aGUgYml0b3BzIGZ1bmN0aW9ucw0KDQojIERlZmluZSBmdW5jdGlvbnMgZm9yIGNvbnZlcnRpbmcgSVAgYWRkcmVzc2VzIHRvL2Zyb20gaW50ZWdlcnMNCiMgdGFrZSBhbiBJUCBhZGRyZXNzIHN0cmluZyBpbiBkb3R0ZWQgb2N0ZXRzIChlLmcuICIxOTIuMTY4LjAuMSIpDQojIGFuZCBjb252ZXJ0IGl0IHRvIGEgMzItYml0IGxvbmcgaW50ZWdlciAoZS5nLiAzMjMyMjM1NTIxKQ0KaXAybG9uZyA8LSBmdW5jdGlvbihpcCkgew0KICAjIGNvbnZlcnQgc3RyaW5nIGludG8gdmVjdG9yIG9mIGNoYXJhY3RlcnMNCiAgaXBzIDwtIHVubGlzdChzdHJzcGxpdChpcCwgJy4nLCBmaXhlZD1UUlVFKSkNCiAgIyBzZXQgdXAgYSBmdW5jdGlvbiB0byBiaXQtc2hpZnQsIHRoZW4gIk9SIiB0aGUgb2N0ZXRzDQogIG9jdGV0IDwtIGZ1bmN0aW9uKHgseSkgYml0T3IoYml0U2hpZnRMKHgsIDgpLCB5KQ0KICAjIFJlZHVjZSBhcHBseXMgYSBmdW5jdGlvbiBjdW11bGF0aXZlbHkgbGVmdCB0byByaWdodA0KICBSZWR1Y2Uob2N0ZXQsIGFzLmludGVnZXIoaXBzKSkNCn0NCg0KYGBgDQoNCg0KDQoNCmBgYHtyfQ0KIyB0YWtlIGFuIDMyLWJpdCBpbnRlZ2VyIElQIGFkZHJlc3MgKGUuZy4gMzIzMjIzNTUyMSkNCiMgYW5kIGNvbnZlcnQgaXQgdG8gYSAoZS5nLiAiMTkyLjE2OC4wLjEiKS4NCmxvbmcyaXAgPC0gZnVuY3Rpb24obG9uZ2lwKSB7DQogICMgc2V0IHVwIHJldmVyc2luZyBiaXQgbWFuaXB1bGF0aW9uDQogIG9jdGV0IDwtIGZ1bmN0aW9uKG5iaXRzKSBiaXRBbmQoYml0U2hpZnRSKGxvbmdpcCwgbmJpdHMpLCAweEZGKQ0KICAjIE1hcCBhcHBseXMgYSBmdW5jdGlvbiB0byBlYWNoIGVsZW1lbnQgb2YgdGhlIGFyZ3VtZW50DQogICMgcGFzdGUgY29udmVydHMgYXJndW1lbnRzIHRvIGNoYXJhY3RlciBhbmQgY29uY2F0ZW5hdGVzIHRoZW0NCiAgcGFzdGUoTWFwKG9jdGV0LCBjKDI0LDE2LDgsMCkpLCBzZXA9IiIsIGNvbGxhcHNlPSIuIikNCn0NCmBgYA0KDQoNCg0KYGBge3J9DQojVGVzdCB0aGUgZnVuY3Rpb25hbGl0eSBieSByZXZpZXdpbmcgdGhlIG91dHB1dCBvZiB0aGUgZm9sbG93aW5nIGNvZGUNCmxvbmcyaXAoaXAybG9uZygiMTkyLjE2OC4wLjAiKSkNCmBgYA0KDQoNCg0KYGBge3J9DQojVGVzdCB0aGUgZnVuY3Rpb25hbGl0eSBieSByZXZpZXdpbmcgdGhlIG91dHB1dCBvZiB0aGUgZm9sbG93aW5nIGNvZGUNCmxvbmcyaXAoaXAybG9uZygiMTkyLjE2OC4xMDAuNiIpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgRGVmaW5lIGZ1bmN0aW9uIHRvIHRlc3QgZm9yIElQIENJRFIgbWVtYmVyc2hpcA0KIyB0YWtlIGFuIElQIGFkZHJlc3MgKHN0cmluZykgYW5kIGEgQ0lEUiAoc3RyaW5nKSBhbmQNCiMgcmV0dXJuIHdoZXRoZXIgdGhlIGdpdmVuIElQIGFkZHJlc3MgaXMgaW4gdGhlIENJRFIgcmFuZ2UNCmlwLmlzLmluLmNpZHIgPC0gZnVuY3Rpb24oaXAsIGNpZHIpIHsNCiAgbG9uZy5pcCA8LSBpcDJsb25nKGlwKQ0KICBjaWRyLnBhcnRzIDwtIHVubGlzdChzdHJzcGxpdChjaWRyLCAiLyIpKQ0KICBjaWRyLnJhbmdlIDwtIGlwMmxvbmcoY2lkci5wYXJ0c1sxXSkNCiAgY2lkci5tYXNrIDwtIGJpdFNoaWZ0TChiaXRGbGlwKDApLCAoMzItYXMuaW50ZWdlcihjaWRyLnBhcnRzWzJdKSkpDQogIHJldHVybihiaXRBbmQobG9uZy5pcCwgY2lkci5tYXNrKSA9PSBiaXRBbmQoY2lkci5yYW5nZSwgY2lkci5tYXNrKSkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KIyBMZXQgdXMga25vdyB0aGF0IHRoZSBJUCBmYWxscyB3aXRoaW4gYSBjZXJ0YWluIHJhbmdlLCBpbiB0aGlzIGNhc2UgaXQgZG9lcywgY29tZXMgYmFjayBhcyBUUlVFLg0KaXAuaXMuaW4uY2lkcigiMTAuMC4xLjE1IiwiMTAuMC4xLjMvMjQiKQ0KYGBgDQoNCmBgYHtyfQ0KIyBMZXQgdXMga25vdyB0aGF0IHRoZSBJUCBmYWxscyB3aXRoaW4gYSBjZXJ0YWluIHJhbmdlLCBpbiB0aGlzIGNhc2UgaXQgZG9lcyBub3QsIGNvbWVzIGJhY2sgYXMgRkFMU0UuDQppcC5pcy5pbi5jaWRyKCIxMC4wLjEuMTUiLCIxMC4wLjIuMjU1LzI0IikNCmBgYA0KDQojIEhlcmUgSSByZWNlaXZlZCBlcnJvciBpbiBzdHJzcGxpdCwgSSBwdXQgdGhlIHdob2xlIHBhdGggaW4gdGhlIHZhcmlhYmxlIGF2UmVwLiBUaGF0IGNsZWFyZWQgdGhlIHByb2JsZW0NCmBgYHtyfQ0KIyBSIGNvZGUgdG8gZXh0cmFjdCBsb25naXR1ZGUvbGF0aXR1ZGUgcGFpcnMgZnJvbSBBbGllblZhdWx0IGRhdGENCiMgcmVhZCBpbiB0aGUgQWxpZW5WYXVsdCByZXB1dGF0aW9uIGRhdGENCmF2UmVwIDwtICJFOi9TY2hvb2wvU3VtbWVyIDIwMjEvU2VjdXJpdHkgYW5kIERhdGEgR292ZXJuYW5jZS9IYW5kb3V0L0hhbmRvdXQgNi9ib29rL2NoMDQvZGF0YS9yZXB1dGF0aW9uLmRhdGEiDQphdi5kZiA8LSByZWFkLmNzdihhdlJlcCwgc2VwPSIjIiwgaGVhZGVyPUZBTFNFKQ0KY29sbmFtZXMoYXYuZGYpIDwtIGMoIklQIiwgIlJlbGlhYmlsaXR5IiwgIlJpc2siLCAiVHlwZSIsDQogICAgICAgICAgICAgICAgICAgICAiQ291bnRyeSIsICJMb2NhbGUiLCAiQ29vcmRzIiwgIngiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgY3JlYXRlIGEgdmVjdG9yIG9mIGxhdC9sb25nIGRhdGEgYnkgc3BsaXR0aW5nIG9uICIsIg0KYXYuY29vcmRzLnZlYyA8LSB1bmxpc3Qoc3Ryc3BsaXQoYXMuY2hhcmFjdGVyKGF2LmRmJENvb3JkcyksICIsIikpDQpgYGANCg0KDQpgYGB7cn0NCiMgY29udmVydCB0aGUgdmVjdG9yIGluIGEgMi1jb2x1bW4gbWF0cml4DQphdi5jb29yZHMubWF0IDwtIG1hdHJpeChhdi5jb29yZHMudmVjLCBuY29sPTIsIGJ5cm93PVRSVUUpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBwcm9qZWN0IGludG8gYSBkYXRhIGZyYW1lDQphdi5jb29yZHMuZGYgPC0gYXMuZGF0YS5mcmFtZShhdi5jb29yZHMubWF0KQ0KYGBgDQoNCg0KYGBge3J9DQojIG5hbWUgdGhlIGNvbHVtbnMgDQpjb2xuYW1lcyhhdi5jb29yZHMuZGYpIDwtIGMoImxhdCIsImxvbmciKQ0KYGBgDQoNCg0KYGBge3J9DQojIGNvbnZlcnQgdGhlIGNoYXJhY3RlcnMgdG8gbnVtZXJpYyB2YWx1ZXMNCmF2LmNvb3Jkcy5kZiRsb25nIDwtIGFzLmRvdWJsZShhcy5jaGFyYWN0ZXIoYXYuY29vcmRzLmRmJGxvbmcpKQ0KYXYuY29vcmRzLmRmJGxhdCA8LSBhcy5kb3VibGUoYXMuY2hhcmFjdGVyKGF2LmNvb3Jkcy5kZiRsYXQpKQ0KYGBgDQoNCmBgYHtyfQ0KIyByZXF1aXJlcyBwYWNrYWdlczogZ2dwbG90MiwgbWFwcywgUkNvbG9yQnJld2VyLCBzY2FsZXMNCiMgcmVxdWlyZXMgb2JqZWN0OiBhdi5jb29yZHMuZGYgKDQtMykNCiMgUiBjb2RlIHRvIGV4dHJhY3QgbG9uZ2l0dWRlL2xhdGl0dWRlIHBhaXJzIGZyb20gQWxpZW5WYXVsdCBkYXRhDQojIG5lZWQgcGxvdHRpbmcgYW5kIG1hcHBpbmcgZnVuY3Rpb25zIHBsdXMgY29sb3JzDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KG1hcHMpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoc2NhbGVzKQ0KYGBgDQoNCldoaWNoIG9mIHRoZSBsaWJyYXJpZXMgbGlzdGVkIGFib3ZlIHdhcyBwcmV2aW91c2x5IHVzZWQgaW4gdGhpcyBjb3Vyc2U/DQojIGdncGxvdDINCg0KYGBge3J9DQojIGV4dHJhY3QgYSBjb2xvciBwYWxldHRlIGZyb20gdGhlIFJDb2xvckJyZXdlciBwYWNrYWdlDQpzZXQyIDwtIGJyZXdlci5wYWwoOCwiU2V0MiIpDQpgYGANCg0KYGBge3J9DQojIGV4dHJhY3QgdGhlIHBvbHlnb24gaW5mb3JtYXRpb24gZm9yIHRoZSB3b3JsZCBtYXAsIG1pbnVzIEFudGFyY3RpY2ENCndvcmxkIDwtIG1hcF9kYXRhKCd3b3JsZCcpDQp3b3JsZCA8LSBzdWJzZXQod29ybGQsIHJlZ2lvbiAhPSAiQW50YXJjdGljYSIpDQpgYGANCg0KRG8geW91IHNlZSB0aGUgdXRpbGl0eSBvZiB0aGUgY29kZSByYW4gYWJvdmU/IFBsZWFzZSBleHBsYWluLg0KIyBZZXMsDQojIGV4dHJhY3QgdGhlIHBvbHlnb24gaW5mb3JtYXRpb24gZm9yIHRoZSB3b3JsZCBtYXAsIG1pbnVzIEFudGFyY3RpY2ENCiMgPiB3b3JsZCA8LSBtYXBfZGF0YSgnd29ybGQnKQ0KIyA+IHdvcmxkIDwtIHN1YnNldCh3b3JsZCwgcmVnaW9uICE9ICJBbnRhcmN0aWNhIikNCiMgQ3JlYXRlIGEgdmFyaWFibGUgd29ybGQgYW5kIGNyZWF0aW5nIG1hcF9kYXRhIG9mIHRoZSB3b3JsZCBieSB1c2luZyAid29ybGQiIHBhY2thZ2UuDQojIFNlY29uZCBsaW5lIGNyZWF0ZSB3b3JsZCBtYXAgZXhjbHVkaW5nIEFudGFyY3RpY2EuDQpgYGB7cn0NCiMgcGxvdCB0aGUgbWFwIHdpdGggdGhlIHBvaW50cyBtYXJraW5nIGxhdC9sb24gb2YgdGhlIGdlb2NvZGVkIGVudHJpZXMNCiMgcGxvdHRpbmcgfjIwMEsgdGFrZXMgYSBiaXQgb2YgdGltZQ0KYw0KYGBgDQoNCg0KTWFrZSBjb21tZW50cyBhYm91dCB0aGUgb3V0cHV0IGFzIHdlbGwgYXMgdGhlIHN5bnRheCB1c2VkIGluIHRoZSBwcmV2aW91cyB0YXNrLg0KDQojIFRoaXMgYWxsb3dzIHRvIHNlZSBhbiBJUCBhZGRyZXNzIHdpdGggYSBwaHlzaWNhbCBsb2NhdGlvbi4gVGhlIG1hcCBpcyBzaG93aW5nIGhpZ2ggZGVuc2l0eSBvZiBwbG90IHBvaW50cyBpbiBvbiB0aGUgZWFzdGVybiBzaWRlIG9mIHRoZSBVbml0ZWQgU3RhdGVzIGFuZCBzb21lIGluIENhbGlmb3JuaWEsIGFsc28gd2Ugc2VlaW5nIHRoZSBzYW1lIGZvciBFdXJvcGUgYW5kIG9uIHRoZSBjb2FzdCBvZiBBc2lhLA0KDQpDcmVhdGVkIGEgdmFyaWFibGUgZ2cgdG8gaG9sZCB0aGUgZnVuY3Rpb24gb2YgZ2dwbG90DQojIGdnIDwtIGdncGxvdCgpDQoNCkhlcmUgaXQgaXMgZ2F0aGVyIHRoZSBsb25nIGFuZCBncm91cHMgdGhlIHBsb3RzIGJ5IHVzZSBsb25naXR1ZGUgYW5kIGxhdGl0dWRlIGFuZCBncm91cCB0aGUgcG9pbnRzIGZpbGxpbmcgdGhlIGluc2lkZSB3aXRoIHdoaXRlIHVzaW5nIHRoZSBnZW9tX3BvbHlnb24gZnVuY3Rpb24gYW5kIGdncGxvdCBhbmQgc3RvcmluZyBpbiBnZy4NCiMgZ2cgPC0gZ2cgKyBnZW9tX3BvbHlnb24oZGF0YT13b3JsZCwgYWVzKGxvbmcsIGxhdCwgZ3JvdXA9Z3JvdXApLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD0id2hpdGUiKQ0KZ2cgdmFyaWFibGUgaXMgZ29pbmcgcGxvdCB0aGUgcG9pbnQgb2YgdGhlIGRhdGEgZnJhbWUgc2V0dGluZyB0aGUgY29sb3IsIHRoZSBzaXplLg0KIyBnZyA8LSBnZyArIGdlb21fcG9pbnQoZGF0YT1hdi5jb29yZHMuZGYsIGFlcyh4PWxvbmcsIHk9bGF0KSwgIA0KIyAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9c2V0MlsyXSwgc2l6ZT0xLCBhbHBoYT0wLjEpDQojIGdnIDwtIGdnICsgbGFicyh4PSIiLCB5PSIiKQ0KZ2cgdmFyaWFibGUgaXMgc2V0dGluZyB0aGUgdGhlbWUgYW5kIGJhY2tncm91ZCBvZiB0aGUgbWFwLg0KIyBnZyA8LSBnZyArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQ9ZWxlbWVudF9yZWN0KGZpbGw9YWxwaGEoc2V0MlszXSwwLjIpLCANCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91cj0nd2hpdGUnKSkNCkRpc3BsYXlzIHRoZSBnZyBtYXAuDQojIGdnDQoNCiMgSGFkIGlzc3VlIHdpdGggdGhlIGRpcmVjdG9yeSBhZ2FpbiBqdXN0IGFkZGVkIHRoZSBwYXRoIGZyb20gYWJvdmUgYW5kIHJhbi4NCmBgYHtyfQ0KIyByZXF1aXJlcyBwYWNrYWdlczogc3RyaW5ncg0KIyByZXF1aXJlcyBvYmplY3Q6IGF2LmRmICg0LTMpDQojIFIgY29kZSB0byBpbmNwb3Jwb3JhdGUgSUFOQSBJUHY0IGFsbG9jYXRpb25zDQojIHJldHJpZXZlIElBTkEgcHJlZml4IGxpc3QNCmxpYnJhcnkoc3RyaW5ncikNCg0KaWFuYVVSTCA8LSAiaHR0cDovL3d3dy5pYW5hLm9yZy9hc3NpZ25tZW50cy9pcHY0LWFkZHJlc3Mtc3BhY2UvaXB2NC1hZGRyZXNzLXNwYWNlLmNzdiINCmlhbmFEYXRhIDwtICJFOi9TY2hvb2wvU3VtbWVyIDIwMjEvU2VjdXJpdHkgYW5kIERhdGEgR292ZXJuYW5jZS9IYW5kb3V0L0hhbmRvdXQgNi9ib29rL2NoMDQvZGF0YS9pcHY0LWFkZHJlc3Mtc3BhY2UuY3N2Ig0KaWYgKGZpbGUuYWNjZXNzKGlhbmFEYXRhKSkgew0KICBkb3dubG9hZC5maWxlKGlhbmFVUkwsIGlhbmFEYXRhKSANCn0NCmBgYA0KDQoNCg0KYGBge3J9DQojIHJlYWQgaW4gdGhlIElBTkEgdGFibGUNCmlhbmEgPC0gcmVhZC5jc3YoaWFuYURhdGEpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBjbGVhbiB1cCB0aGUgaWFuYSBwcmVmaXggc2luY2UgaXQgdXNlcyB0aGUgb2xkL0JTRC0NCiMgbnVtYmVyIGZvcm1hdHRpbmcgKGkuZS4gYWxsb3dzIGxlYWRpbmcgemVyb2VzIGFuZA0KIyB3ZSBkbyBub3QgbmVlZCB0byBrbm93IHRoZSBDSURSIGNvbXBvbmVudC4NCmlhbmEkUHJlZml4IDwtIHN1YigiXigwMHwwKSIsICIiLCBpYW5hJFByZWZpeCwgcGVybD1UUlVFKQ0KaWFuYSRQcmVmaXggPC0gc3ViKCIvOCQiLCAiIiwgaWFuYSRQcmVmaXgsIHBlcmw9VFJVRSkNCmBgYA0KDQoNCg0KYGBge3J9DQojIGRlZmluZSBmdW5jdGlvbiB0byBzdHJpcCAnbicgY2hhcmFjdGVycyBmcm9tIGEgc3RyaW5nDQojIChjaGFyYWN0ZXIgdmVjdG9yKSBhbmQgcmV0dXJuIHRoZSBzaG9ydGVuZWQgc3RyaW5nLg0KIyBub3RlIHRoYXQgdGhpcyBmdW5jdGlvbiBpcyAndmVjdG9yaXplZCcgKHlvdSBjYW4gcGFzcyBpdCBhIHNpbmdsZQ0KIyBzdHJpbmcgb3IgYSB2ZWN0b3Igb2YgdGhlbSkNCnJzdHJpcCA8LSBmdW5jdGlvbih4LCBuKXsNCiAgc3Vic3RyKHgsIDEsIG5jaGFyKHgpLW4pDQp9DQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBleHRyYWN0IGp1c3QgdGhlIHByZWZpeCBmcm9tIHRoZSBBbGllblZhdWx0IGxpc3QNCmF2LklQLnByZWZpeCA8LSByc3RyaXAoc3RyX2V4dHJhY3QoYXMuY2hhcmFjdGVyKGF2LmRmJElQKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiXihbMC05XSspXFwuIiksIDEpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyB0aGVyZSBhcmUgZmFzdGVyIHdheXMgdGhhbiAnc2FwcGx5KCknIGJ1dCB3ZSB3YW50ZWQgeW91IHRvIA0KIyBzZWUgdGhlIGdlbmVyYWwgImFwcGx5IiBwYXR0ZXJuIGluIGFjdGlvbiBhcyB5b3Ugd2lsbCB1c2UgaXQNCiMgcXVpdGUgYSBiaXQgdGhyb3VnaG91dCB5b3VyIHdvcmsgaW4gUg0KYXYuZGYkRGVzaWduYXRpb24gPC0gc2FwcGx5KGF2LklQLnByZWZpeCwgZnVuY3Rpb24oaXApIHsNCiAgaWFuYVtpYW5hJFByZWZpeCA9PSBpcCwgXSREZXNpZ25hdGlvbg0KfSkNCmBgYA0KDQpgYGB7cn0NCiMgc3VtbWFyaXplLCBvcmRlciAmIHJldmlldyB0aGUgZmluZGluZ3MNCnN1bW1hcnkoZmFjdG9yKGF2LmRmJERlc2lnbmF0aW9uKSkNCmBgYA0KDQoNCmBgYHtyfQ0KIyByZXF1aXJlcyBwYWNrYWdlczogZ2dwbG90MiwgbWFwcywgUkNvbG9yQnJld2VyDQojIHJlcXVpcmVzIG9iamVjdDogYXYuY29vcmRzLmRmICg0LTMpLCBpYW5hICg0LTUpDQojIENvZGUgdG8gZXh0cmFjdCBJQU5BIGJsb2NrIGFzc2lnbm1lbnRzICYgY29tcGFyZSB3L0FsaWVuVmF1bHQgZ3JvdXBzDQojIGNyZWF0ZSBhIG5ldyBkYXRhIGZyYW1lIGZyb20gdGhlIGlhbmEgZGVzaWduYXRpb24gZmFjdG9ycw0KaWFuYS5kZiA8LSBkYXRhLmZyYW1lKHRhYmxlKGlhbmEkRGVzaWduYXRpb24pKQ0KY29sbmFtZXMoaWFuYS5kZikgPC0gYygiUmVnaXN0cnkiLCAiSUFOQS5CbG9jay5Db3VudCIpDQoNCiMgbWFrZSBhIGRhdGEgZnJhbWUgb2YgdGhlIGNvdW50cyBvZiB0aGUgYXYgaWFuYQ0KIyBkZXNpZ25hdGlvbiBmYWN0b3INCnRtcC5kZiA8LSBkYXRhLmZyYW1lKHRhYmxlKGZhY3Rvcihhdi5kZiREZXNpZ25hdGlvbikpKQ0KY29sbmFtZXModG1wLmRmKSA8LSBjKCJSZWdpc3RyeSIsICJBbGllblZhdWx0LklBTkEuQ291bnQiKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCiMgbWVyZ2UgKGpvaW4pIHRoZSBkYXRhIGZyYW1lcyBvbiB0aGUgInJlZyIgY29sdW1uDQpjb21iaW5lZC5kZiA8LSBtZXJnZShpYW5hLmRmLCB0bXAuZGYpDQpwcmludChjb21iaW5lZC5kZlt3aXRoKGNvbWJpbmVkLmRmLCBvcmRlcigtSUFOQS5CbG9jay5Db3VudCkpLF0sDQogICAgICByb3cubmFtZXM9RkFMU0UpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyByZXF1aXJlcyBwYWNrYWdlczogcmVzaGFwZSwgZ3JpZCwgZ3JpZEV4dHJhLCBnZ3Bsb3QyLCBSQ29sb3JCcmV3ZXINCiMgcmVxdWlyZXMgb2JqZWN0OiBjb21iaW5lZC5kZiAoNC02KSwgc2V0MiAoNC00KQ0KIyBnZW5lcmF0ZXMgRmlndXJlIDQtMw0KIyBwbG90IGNoYXJ0cyBmcm9tIElBTkEgZGF0YQ0KIyBmbGF0dGVuIHRoZSBkYXRhIGZyYW1lIGJ5IG1ha2luZyBvbmUgZW50cnkgcGVyICJjb3VudCIgdHlwZQ0KIyB2ZXJzdXMgaGF2aW5nIHRoZSBjb3VudHMgaW4gaW5kaXZpZHVhbCBjb2x1bW5zDQojIG5lZWQgdGhlICdtZWx0KCknIGZ1bmN0aW9uIGZyb20gdGhlIHJlc2hhcGUgcGFja2FnZQ0KIyB0byB0cmFuc2Zvcm0gdGhlIGRhdGEgZnJhbWUgc2hhcGUNCmxpYnJhcnkocmVzaGFwZTIpIA0KbGlicmFyeShncmlkKQ0KbGlicmFyeShncmlkRXh0cmEpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBub3JtYWxpemUgdGhlIElBTkEgYW5kIEFWIHZhbHVlcyB0byAlIHNvIGJhciBjaGFydCBzY2FsZXMNCiMgbWF0Y2ggYW5kIG1ha2UgaXQgZWFzaWVyIHRvIGNvbXBhcmUNCmNvbWJpbmVkLmRmJElBTkEucGN0IDwtIDEwMCAqIChjb21iaW5lZC5kZiRJQU5BLkJsb2NrLkNvdW50IC8gDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW0oY29tYmluZWQuZGYkSUFOQS5CbG9jay5Db3VudCkpDQpjb21iaW5lZC5kZiRBVi5wY3QgPC0gMTAwICogKGNvbWJpbmVkLmRmJEFsaWVuVmF1bHQuSUFOQS5Db3VudCAvIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bShjb21iaW5lZC5kZiRBbGllblZhdWx0LklBTkEuQ291bnQpKQ0KDQpjb21iaW5lZC5kZiRJQU5BLnZzLkFWLnBjdCA8LSBjb21iaW5lZC5kZiRJQU5BLnBjdCAtIGNvbWJpbmVkLmRmJEFWLnBjdA0KYGBgDQoNCg0KYGBge3J9DQptZWx0ZWQuZGYgPC0gbWVsdChjb21iaW5lZC5kZikNCiMgcGxvdCB0aGUgbmV3IG1lbHRlZCBkYXRhIGZyYW1lIHZhbHVlcw0KYGBgDQoNCmBgYHtyfQ0KZ2cxIDwtIGdncGxvdChkYXRhPW1lbHRlZC5kZlttZWx0ZWQuZGYkdmFyaWFibGU9PSJJQU5BLnBjdCIsXSwgDQogICAgICAgICAgICAgIGFlcyh4PXJlb3JkZXIoUmVnaXN0cnksIC12YWx1ZSksIHk9dmFsdWUpKQ0KIyBzZXQgbWluL21heCBmb3IgYXhpcyBzbyBzY2FsZSBpcyBzYW1lIGZvciBib3RoIGNoYXJ0cw0KZ2cxIDwtIGdnMSArIHlsaW0oMCw0MCkNCmdnMSA8LSBnZzEgKyAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsPXNldDJbM10pICMgdXNpbmcgYmFycw0KDQojIG1ha2UgYSBiZXR0ZXIgbGFiZWwgZm9yIHRoZSB5IGF4aXMNCmdnMSA8LSBnZzEgKyBsYWJzKHg9IlJlZ2lzdHJ5IiwgeT0iJSIsIHRpdGxlPSJJQU5BICUiKSANCiMgbWFrZSBiYXIgY2hhcnQgaG9yaXpvbnRhbA0KZ2cxIDwtIGdnMSArIGNvb3JkX2ZsaXAoKQ0KIyByb3RhdGUgdGhlIHgtYXhpcyBsYWJlbHMgYW5kIHJlbW92ZSB0aGUgbGVnZW5kDQpnZzEgPC0gZ2cxICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwgDQogICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpnZzENCmBgYA0KDQpgYGB7cn0NCmdnMiA8LSBnZ3Bsb3QoZGF0YT1tZWx0ZWQuZGZbbWVsdGVkLmRmJHZhcmlhYmxlPT0iQVYucGN0IixdLCANCiAgICAgICAgICAgICAgYWVzKHg9cmVvcmRlcihSZWdpc3RyeSwtdmFsdWUpLCB5PXZhbHVlKSkNCmdnMiA8LSBnZzIgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9c2V0Mls0XSkgIyB1c2luZyBiYXJzDQpnZzIgPC0gZ2cyICsgeWxpbSgwLDQwKQ0KZ2cyIDwtIGdnMiArIGxhYnMoeD0iUmVnaXN0cnkiLCB5PSIlIiwgdGl0bGU9IkFsaWVuVmF1bHQgSUFOQSAlIikgDQpnZzIgPC0gZ2cyICsgY29vcmRfZmxpcCgpDQpnZzIgPC0gZ2cyICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwgDQogICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpnZzINCmBgYA0KDQpgYGB7cn0NCiMgZ3JpZC5hcnJhbmdlIG1ha2VzIGl0IHBvc3NpYmxlIHRvIGRvIHZlcnkgcHJlY2lzZSBwbGFjZW1lbnQgb2YgDQojIG11bHRpcGxlIGdncGxvdCBvYmplY3RzDQpncmlkLmFycmFuZ2UoZ2cxLCBnZzIsIG5jb2w9MSwgbnJvdz0yKQ0KYGBgDQoNCg0KSSB3b3VsZCBsaWtlIHlvdSB0byBleHBsYWluIHRoZSBvdXRwdXQgYXMgd2VsbCBhcyB0aGUgc3ludGF4LkluIGFkZGl0aW9uLCBtYWtlIGNvbW1lbnRzIGFib3V0IHRoZSBwb3NzaWJpbGl0eSB0byByZWRlZmluZSB0aGUgZnVuY3Rpb25zIGdnMSBhbmQgZ2cyIGluIHN1Y2ggYSB3YXkgdGhhdCB0aGUgeS1heGlzIHNob3dzIGEgbW9yZSBjbGVhciBkZXNjcmlwdGlvbiBvZiB0aGUgcHJvYmxlbS4NCg0KIyBUaGUgb3V0cHV0IGFyZSBzaG93aW5nIHRoZXJlIGFyZSB2YXJpYXRpb24sIGJ1dCB0aGVyZSBhcmUgbGFyZ2VyIGJsb2NrcyB0aGF0IGNvbnRyaWJ1dGUgdGhlIG1ham9yaXR5IG9mIG1hbGljaW91cyBob3N0LiBTdWNoIGFzIFJJUEUgTkNDLCBBZG1pbmlzdGVyZWQgYnkgQVJJTiBhbmQgTEFDTklDLg0KDQojIFBsb3QgdGhlIG5ldyBtZWx0ZWQgZGF0YSBmcmFtZSB2YWx1ZXMNCmdnMiA8LSBnZ3Bsb3QoZGF0YT1tZWx0ZWQuZGZbbWVsdGVkLmRmJHZhcmlhYmxlPT0iQVYucGN0IixdLCANCiAgICAgICAgICAgICAgYWVzKHg9cmVvcmRlcihSZWdpc3RyeSwtdmFsdWUpLCB5PXZhbHVlKSkNCmdnMiA8LSBnZzIgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9c2V0Mls0XSkgIyB1c2luZyBiYXJzDQoNCiMgU2V0IHRoZSBtaW4vbWF4IGZvciBheGlzIHNvIHNjYWxlIGlzIHNhbWUgZm9yIGJvdGggY2hhcnRzDQpnZzIgPC0gZ2cyICsgeWxpbSgwLDQwKQ0KZ2cyIDwtIGdnMiArIGxhYnMoeD0iUmVnaXN0cnkiLCB5PSIlIiwgdGl0bGU9IkFsaWVuVmF1bHQgSUFOQSAlIikgDQpnZzIgPC0gZ2cyICsgY29vcmRfZmxpcCgpDQoNCiMgUm90YXRlIHRoZSB4LWF4aXMgbGFiZWxzIGFuZCByZW1vdmUgdGhlIGxlZ2VuZA0KZ2cyIDwtIGdnMiArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSksIA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KZ2cyDQojIENhbiBkZWR1Y2UgdGhlIHNhbWUgZm9yIEFsaWVuVmF1bHRJQU5BICUsIHN0YXJ0IHdpdGggQWRtaW5pc3RlcmVkIGJ5IEFSSU4sIExBQ05JQywgQVJJTiwgUklQRSBOQ0MgQVBOSUMgY29udHJpYnV0ZSBsYXJnZSBibG9ja3Mgb2YgbWFsaWNpb3VzIGhvc3RzLiANCg0KDQpgYGB7cn0NCiMgcmVxdWlyZXMgcGFja2FnZXM6IGdncGxvdDINCiMgcmVxdWlyZXMgb2JqZWN0OiBjb21iaW5lZC5kZiAoNC03KSwgc2V0MiAoNC00KQ0KZ2cgPC0gZ2dwbG90KGRhdGE9Y29tYmluZWQuZGYsIA0KICAgICAgICAgICAgIGFlcyh4PXJlb3JkZXIoUmVnaXN0cnksIC1JQU5BLkJsb2NrLkNvdW50KSwgeT1BVi5wY3QgKSkNCmdnIDwtIGdnICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBmaWxsPXNldDJbMl0pDQpnZyA8LSBnZyArIGxhYnMoeD0iUmVnaXN0cnkiLCB5PSJDb3VudCIsDQogICAgICAgICAgICAgICAgdGl0bGU9IkFsaWVuVmF1bHQvSUFOQSBzb3J0ZWQgYnkgSUFOQSAobG93LXRvLWhpZ2giKSANCmdnIDwtIGdnICsgY29vcmRfZmxpcCgpDQpnZyA8LSBnZyArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSksIA0KICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpnZw0KYGBgDQoNCkV4cGxhaW4gdGhlIG91dHB1dCBhcyB3ZWxsIGFzIHRoZSBzeW50YXggdXNlZCBpbiB0aGUgdGFzayB3ZSBqdXN0IGNvbXBsZXRlZCBhYm92ZS4NCg0KIyBUaGUgb3V0cHV0IGlzIHRlbGxpbmcgdXMgdGhhdCBBbGllblZhdWx0IHBvcHVsYXRpb24gZG9lcyBncmF2aXRhdGUgdG93YXJkcyB0aGUgSUFOQSBibG9ja3Mgd2l0aCB0aGUgbW9zdCBhbGxvY2F0aW9ucy4NCg0KDQojIFRoZSBjb2RlIGlzIHBsb3R0aW5nIEFsaWVuVmF1bHQgcG9wdWxhdGlvbiBwZXIgSUFOQSBibG9jayBzb3J0ZWQgYnkgSUFOQSBCbG9ja3NpemUNCg0KIyByZXF1aXJlcyBwYWNrYWdlczogZ2dwbG90Mg0KIyByZXF1aXJlcyBvYmplY3Q6IGNvbWJpbmVkLmRmICg0LTcpLCBzZXQyICg0LTQpDQpnZyA8LSBnZ3Bsb3QoZGF0YT1jb21iaW5lZC5kZiwgDQogICAgICAgICAgICAgYWVzKHg9cmVvcmRlcihSZWdpc3RyeSwgLUlBTkEuQmxvY2suQ291bnQpLCB5PUFWLnBjdCApKQ0KZ2cgPC0gZ2cgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGZpbGw9c2V0MlsyXSkNCmdnIDwtIGdnICsgbGFicyh4PSJSZWdpc3RyeSIsIHk9IkNvdW50IiwNCiAgICAgICAgICAgICAgICB0aXRsZT0iQWxpZW5WYXVsdC9JQU5BIHNvcnRlZCBieSBJQU5BIChsb3ctdG8taGlnaCIpIA0KZ2cgPC0gZ2cgKyBjb29yZF9mbGlwKCkNCmdnIDwtIGdnICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwgDQogICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmdnDQoNCkluIG9yZGVyIHRvIGZ1bGx5IHVuZGVyc3RhbmQgdGhlIHRhc2sgd2UgYXJlIHBlcmZvcm1pbmcgaW4gdGhlIHF1ZXJ5IGJlbG93IGFzIHdlbGwgYXMgdGhlIG91dHB1dCwgd2Ugd291bGQgbmVlZCB0byBydW4gdGhlIGNvZGUgbGlzdGVkIGluIEhhbmRvdXQ3LiBIYW5kb3V0cyA2IGFuZCA3IGFyZSB0byBiZSBjb21wbGV0ZWQgd2l0aGluIHRoZSBzYW1lIHRpbWUgZnJhbWUgc28gdGhhdCBvdXIgZmluZGluZ3MgYW5kIGNvbmNsdXNpb25zIGNhbiBiZSBtb3JlIHNvbGlkLg0KDQoNCmBgYHtyfQ0KIyByZXF1aXJlcyBwYWNrYWdlczogZ2dwbG90Miwgc2NhbGVzDQojIHJlcXVpcmVzIG9iamVjdDogY29tYmluZWQuZGYgKDQtNyksIHNldDIgKDQtNCkNCiMgZ2VuZXJhdGVzIGZpZ3VyZSA0LTYNCmxpYnJhcnkoc2NhbGVzKQ0KZ2cgPC0gZ2dwbG90KGRhdGE9Y29tYmluZWQuZGYpDQpnZyA8LSBnZyArIGdlb21fcG9pbnQoYWVzKHg9SUFOQS5CbG9jay5Db3VudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHk9QWxpZW5WYXVsdC5JQU5BLkNvdW50KSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1zZXQyWzJdLCBzaXplPTQpDQpnZyA8LSBnZyArIGxhYnMoeD0iSUFOQSBCbG9jayBDb3VudCIsIHk9IkFsaWVuVmF1bHQgSUFOQSBDb3VudCIsDQogICAgICAgICAgICAgICAgdGl0bGU9IklBTkEgfiBBbGllblZhdWx0IikNCmdnIDwtIGdnICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSwgDQogICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmdnIDwtIGdnICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZD1lbGVtZW50X3JlY3QoZmlsbD1hbHBoYShzZXQyWzNdLDAuMiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91cj0nd2hpdGUnKSkNCmdnDQpgYGANCg0KRXhwbGFpbiB0aGUgb3V0cHV0IGFzIHdlbGwgYXMgdGhlIHN5bnRheCB3ZSBpbmNsdWRlZCBpbiB0aGUgY2h1bmsgYWJvdmUuIFVzZSBIYW5kb3V0IDcgYW5kIGNsYXNzIGRpc2N1c3Npb24gdG8gc3VwcG9ydCB5b3VyIGFuc3dlcnMuDQojIFRoZSBzY2F0dGVycGxvdCBpcyBzaG93aW5nIGEgcG9zaXRpdmUgY29ycmVsYXRpb24uDQoNCiMgcmVxdWlyZXMgcGFja2FnZXM6IGdncGxvdDIsIHNjYWxlcw0KIyByZXF1aXJlcyBvYmplY3Q6IGNvbWJpbmVkLmRmICg0LTcpLCBzZXQyICg0LTQpDQojIGdlbmVyYXRlcyBmaWd1cmUgNC02DQpsaWJyYXJ5KHNjYWxlcykNCiNDcmVhdGluZyBhIHZhcmlhYmxlIGdnIHRvIGhvbGQgdGhlIGdncGxvdCBmcm9tIHRoZSBjb21iaW5lZC5kZiANCmdnIDwtIGdncGxvdChkYXRhPWNvbWJpbmVkLmRmKQ0KDQojIENyZWF0aW5nIGEgc2NhdHRlcnBsb3QgYnkgZGlzcGxheWluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdG93IGNvbnRpbm91cyB2YXJpYWJsZXMuDQpnZyA8LSBnZyArIGdlb21fcG9pbnQoYWVzKHg9SUFOQS5CbG9jay5Db3VudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHk9QWxpZW5WYXVsdC5JQU5BLkNvdW50KSwNCiAgICAgICAgICAgICAgICAgICAgICBjb2xvcj1zZXQyWzJdLCBzaXplPTQpDQogICAgICAgICAgICAgICAgICAgICAgDQojIENyZWF0aW5nIGxhYmVscyBmb3IgdGhlIHNjYXR0ZXJwbG90IGRpYWdyYW0uDQpnZyA8LSBnZyArIGxhYnMoeD0iSUFOQSBCbG9jayBDb3VudCIsIHk9IkFsaWVuVmF1bHQgSUFOQSBDb3VudCIsDQogICAgICAgICAgICAgICAgdGl0bGU9IklBTkEgfiBBbGllblZhdWx0IikNCg0KIyBDcmVhdGUgYSB0aGVtZSB0byBjdXN0b21pemUgdGhlIG5vbi1kYXRhIGNvbXBvbmVudHMgb2YgdGhlIHBsb3RzLg0KZ2cgPC0gZ2cgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpLCANCiAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KZ2cgPC0gZ2cgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kPWVsZW1lbnRfcmVjdChmaWxsPWFscGhhKHNldDJbM10sMC4yKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyPSd3aGl0ZScpKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiMgRGlzcGxheSBzY2F0dGVycGxvdC4NCmdnDQoNCg0KIyBUaGlzIHJldHVybmluZyB0aGUgY29ycmVsYXRpb24gY29lZmZpZWNlbnQgdGhlIHJlc3VsdHMgZmFsbHMgY2xvc2UgdG8gKzEsIHRoaXMgaW5kaWNhdGVzIGEgc3Ryb25nIHBvc2l0aXZlIGxpbmVhciByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdHdvIHZhcmlhYmxlcy4NCmBgYHtyfQ0KY29yKGNvbWJpbmVkLmRmJElBTkEuQmxvY2suQ291bnQsDQogICAgY29tYmluZWQuZGYkQWxpZW5WYXVsdC5JQU5BLkNvdW50LCBtZXRob2Q9InNwZWFybWFuIikNCmBgYA0KDQpNYWtlIGNvbW1lbnRzIGFib3V0IGJvdGggdGhlIG91dHB1dCBhbmQgdGhlIHN5bnRheC4NCiMgVGhpcyByZXR1cm5pbmcgdGhlIGNvcnJlbGF0aW9uIGNvZWZmaWVjZW50IHRoZSByZXN1bHRzIGZhbGxzIGNsb3NlIHRvICsxLCB0aGlzIGluZGljYXRlcyBhIHN0cm9uZyBwb3NpdGl2ZSBsaW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMuDQojIFNwZWFybWFuIGNvcnJlbGF0aW9uIHdhcyBhcHBsaWVkICBpdCBwcm9kdWNlcyBhIHJhbmsgY29ycmVsYXRpb24gY29lZmZpY2llbnQgYW5kIGlzIG1vcmUgc3VpdGVkIHRvIHZhcmlhYmxlcyB0aGF0IGRvIG5vdCBoYXZlIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVmlzdWFsIHBhdHRlcm5zIGFyZSBzaG93aW5nIHRoYXQgbGFyZ2VyIGJsb2NrcyBvZiBuZXR3b3JrcyB3aWxsIGNvbnRhaW4gbW9yZSBtYWxpY2lvdXMgaG9zdHMuDQo=