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
# Lets check our working directory
getwd()
[1] "C:/Users/antho/OneDrive/Documents/School/4.DataSecurity&Governance"
Let us now read the file and start exploring the dataset
memproc.
mydata1<- read.csv("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
We can see we were succesfull in uploading the memproc.csv data into
a data fram we named “mydata1” which has 4 fields. Lets get more info on
the dataset by using the head function and inspecting the colomns and
top 6 rows.
head(mydata1)
We can see that There are two fields (“host” & “state”) that look
like characters and two fields (“proc” & “mem”) that look like
numeric data under the dbl data type
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.
# you will see that we use the ggplot function to create graph comparing the proc(x) and mem(y) fields and set the details/legend of the "state" field by color.
library(ggplot2)
Warning: package ‘ggplot2’ was built under R version 4.2.1
gg <- ggplot(mydata1, aes(proc, mem, color=state))
gg <- gg + scale_color_brewer(palette="Set2") # we can change the color here
gg <- gg + geom_point(size=3) + theme_bw() # we can change the size of the dots here
print(gg)

I sugest 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”.
# We print the original plot (gg), adding the string title we want by inserting it using the ggplot2 function ggtitle()
print(gg + ggtitle("Memory vs Processor Usage as function of the Malware state"))

We print the original plot (gg), adding the string title we want by
inserting it using the ggplot2 function ggtitle()
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, ]
# 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")])
We create two variables storing the mean value for all the “infected”
and “normal” states from the train data that we split earlier.
print(inf)
proc mem
0.9354513 1.0868010
print(nrm)
proc mem
-0.7907962 -0.9352974
By using the colMens function we are able to see the mean of the
“infected” and “normal” states for the Processor(proc) and memory (mem)
fields in the train data.
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"
We create a function called predict.malware to do exactly that, by
finding the shortest distance from all point in the train data. Very
interesting algorithms we use above to find the pythagorean distance
forbetween the processor and memory values for the infected and normal
states.
prediction <- apply(test, 1, predict.malware)
sum(test$state==prediction)/nrow(test)
[1] 0.902439
Above is our predictions accuracy which is considerably good at
90%
Lets make a slope and intercept so we can add an diagnal line on the
chart so we can better visualize the divide of states.
# 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'])))
Lets create a result variable that will display weather or
predictions were accurate or not
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)
Now recreate the previous graph but add the detail/legend of accuracy
by shape. We also use the geom_abline() function to create the diagnal
line from the slope and intercept we identified and created above.
# notice here we set the detail/legend at the end of this next line where we create the chart.
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)

we can see that we have less false negatives than we do false
positives which means we are predicting more infected situations
accurately than we are non infected meaning those with a threat are
being identified. Only issue is there are six times more people that are
being told they are infected when they are not.
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)

Here we create a linear regression model based of random data x, y
values we created with a distribution like that of sigmoid function.
Very interesting notice how we use the geom_smooth() function to define
and deploy the linear regression model in nearly one line. This is
useful for predicting quantitative values.
memproc <- read.csv("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, ]
Since this data is being used more so to classify a qualitative field
(“state”) which has only values it we would benifit us to convert these
two string values into binary digits so that they are easier to use as a
target value for logistic regression.
You will see below how we target only the infected state for our
chart when creating this logistic regression model. Logistic regrssion
are achieved using the “generalized linear models” glm() function which
i read can also do poisson regression and survival analysis.
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)

Notice this chart is perfect for showing the probability of a
classification, kind of like a confusion matrix this is identify the
probability of each of a binary classifications two values. Notice the
host that have a higher probability of infection have more grouping on
the yes line of the y axis. Also Notice that the majority of records
fall near the 0 probability and on the “no” known infected line.
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]])

