These packages are needed for this assignment:
pkg <- c("ggplot2", "RColorBrewer")
new.pkg <- pkg[!(pkg %in% installed.packages())]
if (length(new.pkg)) {
install.packages(new.pkg)
}
Let us first make sure we are working in a directory we feel comfortable with. So I will start by changing my working directory to the one listed below
Let us now read the file and start exploring the dataset memproc.
mydata1<- read.csv("data/memproc.csv", header=T)
summary(mydata1)
host proc mem state
Length:247 Min. :-3.1517 Min. :-3.5939 Length:247
Class :character 1st Qu.:-1.2056 1st Qu.:-1.4202 Class :character
Mode :character Median :-0.4484 Median :-0.6212 Mode :character
Mean :-0.4287 Mean :-0.5181
3rd Qu.: 0.3689 3rd Qu.: 0.2413
Max. : 3.1428 Max. : 3.2184
mydata1
In order to explore this dataset more in detail, let us create a plot to compare the processor and memory usage, and differentiate it based on the malware state.
library(ggplot2)
gg <- ggplot(mydata1, aes(proc, mem, color=state))
gg <- gg + scale_color_brewer(palette="Set2")
gg <- gg + geom_point(size=3) + theme_bw()
print(gg)

I suggest you to create now a new graph but this time including the title in the chart. The title would be “Memory vs Processor Usage as function of the Malaware state”. # Added a title by using + ggtitle(“Memory vs Processor Usage as function of the Malaware state”)) to the print: # print(gg + ggtitle(“Memory vs Processor Usage as function of the Malaware state”))
library(ggplot2)
gg <- ggplot(mydata1, aes(proc, mem, color=state))
gg <- gg + scale_color_brewer(palette="Set2")
gg <- gg + geom_point(size=3) + theme_bw()
print(gg + ggtitle("Memory vs Processor Usage as function of the Malaware state"))

Going to take 1/3 of the data for test data and the train the algorithm
on the remaining 2/3.
set.seed(1492)
# count how many in the overall sample
n <- nrow(mydata1)
# set the test.size to be 1/3rd
test.size <- as.integer(n/3)
# randomly sample the rows for test set
testset <- sample(n, test.size)
# now split the data into test and train
test <- mydata1[testset, ]
train <- mydata1[-testset, ]
Splitting the data into test and train
# pull out proc and mem columns for infected then normal
# then use colMeans() to means of the columns
inf <- colMeans(train[train$state=="Infected", c("proc", "mem")])
nrm <- colMeans(train[train$state=="Normal", c("proc", "mem")])
Display the results of the on the two columns and return vector with
two elements.
print(inf)
proc mem
0.9354513 1.0868010
print(nrm)
proc mem
-0.7907962 -0.9352974
Predicting malware predict.malware take in a single named vector called
data, extract out the proc and mem values, then calculate how far those are from the means
tat was generated during the training. Calculate the distance by using the
Pythagorean theorem a^2 + b^2 = c^2. Once the difference between the trained proc mean
and test proc mean and the trained mem mean and the test mem mean is calculated we just’
then just compare them.
predict.malware <- function(data) {
# get 'proc' and 'mem' as numeric values
proc <- as.numeric(data[['proc']])
mem <- as.numeric(data[['mem']])
# set up infected comparison
inf.a <- inf['proc'] - proc
inf.b <- inf['mem'] - mem
# pythagorean distance c = sqrt(a^2 + b^2)
inf.dist <- sqrt(inf.a^2 + inf.b^2)
# repeat for normal systems
nrm.a <- nrm['proc'] - proc
nrm.b <- nrm['mem'] - mem
nrm.dist <- sqrt(nrm.a^2 + nrm.b^2)
# assign a label of the closest (smallest)
ifelse(inf.dist<nrm.dist,"Infected", "Normal")
}
# could test with these if you uncomment them
# predict.malware(inf['proc'], inf['mem'])
# expect "Infected"
# predict.malware(nrm['proc'], nrm['mem'])
# expect "Normal"
requires the values from test test <- memproc[testset, ] and predict.malware value
calculated earlier.
prediction <- apply(test, 1, predict.malware)
Now have a set of predictions and the ability to compare
them to the real values to determine how well it did, look at
the proportion of correctly predicted results on the test data.
Then calculate that by taking the number of correct predictions
(where the real test$state and the predicted prediction match) and then
dividing that by the total number of predictions.
This simple algorithgm predicted 90% of the values correctly.
sum(test$state==prediction)/nrow(test)
[1] 0.902439
# Figure 9-2 #########################################################
slope <- -1*(1/((inf['mem']-nrm['mem'])/(inf['proc']-nrm['proc'])))
intercept <- mean(c(inf['mem'], nrm['mem'])) - (slope*mean(c(inf['proc'], nrm['proc'])))
result <- cbind(test, predict=prediction)
result$Accurate <- ifelse(result$state==result$predict, "Yes", "No")
result$Accurate <- factor(result$Accurate, levels=c("Yes", "No"), ordered=T)
This classifier creates a line halfway between the two means and perpendicular to an intersecting line. Anything above the line is predicted as infected; anything below is predicted to be normal.
The misclassified values are clearly marked. There are normal system above the line that are mislabeled as well as the infected systems below the line.
gg <- ggplot(result, aes(proc, mem, color=state, size=Accurate, shape=Accurate))
gg <- gg + scale_shape_manual(values=c(16, 8))
gg <- gg + scale_size_manual(values=c(3, 6))
gg <- gg + scale_color_brewer(palette="Set2")
gg <- gg + geom_point() + theme_bw()
gg <- gg + geom_abline(intercept = intercept, slope = slope, color="gray80")
print(gg)