Here we create a random random scatter plot to demonstrate k-means
clustering. The K stands for the number of clusters you want to create.
Notice that we made 4 different charts with a cluster of 3 in the first
and ascending to 6 clusters in the last. The clustering is at random and
needs to be specified by you which is why it hierarchical clustering can
help in eliminating the guessing as to how many clusters suits you,
maybe for dimension reduction.
LS0tDQp0aXRsZTogIk1hY2hpbmVfTGVhcm5pbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KVGhlc2UgcGFja2FnZXMgYXJlIG5lZWRlZCBmb3IgdGhpcyBhc3NpZ25tZW50Og0KDQpgYGB7cn0NCnBrZyA8LSBjKCJnZ3Bsb3QyIiwgIlJDb2xvckJyZXdlciIpDQpuZXcucGtnIDwtIHBrZ1shKHBrZyAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKV0NCmlmIChsZW5ndGgobmV3LnBrZykpIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGtnKSAgDQp9DQpgYGANCg0KDQpMZXQgdXMgZmlyc3QgbWFrZSBzdXJlIHdlIGFyZSB3b3JraW5nIGluIGEgZGlyZWN0b3J5IHdlIGZlZWwgY29tZm9ydGFibGUgd2l0aC4gU28gSSB3aWxsIHN0YXJ0IGJ5IGNoYW5naW5nIG15IHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBvbmUgbGlzdGVkIGJlbG93DQoNCmBgYHtyfQ0KIyBMZXRzIGNoZWNrIG91ciB3b3JraW5nIGRpcmVjdG9yeQ0KZ2V0d2QoKQ0KDQpgYGANCg0KTGV0IHVzIG5vdyByZWFkIHRoZSBmaWxlIGFuZCBzdGFydCBleHBsb3JpbmcgdGhlIGRhdGFzZXQgKm1lbXByb2MqLg0KDQpgYGB7cn0NCm15ZGF0YTE8LSByZWFkLmNzdigibWVtcHJvYy5jc3YiLCBoZWFkZXI9VCkNCnN1bW1hcnkobXlkYXRhMSkNCmBgYA0KDQpXZSBjYW4gc2VlIHdlIHdlcmUgc3VjY2VzZnVsbCBpbiB1cGxvYWRpbmcgdGhlIG1lbXByb2MuY3N2IGRhdGEgaW50byBhIGRhdGEgZnJhbSB3ZSBuYW1lZCAibXlkYXRhMSIgd2hpY2ggaGFzIDQgZmllbGRzLg0KTGV0cyBnZXQgbW9yZSBpbmZvIG9uIHRoZSBkYXRhc2V0IGJ5IHVzaW5nIHRoZSBoZWFkIGZ1bmN0aW9uIGFuZCBpbnNwZWN0aW5nIHRoZSBjb2xvbW5zIGFuZCB0b3AgNiByb3dzLiANCg0KYGBge3J9DQpoZWFkKG15ZGF0YTEpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IFRoZXJlIGFyZSB0d28gZmllbGRzICgiaG9zdCIgJiAic3RhdGUiKSB0aGF0IGxvb2sgbGlrZSBjaGFyYWN0ZXJzIGFuZCB0d28gZmllbGRzICgicHJvYyIgJiAibWVtIikgdGhhdCBsb29rIGxpa2UgbnVtZXJpYyBkYXRhIHVuZGVyIHRoZSBkYmwgZGF0YSB0eXBlDQoNCkluIG9yZGVyIHRvIGV4cGxvcmUgdGhpcyBkYXRhc2V0IG1vcmUgaW4gZGV0YWlsLCBsZXQgdXMgY3JlYXRlIGEgcGxvdCB0byBjb21wYXJlIHRoZSBwcm9jZXNzb3IgYW5kIG1lbW9yeSB1c2FnZSwgYW5kIGRpZmZlcmVudGlhdGUgaXQgYmFzZWQgb24gdGhlIG1hbHdhcmUgc3RhdGUuDQoNCmBgYHtyfQ0KIyB5b3Ugd2lsbCBzZWUgdGhhdCB3ZSB1c2UgdGhlIGdncGxvdCBmdW5jdGlvbiB0byBjcmVhdGUgZ3JhcGggY29tcGFyaW5nIHRoZSBwcm9jKHgpIGFuZCBtZW0oeSkgZmllbGRzIGFuZCBzZXQgdGhlIGRldGFpbHMvbGVnZW5kIG9mIHRoZSAic3RhdGUiIGZpZWxkIGJ5IGNvbG9yLiANCmxpYnJhcnkoZ2dwbG90MikNCmdnIDwtIGdncGxvdChteWRhdGExLCBhZXMocHJvYywgbWVtLCBjb2xvcj1zdGF0ZSkpDQpnZyA8LSBnZyArIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlPSJTZXQyIikgIyB3ZSBjYW4gY2hhbmdlIHRoZSBjb2xvciBoZXJlDQpnZyA8LSBnZyArIGdlb21fcG9pbnQoc2l6ZT0zKSArIHRoZW1lX2J3KCkgIyB3ZSBjYW4gY2hhbmdlIHRoZSBzaXplIG9mIHRoZSBkb3RzIGhlcmUNCnByaW50KGdnKQ0KYGBgDQoNCkkgc3VnZXN0IHlvdSB0byBjcmVhdGUgbm93IGEgbmV3IGdyYXBoIGJ1dCB0aGlzIHRpbWUgaW5jbHVkaW5nIHRoZSB0aXRsZSBpbiB0aGUgY2hhcnQuIFRoZSB0aXRsZSB3b3VsZCBiZSAiTWVtb3J5IHZzIFByb2Nlc3NvciBVc2FnZSBhcyBmdW5jdGlvbiBvZiB0aGUgTWFsYXdhcmUgc3RhdGUiLg0KDQoNCmBgYHtyfQ0KcHJpbnQoZ2cgKyBnZ3RpdGxlKCJNZW1vcnkgdnMgUHJvY2Vzc29yIFVzYWdlIGFzIGZ1bmN0aW9uIG9mIHRoZSBNYWx3YXJlIHN0YXRlIikpDQpgYGANCldlIHByaW50IHRoZSBvcmlnaW5hbCBwbG90IChnZyksIGFkZGluZyB0aGUgc3RyaW5nIHRpdGxlIHdlIHdhbnQgYnkgaW5zZXJ0aW5nIGl0IHVzaW5nIHRoZSBnZ3Bsb3QyIGZ1bmN0aW9uIGdndGl0bGUoKQ0KDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMTQ5MikNCiMgY291bnQgaG93IG1hbnkgaW4gdGhlIG92ZXJhbGwgc2FtcGxlDQpuIDwtIG5yb3cobXlkYXRhMSkNCiMgc2V0IHRoZSB0ZXN0LnNpemUgdG8gYmUgMS8zcmQNCnRlc3Quc2l6ZSA8LSBhcy5pbnRlZ2VyKG4vMykNCiMgcmFuZG9tbHkgc2FtcGxlIHRoZSByb3dzIGZvciB0ZXN0IHNldA0KdGVzdHNldCA8LSBzYW1wbGUobiwgdGVzdC5zaXplKQ0KIyBub3cgc3BsaXQgdGhlIGRhdGEgaW50byB0ZXN0IGFuZCB0cmFpbg0KdGVzdCA8LSBteWRhdGExW3Rlc3RzZXQsIF0NCnRyYWluIDwtIG15ZGF0YTFbLXRlc3RzZXQsIF0NCmBgYA0KDQoNCg0KYGBge3J9DQojIHB1bGwgb3V0IHByb2MgYW5kIG1lbSBjb2x1bW5zIGZvciBpbmZlY3RlZCB0aGVuIG5vcm1hbA0KIyB0aGVuIHVzZSBjb2xNZWFucygpIHRvIG1lYW5zIG9mIHRoZSBjb2x1bW5zDQppbmYgPC0gY29sTWVhbnModHJhaW5bdHJhaW4kc3RhdGU9PSJJbmZlY3RlZCIsIGMoInByb2MiLCAibWVtIildKQ0KbnJtIDwtIGNvbE1lYW5zKHRyYWluW3RyYWluJHN0YXRlPT0iTm9ybWFsIiwgYygicHJvYyIsICJtZW0iKV0pDQpgYGANCg0KDQpXZSBjcmVhdGUgdHdvIHZhcmlhYmxlcyBzdG9yaW5nIHRoZSBtZWFuIHZhbHVlIGZvciBhbGwgdGhlICJpbmZlY3RlZCIgYW5kICJub3JtYWwiIHN0YXRlcyBmcm9tIHRoZSB0cmFpbiBkYXRhIHRoYXQgd2Ugc3BsaXQgZWFybGllci4gDQoNCmBgYHtyfQ0KcHJpbnQoaW5mKQ0KcHJpbnQobnJtKQ0KYGBgDQpCeSB1c2luZyB0aGUgY29sTWVucyBmdW5jdGlvbiB3ZSBhcmUgYWJsZSB0byBzZWUgdGhlIG1lYW4gb2YgdGhlICJpbmZlY3RlZCIgYW5kICJub3JtYWwiIHN0YXRlcyBmb3IgdGhlIFByb2Nlc3Nvcihwcm9jKSBhbmQgbWVtb3J5IChtZW0pIGZpZWxkcyBpbiB0aGUgdHJhaW4gZGF0YS4gDQoNCg0KYGBge3J9DQpwcmVkaWN0Lm1hbHdhcmUgPC0gZnVuY3Rpb24oZGF0YSkgew0KICAjIGdldCAncHJvYycgYW5kICdtZW0nIGFzIG51bWVyaWMgdmFsdWVzDQogIHByb2MgPC0gYXMubnVtZXJpYyhkYXRhW1sncHJvYyddXSkNCiAgbWVtIDwtIGFzLm51bWVyaWMoZGF0YVtbJ21lbSddXSkNCiAgIyBzZXQgdXAgaW5mZWN0ZWQgY29tcGFyaXNvbg0KICBpbmYuYSA8LSBpbmZbJ3Byb2MnXSAtIHByb2MNCiAgaW5mLmIgPC0gaW5mWydtZW0nXSAtIG1lbQ0KICAjIHB5dGhhZ29yZWFuIGRpc3RhbmNlIGMgPSBzcXJ0KGFeMiArIGJeMikNCiAgaW5mLmRpc3QgPC0gc3FydChpbmYuYV4yICsgaW5mLmJeMikNCiAgIyByZXBlYXQgZm9yIG5vcm1hbCBzeXN0ZW1zDQogIG5ybS5hIDwtIG5ybVsncHJvYyddIC0gcHJvYw0KICBucm0uYiA8LSBucm1bJ21lbSddIC0gbWVtDQogIG5ybS5kaXN0IDwtIHNxcnQobnJtLmFeMiArIG5ybS5iXjIpDQogICMgYXNzaWduIGEgbGFiZWwgb2YgdGhlIGNsb3Nlc3QgKHNtYWxsZXN0KQ0KICBpZmVsc2UoaW5mLmRpc3Q8bnJtLmRpc3QsIkluZmVjdGVkIiwgIk5vcm1hbCIpDQp9DQojIGNvdWxkIHRlc3Qgd2l0aCB0aGVzZSBpZiB5b3UgdW5jb21tZW50IHRoZW0NCiMgcHJlZGljdC5tYWx3YXJlKGluZlsncHJvYyddLCBpbmZbJ21lbSddKQ0KIyBleHBlY3QgIkluZmVjdGVkIiANCiMgcHJlZGljdC5tYWx3YXJlKG5ybVsncHJvYyddLCBucm1bJ21lbSddKQ0KIyBleHBlY3QgIk5vcm1hbCINCmBgYA0KDQpXZSBjcmVhdGUgYSBmdW5jdGlvbiBjYWxsZWQgcHJlZGljdC5tYWx3YXJlIHRvIGRvIGV4YWN0bHkgdGhhdCwgYnkgZmluZGluZyB0aGUgc2hvcnRlc3QgZGlzdGFuY2UgZnJvbSBhbGwgcG9pbnQgaW4gdGhlIHRyYWluIGRhdGEuIFZlcnkgaW50ZXJlc3RpbmcgYWxnb3JpdGhtcyB3ZSB1c2UgYWJvdmUgdG8gZmluZCB0aGUgcHl0aGFnb3JlYW4gZGlzdGFuY2UgZm9yYmV0d2VlbiB0aGUgcHJvY2Vzc29yIGFuZCBtZW1vcnkgdmFsdWVzIGZvciB0aGUgaW5mZWN0ZWQgYW5kIG5vcm1hbCBzdGF0ZXMuIA0KDQpgYGB7cn0NCnByZWRpY3Rpb24gPC0gYXBwbHkodGVzdCwgMSwgcHJlZGljdC5tYWx3YXJlKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtKHRlc3Qkc3RhdGU9PXByZWRpY3Rpb24pL25yb3codGVzdCkNCmBgYA0KQWJvdmUgaXMgb3VyIHByZWRpY3Rpb25zIGFjY3VyYWN5IHdoaWNoIGlzIGNvbnNpZGVyYWJseSBnb29kIGF0IDkwJQ0KDQpMZXRzIG1ha2UgYSBzbG9wZSBhbmQgaW50ZXJjZXB0IHNvIHdlIGNhbiBhZGQgYW4gZGlhZ25hbCBsaW5lIG9uIHRoZSBjaGFydCBzbyB3ZSBjYW4gYmV0dGVyIHZpc3VhbGl6ZSB0aGUgZGl2aWRlIG9mIHN0YXRlcy4gDQoNCmBgYHtyfQ0KIyBGaWd1cmUgOS0yICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc2xvcGUgPC0gLTEqKDEvKChpbmZbJ21lbSddLW5ybVsnbWVtJ10pLyhpbmZbJ3Byb2MnXS1ucm1bJ3Byb2MnXSkpKQ0KaW50ZXJjZXB0IDwtIG1lYW4oYyhpbmZbJ21lbSddLCBucm1bJ21lbSddKSkgLSAoc2xvcGUqbWVhbihjKGluZlsncHJvYyddLCBucm1bJ3Byb2MnXSkpKQ0KYGBgDQoNCkxldHMgY3JlYXRlIGEgcmVzdWx0IHZhcmlhYmxlIHRoYXQgd2lsbCBkaXNwbGF5IHdlYXRoZXIgb3IgcHJlZGljdGlvbnMgd2VyZSBhY2N1cmF0ZSBvciBub3QNCg0KYGBge3J9DQpyZXN1bHQgPC0gY2JpbmQodGVzdCwgcHJlZGljdD1wcmVkaWN0aW9uKQ0KcmVzdWx0JEFjY3VyYXRlIDwtIGlmZWxzZShyZXN1bHQkc3RhdGU9PXJlc3VsdCRwcmVkaWN0LCAiWWVzIiwgIk5vIikNCnJlc3VsdCRBY2N1cmF0ZSA8LSBmYWN0b3IocmVzdWx0JEFjY3VyYXRlLCBsZXZlbHM9YygiWWVzIiwgIk5vIiksIG9yZGVyZWQ9VCkNCmBgYA0KDQpOb3cgcmVjcmVhdGUgdGhlIHByZXZpb3VzIGdyYXBoIGJ1dCBhZGQgdGhlIGRldGFpbC9sZWdlbmQgb2YgYWNjdXJhY3kgYnkgc2hhcGUuIFdlIGFsc28gdXNlIHRoZSBnZW9tX2FibGluZSgpIGZ1bmN0aW9uIHRvIGNyZWF0ZSB0aGUgZGlhZ25hbCBsaW5lIGZyb20gdGhlIHNsb3BlIGFuZCBpbnRlcmNlcHQgd2UgaWRlbnRpZmllZCBhbmQgY3JlYXRlZCBhYm92ZS4gDQoNCg0KYGBge3J9DQojIG5vdGljZSBoZXJlIHdlIHNldCB0aGUgZGV0YWlsL2xlZ2VuZCBhdCB0aGUgZW5kIG9mIHRoaXMgbmV4dCBsaW5lIHdoZXJlIHdlIGNyZWF0ZSB0aGUgY2hhcnQuIA0KZ2cgPC0gZ2dwbG90KHJlc3VsdCwgYWVzKHByb2MsIG1lbSwgY29sb3I9c3RhdGUsIHNpemU9QWNjdXJhdGUsIHNoYXBlPUFjY3VyYXRlKSkgDQpnZyA8LSBnZyArIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXM9YygxNiwgOCkpDQpnZyA8LSBnZyArIHNjYWxlX3NpemVfbWFudWFsKHZhbHVlcz1jKDMsIDYpKQ0KZ2cgPC0gZ2cgKyBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZT0iU2V0MiIpDQpnZyA8LSBnZyArIGdlb21fcG9pbnQoKSArIHRoZW1lX2J3KCkNCmdnIDwtIGdnICsgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gaW50ZXJjZXB0LCBzbG9wZSA9IHNsb3BlLCBjb2xvcj0iZ3JheTgwIikNCnByaW50KGdnKQ0KYGBgDQoNCndlIGNhbiBzZWUgdGhhdCB3ZSBoYXZlIGxlc3MgZmFsc2UgbmVnYXRpdmVzIHRoYW4gd2UgZG8gZmFsc2UgcG9zaXRpdmVzIHdoaWNoIG1lYW5zIHdlIGFyZSBwcmVkaWN0aW5nIG1vcmUgaW5mZWN0ZWQgc2l0dWF0aW9ucyBhY2N1cmF0ZWx5IHRoYW4gd2UgYXJlIG5vbiBpbmZlY3RlZCBtZWFuaW5nIHRob3NlIHdpdGggYSB0aHJlYXQgYXJlIGJlaW5nIGlkZW50aWZpZWQuIE9ubHkgaXNzdWUgaXMgdGhlcmUgYXJlIHNpeCB0aW1lcyBtb3JlIHBlb3BsZSB0aGF0IGFyZSBiZWluZyB0b2xkIHRoZXkgYXJlIGluZmVjdGVkIHdoZW4gdGhleSBhcmUgbm90LiANCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQp4IDwtIHJ1bmlmKDIwMCwgbWluPS0xMCwgbWF4PTEwKQ0KeSA8LSAxLjM3NyooeF4zKSArIDAuOTIqKHheMikgKyAuMyp4ICsgcm5vcm0oMjAwLCBzZD0yNTApICsgMTU3Mg0KeCA8LSB4ICsgMTANCnNtb290aCA8LSBnZ3Bsb3QoZGF0YS5mcmFtZSh4LHkpLCBhZXMoeCwgeSkpICsgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSB5IH4gcG9seSh4LCAzKSwgc2l6ZSA9IDEsIHNlPUYpICsgDQogIHRoZW1lX2J3KCkNCnByaW50KHNtb290aCkNCmBgYA0KSGVyZSB3ZSBjcmVhdGUgYSBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbCBiYXNlZCBvZiByYW5kb20gZGF0YSB4LCB5IHZhbHVlcyB3ZSBjcmVhdGVkIHdpdGggYSBkaXN0cmlidXRpb24gbGlrZSB0aGF0IG9mIHNpZ21vaWQgZnVuY3Rpb24uIA0KVmVyeSBpbnRlcmVzdGluZyBub3RpY2UgaG93IHdlIHVzZSB0aGUgZ2VvbV9zbW9vdGgoKSBmdW5jdGlvbiB0byBkZWZpbmUgYW5kIGRlcGxveSB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgaW4gbmVhcmx5IG9uZSBsaW5lLiANClRoaXMgaXMgdXNlZnVsIGZvciBwcmVkaWN0aW5nIHF1YW50aXRhdGl2ZSB2YWx1ZXMuICANCg0KYGBge3J9DQptZW1wcm9jIDwtIHJlYWQuY3N2KCJtZW1wcm9jLmNzdiIsIGhlYWRlcj1UKQ0KbWVtcHJvYyRpbmZlY3RlZCA8LSBpZmVsc2UobWVtcHJvYyRzdGF0ZT09IkluZmVjdGVkIiwgMSwgMCkNCnNldC5zZWVkKDE0OTIpDQpuIDwtIG5yb3cobWVtcHJvYykNCnRlc3Quc2l6ZSA8LSBhcy5pbnRlZ2VyKG4vMykNCnRlc3RzZXQgPC0gc2FtcGxlKG4sIHRlc3Quc2l6ZSkNCnRlc3QgPC0gbWVtcHJvY1t0ZXN0c2V0LCBdDQp0cmFpbiA8LSBtZW1wcm9jWy10ZXN0c2V0LCBdDQpgYGANCg0KU2luY2UgdGhpcyBkYXRhIGlzIGJlaW5nIHVzZWQgbW9yZSBzbyB0byBjbGFzc2lmeSBhIHF1YWxpdGF0aXZlIGZpZWxkICgic3RhdGUiKSB3aGljaCBoYXMgb25seSB2YWx1ZXMgaXQgd2Ugd291bGQgYmVuaWZpdCB1cyB0byBjb252ZXJ0IHRoZXNlIHR3byBzdHJpbmcgdmFsdWVzIGludG8gYmluYXJ5IGRpZ2l0cyBzbyB0aGF0IHRoZXkgYXJlIGVhc2llciB0byB1c2UgYXMgYSB0YXJnZXQgdmFsdWUgIGZvciBsb2dpc3RpYyByZWdyZXNzaW9uLiANCg0KWW91IHdpbGwgc2VlIGJlbG93IGhvdyB3ZSB0YXJnZXQgb25seSB0aGUgaW5mZWN0ZWQgc3RhdGUgZm9yIG91ciBjaGFydCB3aGVuIGNyZWF0aW5nIHRoaXMgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbC4NCkxvZ2lzdGljIHJlZ3Jzc2lvbiBhcmUgYWNoaWV2ZWQgdXNpbmcgdGhlICJnZW5lcmFsaXplZCBsaW5lYXIgbW9kZWxzIiBnbG0oKSBmdW5jdGlvbiB3aGljaCBpIHJlYWQgY2FuIGFsc28gZG8gcG9pc3NvbiByZWdyZXNzaW9uIGFuZCBzdXJ2aXZhbCBhbmFseXNpcy4gDQoNCmBgYHtyfQ0KZ2xtLm91dCA9IGdsbShpbmZlY3RlZCB+IHByb2MgKyBtZW0sIGRhdGE9dGVzdCwgZmFtaWx5PWJpbm9taWFsKGxvZ2l0KSkNCnN1bW1hcnkoZ2xtLm91dCkNCm1vZGVsb2cgPC0gcHJlZGljdC5nbG0oZ2xtLm91dCwgdGVzdCwgdHlwZT0icmVzcG9uc2UiKQ0KZ2cgPC0gZ2dwbG90KGRhdGEuZnJhbWUoeD1tb2RlbG9nLCB5PWlmZWxzZSh0ZXN0JGluZmVjdGVkPjAuNSwgIlllcyIsICJObyIpKSwgYWVzKHgsIHkpKSArDQogIGdlb21fcG9pbnQoc2l6ZT0zLCBmaWxsPSJzdGVlbGJsdWUiLCBjb2xvcj0iYmxhY2siLCBzaGFwZT00KSArIA0KICB5bGFiKCJLbm93biBJbmZlY3RlZCBIb3N0IikgKw0KICB4bGFiKCJFc3RpbWF0ZWQgUHJvYmFiaWxpdHkgb2YgSW5mZWN0ZWQgSG9zdCIpICsgdGhlbWVfYncoKQ0KcHJpbnQoZ2cpDQpgYGANCg0KTm90aWNlIHRoaXMgY2hhcnQgaXMgcGVyZmVjdCBmb3Igc2hvd2luZyB0aGUgcHJvYmFiaWxpdHkgb2YgYSBjbGFzc2lmaWNhdGlvbiwga2luZCBvZiBsaWtlIGEgY29uZnVzaW9uIG1hdHJpeCB0aGlzIGlzIGlkZW50aWZ5IHRoZSBwcm9iYWJpbGl0eSBvZiBlYWNoIG9mIGEgYmluYXJ5IGNsYXNzaWZpY2F0aW9ucyB0d28gdmFsdWVzLiANCk5vdGljZSB0aGUgaG9zdCB0aGF0IGhhdmUgYSBoaWdoZXIgcHJvYmFiaWxpdHkgb2YgaW5mZWN0aW9uIGhhdmUgbW9yZSBncm91cGluZyBvbiB0aGUgeWVzIGxpbmUgb2YgdGhlIHkgYXhpcy4gDQpBbHNvIE5vdGljZSB0aGF0IHRoZSBtYWpvcml0eSBvZiByZWNvcmRzIGZhbGwgbmVhciB0aGUgMCBwcm9iYWJpbGl0eSBhbmQgb24gdGhlICJubyIga25vd24gaW5mZWN0ZWQgbGluZS4gDQoNCmBgYHtyfQ0Kc2V0LnNlZWQoMSkgIyByZXBlYXRhYmxlDQp4IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oNDAwKSsyLCBybm9ybSg0MDApLTIpDQp5IDwtIGMocm5vcm0oMjAwKSwgcm5vcm0oMjAwKSsyLCBybm9ybSgyMDApLTIsIHJub3JtKDIwMCkrMiwgcm5vcm0oMjAwKS0yKQ0KcmFuZGF0YSA8LSBkYXRhLmZyYW1lKHg9eCwgeT15KQ0Kb3V0IDwtIGxpc3QoKQ0KZm9yKGkgaW4gYygzLDQsNSw2KSkgew0KICBrbSA8LSBrbWVhbnMocmFuZGF0YSwgaSkNCiAgY2VudGVycyA8LSBkYXRhLmZyYW1lKHg9a20kY2VudGVyc1sgLDFdLCB5PWttJGNlbnRlcnNbICwyXSwgY2x1c3Rlcj0xKQ0KICByYW5kYXRhJGNsdXN0ZXIgPC0gZmFjdG9yKGttJGNsdXN0ZXIpDQogIGdnIDwtIGdncGxvdChyYW5kYXRhLCBhZXMoeCwgeSwgY29sb3I9Y2x1c3RlcikpICsgZ2VvbV9wb2ludChzaXplPTIpDQogIGdnIDwtIGdnICsgZ2VvbV9wb2ludChkYXRhPWNlbnRlcnMsIGFlcyh4LCB5KSwgc2hhcGU9OCwgY29sb3I9ImJsYWNrIiwgc2l6ZT00KQ0KICBnZyA8LSBnZyArIHNjYWxlX3hfY29udGludW91cyhleHBhbmQ9YygwLDAuMSkpDQogIGdnIDwtIGdnICsgc2NhbGVfeV9jb250aW51b3VzKGV4cGFuZD1jKDAsMC4xKSkNCiAgZ2cgPC0gZ2cgKyBnZ3RpdGxlKHBhc3RlKCJrLW1lYW5zIHdpdGgiLCBpLCAiY2x1c3RlcnMiKSkNCiAgZ2cgPC0gZ2cgKyB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgZmlsbD1OQSksDQogICAgICAgICAgICAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgICAgICAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICAgICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkpDQogIG91dFtbaS0yXV0gPC0gZ2cNCn0NCnByaW50KG91dFtbMV1dKQ0KcHJpbnQob3V0W1syXV0pDQpwcmludChvdXRbWzNdXSkNCnByaW50KG91dFtbNF1dKQ0KYGBgDQoNCkhlcmUgd2UgY3JlYXRlIGEgcmFuZG9tIHJhbmRvbSBzY2F0dGVyIHBsb3QgdG8gZGVtb25zdHJhdGUgay1tZWFucyBjbHVzdGVyaW5nLiBUaGUgSyBzdGFuZHMgZm9yIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMgeW91IHdhbnQgdG8gY3JlYXRlLiBOb3RpY2UgdGhhdCB3ZSBtYWRlIDQgZGlmZmVyZW50IGNoYXJ0cyB3aXRoIGEgY2x1c3RlciBvZiAzIGluIHRoZSBmaXJzdCBhbmQgYXNjZW5kaW5nIHRvIDYgY2x1c3RlcnMgaW4gdGhlIGxhc3QuIFRoZSBjbHVzdGVyaW5nIGlzIGF0IHJhbmRvbSBhbmQgbmVlZHMgdG8gYmUgc3BlY2lmaWVkIGJ5IHlvdSB3aGljaCBpcyB3aHkgaXQgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgY2FuIGhlbHAgaW4gZWxpbWluYXRpbmcgdGhlIGd1ZXNzaW5nIGFzIHRvIGhvdyBtYW55IGNsdXN0ZXJzIHN1aXRzIHlvdSwgbWF5YmUgZm9yIGRpbWVuc2lvbiByZWR1Y3Rpb24uDQo=