Here we going to plot linear regression on non-linear data, this happens because
part of the linear regression is a reference to the linear coefficients estimated
not the data.
set.seed(1)
x <- runif(200, min=-10, max=10)
y <- 1.377*(x^3) + 0.92*(x^2) + .3*x + rnorm(200, sd=250) + 1572
x <- x + 10
smooth <- ggplot(data.frame(x,y), aes(x, y)) + geom_point() +
geom_smooth(method = "lm", formula = y ~ poly(x, 3), size = 1, se=F) +
theme_bw()
print(smooth)

memproc <- read.csv("data/memproc.csv", header=T)
memproc$infected <- ifelse(memproc$state=="Infected", 1, 0)
set.seed(1492)
n <- nrow(memproc)
test.size <- as.integer(n/3)
testset <- sample(n, test.size)
test <- memproc[testset, ]
train <- memproc[-testset, ]
The plots are showing linear regression on infection test data
Output on the x-axis is an estimated probability of a host being infected
based on the input variables. This plotted against the known values in the test
data on the y-axis(real life).
glm() function is one of serveral approaches to logistic regression. This function
can handle most situations.
glm.out = glm(infected ~ proc + mem, data=test, family=binomial(logit))
summary(glm.out)
Call:
glm(formula = infected ~ proc + mem, family = binomial(logit),
data = test)
Deviance Residuals:
Min 1Q Median 3Q Max
-1.51110 -0.17718 -0.08015 -0.01132 2.28073
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -2.1905 0.6943 -3.155 0.00160 **
proc 2.1378 0.7192 2.972 0.00295 **
mem 1.6530 0.5682 2.909 0.00362 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 68.275 on 81 degrees of freedom
Residual deviance: 25.348 on 79 degrees of freedom
AIC: 31.348
Number of Fisher Scoring iterations: 8
modelog <- predict.glm(glm.out, test, type="response")
gg <- ggplot(data.frame(x=modelog, y=ifelse(test$infected>0.5, "Yes", "No")), aes(x, y)) +
geom_point(size=3, fill="steelblue", color="black", shape=4) +
ylab("Known Infected Host") +
xlab("Estimated Probability of Infected Host") + theme_bw()
print(gg)

Creating a diagram with same data with multiple differenct k-values
kmeans() function will perform k-means clustering.
The first diagram is k-means with 3 clusters.
The second diagram is k-means with 4 clusters.
The third diagram is k-means with 5 clusters.
The fourth diagram is k-means with 6 clusters.
set.seed(1) # repeatable
x <- c(rnorm(200), rnorm(400)+2, rnorm(400)-2)
y <- c(rnorm(200), rnorm(200)+2, rnorm(200)-2, rnorm(200)+2, rnorm(200)-2)
randata <- data.frame(x=x, y=y)
out <- list()
for(i in c(3,4,5,6)) {
km <- kmeans(randata, i)
centers <- data.frame(x=km$centers[ ,1], y=km$centers[ ,2], cluster=1)
randata$cluster <- factor(km$cluster)
gg <- ggplot(randata, aes(x, y, color=cluster)) + geom_point(size=2)
gg <- gg + geom_point(data=centers, aes(x, y), shape=8, color="black", size=4)
gg <- gg + scale_x_continuous(expand=c(0,0.1))
gg <- gg + scale_y_continuous(expand=c(0,0.1))
gg <- gg + ggtitle(paste("k-means with", i, "clusters"))
gg <- gg + theme(panel.grid = element_blank(),
panel.background = element_rect(colour = "black", fill=NA),
axis.text = element_blank(),
axis.title = element_blank(),
legend.position = "none",
axis.ticks = element_blank())
out[[i-2]] <- gg
}
print(out[[1]])

print(out[[2]])

print(out[[3]])

print(out[[4]])

LS0tDQp0aXRsZTogIk1hY2hpbmVfTGVhcm5pbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KVGhlc2UgcGFja2FnZXMgYXJlIG5lZWRlZCBmb3IgdGhpcyBhc3NpZ25tZW50Og0KDQpgYGB7cn0NCnBrZyA8LSBjKCJnZ3Bsb3QyIiwgIlJDb2xvckJyZXdlciIpDQpuZXcucGtnIDwtIHBrZ1shKHBrZyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKV0NCmlmIChsZW5ndGgobmV3LnBrZykpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnKSAgDQp9DQpgYGANCg0KDQpMZXQgdXMgZmlyc3QgbWFrZSBzdXJlIHdlIGFyZSB3b3JraW5nIGluIGEgZGlyZWN0b3J5IHdlIGZlZWwgY29tZm9ydGFibGUgd2l0aC4gU28gSSB3aWxsIHN0YXJ0IGJ5IGNoYW5naW5nIG15IHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBvbmUgbGlzdGVkIGJlbG93DQoNCg0KTGV0IHVzIG5vdyByZWFkIHRoZSBmaWxlIGFuZCBzdGFydCBleHBsb3JpbmcgdGhlIGRhdGFzZXQgKm1lbXByb2MqLg0KDQpgYGB7cn0NCm15ZGF0YTE8LSByZWFkLmNzdigiZGF0YS9tZW1wcm9jLmNzdiIsIGhlYWRlcj1UKQ0Kc3VtbWFyeShteWRhdGExKQ0KYGBgDQpgYGB7cn0NCm15ZGF0YTENCmBgYA0KDQpJbiBvcmRlciB0byBleHBsb3JlIHRoaXMgZGF0YXNldCBtb3JlIGluIGRldGFpbCwgbGV0IHVzIGNyZWF0ZSBhIHBsb3QgdG8gY29tcGFyZSB0aGUgcHJvY2Vzc29yIGFuZCBtZW1vcnkgdXNhZ2UsIGFuZCBkaWZmZXJlbnRpYXRlIGl0IGJhc2VkIG9uIHRoZSBtYWx3YXJlIHN0YXRlLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdnIDwtIGdncGxvdChteWRhdGExLCBhZXMocHJvYywgbWVtLCBjb2xvcj1zdGF0ZSkpDQpnZyA8LSBnZyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikNCmdnIDwtIGdnICsgZ2VvbV9wb2ludChzaXplPTMpICsgdGhlbWVfYncoKQ0KcHJpbnQoZ2cpDQpgYGANCg0KDQpJIHN1Z2dlc3QgeW91IHRvIGNyZWF0ZSBub3cgYSBuZXcgZ3JhcGggYnV0IHRoaXMgdGltZSBpbmNsdWRpbmcgdGhlIHRpdGxlIGluIHRoZSBjaGFydC4gVGhlIHRpdGxlIHdvdWxkIGJlICJNZW1vcnkgdnMgUHJvY2Vzc29yIFVzYWdlIGFzIGZ1bmN0aW9uIG9mIHRoZSBNYWxhd2FyZSBzdGF0ZSIuDQojIEFkZGVkIGEgdGl0bGUgYnkgdXNpbmcgKyBnZ3RpdGxlKCJNZW1vcnkgdnMgUHJvY2Vzc29yIFVzYWdlIGFzIGZ1bmN0aW9uIG9mIHRoZSBNYWxhd2FyZSBzdGF0ZSIpKSB0byB0aGUgcHJpbnQ6DQojIHByaW50KGdnICsgZ2d0aXRsZSgiTWVtb3J5IHZzIFByb2Nlc3NvciBVc2FnZSBhcyBmdW5jdGlvbiBvZiB0aGUgTWFsYXdhcmUgc3RhdGUiKSkNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2cgPC0gZ2dwbG90KG15ZGF0YTEsIGFlcyhwcm9jLCBtZW0sIGNvbG9yPXN0YXRlKSkNCmdnIDwtIGdnICsgc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGU9IlNldDIiKQ0KZ2cgPC0gZ2cgKyBnZW9tX3BvaW50KHNpemU9MykgKyB0aGVtZV9idygpDQpwcmludChnZyArIGdndGl0bGUoIk1lbW9yeSB2cyBQcm9jZXNzb3IgVXNhZ2UgYXMgZnVuY3Rpb24gb2YgdGhlIE1hbGF3YXJlIHN0YXRlIikpDQpgYGANCg0KIyBHb2luZyB0byB0YWtlIDEvMyBvZiB0aGUgZGF0YSBmb3IgdGVzdCBkYXRhIGFuZCB0aGUgdHJhaW4gdGhlIGFsZ29yaXRobQ0KIyBvbiB0aGUgcmVtYWluaW5nIDIvMy4NCmBgYHtyfQ0Kc2V0LnNlZWQoMTQ5MikNCiMgY291bnQgaG93IG1hbnkgaW4gdGhlIG92ZXJhbGwgc2FtcGxlDQpuIDwtIG5yb3cobXlkYXRhMSkNCiMgc2V0IHRoZSB0ZXN0LnNpemUgdG8gYmUgMS8zcmQNCnRlc3Quc2l6ZSA8LSBhcy5pbnRlZ2VyKG4vMykNCiMgcmFuZG9tbHkgc2FtcGxlIHRoZSByb3dzIGZvciB0ZXN0IHNldA0KdGVzdHNldCA8LSBzYW1wbGUobiwgdGVzdC5zaXplKQ0KIyBub3cgc3BsaXQgdGhlIGRhdGEgaW50byB0ZXN0IGFuZCB0cmFpbg0KdGVzdCA8LSBteWRhdGExW3Rlc3RzZXQsIF0NCnRyYWluIDwtIG15ZGF0YTFbLXRlc3RzZXQsIF0NCmBgYA0KDQojIFNwbGl0dGluZyB0aGUgZGF0YSBpbnRvIHRlc3QgYW5kIHRyYWluDQoNCmBgYHtyfQ0KIyBwdWxsIG91dCBwcm9jIGFuZCBtZW0gY29sdW1ucyBmb3IgaW5mZWN0ZWQgdGhlbiBub3JtYWwNCiMgdGhlbiB1c2UgY29sTWVhbnMoKSB0byBtZWFucyBvZiB0aGUgY29sdW1ucw0KaW5mIDwtIGNvbE1lYW5zKHRyYWluW3RyYWluJHN0YXRlPT0iSW5mZWN0ZWQiLCBjKCJwcm9jIiwgIm1lbSIpXSkNCm5ybSA8LSBjb2xNZWFucyh0cmFpblt0cmFpbiRzdGF0ZT09Ik5vcm1hbCIsIGMoInByb2MiLCAibWVtIildKQ0KYGBgDQoNCg0KIyBEaXNwbGF5IHRoZSByZXN1bHRzIG9mIHRoZSBvbiB0aGUgdHdvIGNvbHVtbnMgYW5kIHJldHVybiB2ZWN0b3Igd2l0aA0KIyB0d28gZWxlbWVudHMuDQoNCmBgYHtyfQ0KcHJpbnQoaW5mKQ0KcHJpbnQobnJtKQ0KYGBgDQoNCiMgUHJlZGljdGluZyBtYWx3YXJlIHByZWRpY3QubWFsd2FyZSB0YWtlIGluIGEgc2luZ2xlIG5hbWVkIHZlY3RvciBjYWxsZWQNCiMgZGF0YSwgZXh0cmFjdCBvdXQgdGhlIHByb2MgYW5kIG1lbSB2YWx1ZXMsIHRoZW4gY2FsY3VsYXRlIGhvdyBmYXIgdGhvc2UgYXJlIGZyb20gdGhlIG1lYW5zDQojIHRhdCB3YXMgZ2VuZXJhdGVkIGR1cmluZyB0aGUgdHJhaW5pbmcuIENhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYnkgdXNpbmcgdGhlIA0KIyBQeXRoYWdvcmVhbiB0aGVvcmVtIGFeMiArIGJeMiA9IGNeMi4gT25jZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0cmFpbmVkIHByb2MgbWVhbiANCiMgYW5kIHRlc3QgcHJvYyBtZWFuIGFuZCB0aGUgdHJhaW5lZCBtZW0gbWVhbiBhbmQgdGhlIHRlc3QgbWVtIG1lYW4gaXMgY2FsY3VsYXRlZCB3ZSBqdXN0Jw0KIyB0aGVuIGp1c3QgY29tcGFyZSB0aGVtLg0KDQoNCmBgYHtyfQ0KcHJlZGljdC5tYWx3YXJlIDwtIGZ1bmN0aW9uKGRhdGEpIHsNCiAgIyBnZXQgJ3Byb2MnIGFuZCAnbWVtJyBhcyBudW1lcmljIHZhbHVlcw0KICBwcm9jIDwtIGFzLm51bWVyaWMoZGF0YVtbJ3Byb2MnXV0pDQogIG1lbSA8LSBhcy5udW1lcmljKGRhdGFbWydtZW0nXV0pDQogICMgc2V0IHVwIGluZmVjdGVkIGNvbXBhcmlzb24NCiAgaW5mLmEgPC0gaW5mWydwcm9jJ10gLSBwcm9jDQogIGluZi5iIDwtIGluZlsnbWVtJ10gLSBtZW0NCiAgIyBweXRoYWdvcmVhbiBkaXN0YW5jZSBjID0gc3FydChhXjIgKyBiXjIpDQogIGluZi5kaXN0IDwtIHNxcnQoaW5mLmFeMiArIGluZi5iXjIpDQogICMgcmVwZWF0IGZvciBub3JtYWwgc3lzdGVtcw0KICBucm0uYSA8LSBucm1bJ3Byb2MnXSAtIHByb2MNCiAgbnJtLmIgPC0gbnJtWydtZW0nXSAtIG1lbQ0KICBucm0uZGlzdCA8LSBzcXJ0KG5ybS5hXjIgKyBucm0uYl4yKQ0KICAjIGFzc2lnbiBhIGxhYmVsIG9mIHRoZSBjbG9zZXN0IChzbWFsbGVzdCkNCiAgaWZlbHNlKGluZi5kaXN0PG5ybS5kaXN0LCJJbmZlY3RlZCIsICJOb3JtYWwiKQ0KfQ0KIyBjb3VsZCB0ZXN0IHdpdGggdGhlc2UgaWYgeW91IHVuY29tbWVudCB0aGVtDQojIHByZWRpY3QubWFsd2FyZShpbmZbJ3Byb2MnXSwgaW5mWydtZW0nXSkNCiMgZXhwZWN0ICJJbmZlY3RlZCIgDQojIHByZWRpY3QubWFsd2FyZShucm1bJ3Byb2MnXSwgbnJtWydtZW0nXSkNCiMgZXhwZWN0ICJOb3JtYWwiDQpgYGANCg0KIyByZXF1aXJlcyB0aGUgdmFsdWVzIGZyb20gdGVzdCB0ZXN0IDwtIG1lbXByb2NbdGVzdHNldCwgXSBhbmQgcHJlZGljdC5tYWx3YXJlIHZhbHVlDQojIGNhbGN1bGF0ZWQgZWFybGllci4NCmBgYHtyfQ0KcHJlZGljdGlvbiA8LSBhcHBseSh0ZXN0LCAxLCBwcmVkaWN0Lm1hbHdhcmUpDQpgYGANCg0KIyBOb3cgaGF2ZSBhIHNldCBvZiBwcmVkaWN0aW9ucyBhbmQgdGhlIGFiaWxpdHkgdG8gY29tcGFyZQ0KIyB0aGVtIHRvIHRoZSByZWFsIHZhbHVlcyB0byBkZXRlcm1pbmUgaG93IHdlbGwgaXQgZGlkLCBsb29rIGF0DQojIHRoZSBwcm9wb3J0aW9uIG9mIGNvcnJlY3RseSBwcmVkaWN0ZWQgcmVzdWx0cyBvbiB0aGUgdGVzdCBkYXRhLiANCiMgVGhlbiBjYWxjdWxhdGUgdGhhdCBieSB0YWtpbmcgdGhlIG51bWJlciBvZiBjb3JyZWN0IHByZWRpY3Rpb25zIA0KIyAod2hlcmUgdGhlIHJlYWwgdGVzdCRzdGF0ZSBhbmQgdGhlIHByZWRpY3RlZCBwcmVkaWN0aW9uIG1hdGNoKSBhbmQgdGhlbg0KIyBkaXZpZGluZyB0aGF0IGJ5IHRoZSB0b3RhbCBudW1iZXIgb2YgcHJlZGljdGlvbnMuDQoNCiMgVGhpcyBzaW1wbGUgYWxnb3JpdGhnbSBwcmVkaWN0ZWQgOTAlIG9mIHRoZSB2YWx1ZXMgY29ycmVjdGx5Lg0KDQpgYGB7cn0NCnN1bSh0ZXN0JHN0YXRlPT1wcmVkaWN0aW9uKS9ucm93KHRlc3QpDQpgYGANCg0KDQoNCmBgYHtyfQ0KIyBGaWd1cmUgOS0yICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc2xvcGUgPC0gLTEqKDEvKChpbmZbJ21lbSddLW5ybVsnbWVtJ10pLyhpbmZbJ3Byb2MnXS1ucm1bJ3Byb2MnXSkpKQ0KaW50ZXJjZXB0IDwtIG1lYW4oYyhpbmZbJ21lbSddLCBucm1bJ21lbSddKSkgLSAoc2xvcGUqbWVhbihjKGluZlsncHJvYyddLCBucm1bJ3Byb2MnXSkpKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCnJlc3VsdCA8LSBjYmluZCh0ZXN0LCBwcmVkaWN0PXByZWRpY3Rpb24pDQpyZXN1bHQkQWNjdXJhdGUgPC0gaWZlbHNlKHJlc3VsdCRzdGF0ZT09cmVzdWx0JHByZWRpY3QsICJZZXMiLCAiTm8iKQ0KcmVzdWx0JEFjY3VyYXRlIDwtIGZhY3RvcihyZXN1bHQkQWNjdXJhdGUsIGxldmVscz1jKCJZZXMiLCAiTm8iKSwgb3JkZXJlZD1UKQ0KYGBgDQoNClRoaXMgY2xhc3NpZmllciBjcmVhdGVzIGEgbGluZSBoYWxmd2F5IGJldHdlZW4gdGhlIHR3byBtZWFucyBhbmQgcGVycGVuZGljdWxhciB0byBhbiBpbnRlcnNlY3RpbmcgbGluZS4NCkFueXRoaW5nIGFib3ZlIHRoZSBsaW5lIGlzIHByZWRpY3RlZCBhcyBpbmZlY3RlZDsgYW55dGhpbmcgYmVsb3cgaXMgcHJlZGljdGVkIHRvIGJlIG5vcm1hbC4gDQoNClRoZSBtaXNjbGFzc2lmaWVkIHZhbHVlcyBhcmUgY2xlYXJseSBtYXJrZWQuIFRoZXJlIGFyZSBub3JtYWwgc3lzdGVtIGFib3ZlIHRoZQ0KbGluZSB0aGF0IGFyZSBtaXNsYWJlbGVkIGFzIHdlbGwgYXMgdGhlIGluZmVjdGVkIHN5c3RlbXMgYmVsb3cgdGhlIGxpbmUuDQoNCmBgYHtyfQ0KZ2cgPC0gZ2dwbG90KHJlc3VsdCwgYWVzKHByb2MsIG1lbSwgY29sb3I9c3RhdGUsIHNpemU9QWNjdXJhdGUsIHNoYXBlPUFjY3VyYXRlKSkNCmdnIDwtIGdnICsgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcz1jKDE2LCA4KSkNCmdnIDwtIGdnICsgc2NhbGVfc2l6ZV9tYW51YWwodmFsdWVzPWMoMywgNikpDQpnZyA8LSBnZyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikNCmdnIDwtIGdnICsgZ2VvbV9wb2ludCgpICsgdGhlbWVfYncoKQ0KZ2cgPC0gZ2cgKyBnZW9tX2FibGluZShpbnRlcmNlcHQgPSBpbnRlcmNlcHQsIHNsb3BlID0gc2xvcGUsIGNvbG9yPSJncmF5ODAiKQ0KcHJpbnQoZ2cpDQpgYGANCg0KIyBIZXJlIHdlIGdvaW5nIHRvIHBsb3QgbGluZWFyIHJlZ3Jlc3Npb24gb24gbm9uLWxpbmVhciBkYXRhLCB0aGlzIGhhcHBlbnMgYmVjYXVzZQ0KIyBwYXJ0IG9mIHRoZSBsaW5lYXIgcmVncmVzc2lvbiBpcyBhIHJlZmVyZW5jZSB0byB0aGUgbGluZWFyIGNvZWZmaWNpZW50cyBlc3RpbWF0ZWQNCiMgbm90IHRoZSBkYXRhLg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp4IDwtIHJ1bmlmKDIwMCwgbWluPS0xMCwgbWF4PTEwKQ0KeSA8LSAxLjM3NyooeF4zKSArIDAuOTIqKHheMikgKyAuMyp4ICsgcm5vcm0oMjAwLCBzZD0yNTApICsgMTU3Mg0KeCA8LSB4ICsgMTANCnNtb290aCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZSh4LHkpLCBhZXMoeCwgeSkpICsgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAzKSwgc2l6ZSA9IDEsIHNlPUYpICsgDQogIHRoZW1lX2J3KCkNCnByaW50KHNtb290aCkNCmBgYA0KDQoNCg0KYGBge3J9DQptZW1wcm9jIDwtIHJlYWQuY3N2KCJkYXRhL21lbXByb2MuY3N2IiwgaGVhZGVyPVQpDQptZW1wcm9jJGluZmVjdGVkIDwtIGlmZWxzZShtZW1wcm9jJHN0YXRlPT0iSW5mZWN0ZWQiLCAxLCAwKQ0Kc2V0LnNlZWQoMTQ5MikNCm4gPC0gbnJvdyhtZW1wcm9jKQ0KdGVzdC5zaXplIDwtIGFzLmludGVnZXIobi8zKQ0KdGVzdHNldCA8LSBzYW1wbGUobiwgdGVzdC5zaXplKQ0KdGVzdCA8LSBtZW1wcm9jW3Rlc3RzZXQsIF0NCnRyYWluIDwtIG1lbXByb2NbLXRlc3RzZXQsIF0NCmBgYA0KDQoNCiMgVGhlIHBsb3RzIGFyZSBzaG93aW5nIGxpbmVhciByZWdyZXNzaW9uIG9uIGluZmVjdGlvbiB0ZXN0IGRhdGENCiMgT3V0cHV0IG9uIHRoZSB4LWF4aXMgaXMgYW4gZXN0aW1hdGVkIHByb2JhYmlsaXR5IG9mIGEgaG9zdCBiZWluZyBpbmZlY3RlZA0KIyBiYXNlZCBvbiB0aGUgaW5wdXQgdmFyaWFibGVzLiBUaGlzIHBsb3R0ZWQgYWdhaW5zdCB0aGUga25vd24gdmFsdWVzIGluIHRoZSB0ZXN0DQojIGRhdGEgb24gdGhlIHktYXhpcyhyZWFsIGxpZmUpLiANCiMgZ2xtKCkgZnVuY3Rpb24gaXMgb25lIG9mIHNlcnZlcmFsIGFwcHJvYWNoZXMgdG8gbG9naXN0aWMgcmVncmVzc2lvbi4gVGhpcyBmdW5jdGlvbg0KIyBjYW4gaGFuZGxlIG1vc3Qgc2l0dWF0aW9ucy4NCmBgYHtyfQ0KZ2xtLm91dCA9IGdsbShpbmZlY3RlZCB+IHByb2MgKyBtZW0sIGRhdGE9dGVzdCwgZmFtaWx5PWJpbm9taWFsKGxvZ2l0KSkNCnN1bW1hcnkoZ2xtLm91dCkNCm1vZGVsb2cgPC0gcHJlZGljdC5nbG0oZ2xtLm91dCwgdGVzdCwgdHlwZT0icmVzcG9uc2UiKQ0KZ2cgPC0gZ2dwbG90KGRhdGEuZnJhbWUoeD1tb2RlbG9nLCB5PWlmZWxzZSh0ZXN0JGluZmVjdGVkPjAuNSwgIlllcyIsICJObyIpKSwgYWVzKHgsIHkpKSArDQogIGdlb21fcG9pbnQoc2l6ZT0zLCBmaWxsPSJzdGVlbGJsdWUiLCBjb2xvcj0iYmxhY2siLCBzaGFwZT00KSArIA0KICB5bGFiKCJLbm93biBJbmZlY3RlZCBIb3N0IikgKw0KICB4bGFiKCJFc3RpbWF0ZWQgUHJvYmFiaWxpdHkgb2YgSW5mZWN0ZWQgSG9zdCIpICsgdGhlbWVfYncoKQ0KcHJpbnQoZ2cpDQpgYGANCg0KIyBDcmVhdGluZyBhIGRpYWdyYW0gd2l0aCBzYW1lIGRhdGEgd2l0aCBtdWx0aXBsZSBkaWZmZXJlbmN0IGstdmFsdWVzDQojIGttZWFucygpIGZ1bmN0aW9uIHdpbGwgcGVyZm9ybSBrLW1lYW5zIGNsdXN0ZXJpbmcuDQojIFRoZSBmaXJzdCBkaWFncmFtIGlzIGstbWVhbnMgd2l0aCAzIGNsdXN0ZXJzLg0KIyBUaGUgc2Vjb25kIGRpYWdyYW0gaXMgay1tZWFucyB3aXRoIDQgY2x1c3RlcnMuDQojIFRoZSB0aGlyZCBkaWFncmFtIGlzIGstbWVhbnMgd2l0aCA1IGNsdXN0ZXJzLg0KIyBUaGUgZm91cnRoIGRpYWdyYW0gaXMgay1tZWFucyB3aXRoIDYgY2x1c3RlcnMuDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkgIyByZXBlYXRhYmxlDQp4IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oNDAwKSsyLCBybm9ybSg0MDApLTIpDQp5IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oMjAwKSsyLCBybm9ybSgyMDApLTIsIHJub3JtKDIwMCkrMiwgcm5vcm0oMjAwKS0yKQ0KcmFuZGF0YSA8LSBkYXRhLmZyYW1lKHg9eCwgeT15KQ0Kb3V0IDwtIGxpc3QoKQ0KZm9yKGkgaW4gYygzLDQsNSw2KSkgew0KICBrbSA8LSBrbWVhbnMocmFuZGF0YSwgaSkNCiAgY2VudGVycyA8LSBkYXRhLmZyYW1lKHg9a20kY2VudGVyc1sgLDFdLCB5PWttJGNlbnRlcnNbICwyXSwgY2x1c3Rlcj0xKQ0KICByYW5kYXRhJGNsdXN0ZXIgPC0gZmFjdG9yKGttJGNsdXN0ZXIpDQogIGdnIDwtIGdncGxvdChyYW5kYXRhLCBhZXMoeCwgeSwgY29sb3I9Y2x1c3RlcikpICsgZ2VvbV9wb2ludChzaXplPTIpDQogIGdnIDwtIGdnICsgZ2VvbV9wb2ludChkYXRhPWNlbnRlcnMsIGFlcyh4LCB5KSwgc2hhcGU9OCwgY29sb3I9ImJsYWNrIiwgc2l6ZT00KQ0KICBnZyA8LSBnZyArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDAuMSkpDQogIGdnIDwtIGdnICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMC4xKSkNCiAgZ2cgPC0gZ2cgKyBnZ3RpdGxlKHBhc3RlKCJrLW1lYW5zIHdpdGgiLCBpLCAiY2x1c3RlcnMiKSkNCiAgZ2cgPC0gZ2cgKyB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSksDQogICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQogIG91dFtbaS0yXV0gPC0gZ2cNCn0NCnByaW50KG91dFtbMV1dKQ0KcHJpbnQob3V0W1syXV0pDQpwcmludChvdXRbWzNdXSkNCnByaW50KG91dFtbNF1dKQ0KYGBgDQoNCg0K