Running principal component and k-means clustering analyses
dir.create("./results", showWarnings = F)
dir.create("./results/figures", showWarnings = F)
source("./src/GM_stats.R")
Performing GPA
|
| | 0%
|
|==================== | 25%
|
|======================================== | 50%
|
|============================================================ | 75%
|
|================================================================================| 100%
Making projections... Finished!



[1] -0.9999859
[1] 0.9999956
[1] -0.9999535
[1] -0.9999967
[1] -0.999442
[1] -0.9999388
[1] -1
[1] 0.9997545
[1] -0.9996835
[1] 0.9999969

Plotting PC and k-means results
K-means clustering plot:

Kennel-club groupings and PC results:

Discriminant Factors between groups
Comparison of landmarks between group means
# Extracting first 5 PCs and each kennel-club grouping scheme
skulls <- cbind(SlicerMorph.repc[, 1:5],
"UKC" = factor(SlicerMorph.repc$UKC, ordered = T, levels = UKC_group_order),
"AKC" = factor(SlicerMorph.repc$AKC, ordered = T, levels = AKC_group_order),
"bitework" = factor(SlicerMorph.repc$Bitework),
"scentwork" = factor(SlicerMorph.repc$Nosework))
skulls <- na.omit(skulls)
# Create a paired and ordered GPA-grouping object
mixdrop<-gpa$coords[,,order(dimnames(gpa$coords)[[3]])]
mixdrop<-mixdrop[,,-96]
reforge<-geomorph.data.frame(shape = mixdrop,
UKC = na.omit(factor(SlicerMorph.repc$UKC, ordered = T)),
AKC = na.omit(factor(SlicerMorph.repc$AKC, ordered = T)),
bitework = na.omit(factor(SlicerMorph.repc$Bitework[-96])),
scentwork = na.omit(factor(SlicerMorph.repc$Nosework[-96])))
# Create a function that subsets the mean landmarks for each group and does a pairwise subtraction and squaring of those means
meanforge <- function(target, group) {
#sub-setting the means and sending them to a new object
new.coords<-coords.subset(target[["shape"]], group = target[[group]])
output_means<-lapply(new.coords, mshape)
#perform the subtraction and squaring of the pairwise combinations
result<- combn(output_means, 2, function(x) (x[[1]]-x[[2]])^2, simplify = FALSE)
#rename the pairs so they're readable
names(result)<-combn(names(output_means), 2, function(n) paste(n[1], "-", n[2]), simplify = TRUE)
return(result)
}
#now run the function for each grouping scheme. Each member of the list will be named based on which two groups are compared
meanshape.UKC<-meanforge(reforge,"UKC")
meanshape.AKC<-meanforge(reforge,"AKC")
meanshape.nose<-meanforge(reforge,"scentwork")
meanshape.bite<-meanforge(reforge,"bitework")
Reformatting data for visualization:
calc_gpa_dist <- function(dat){
require(tidyr)
n <- length(names(dat))
results <- matrix(NA, nrow = nrow(dat[[1]]), ncol = n)
results <- as.data.frame(results)
results <- cbind(seq(1,nrow(results)), results)
colnames(results) <- c("landmark",names(dat))
for(i in names(dat)){
results[[i]] <- sqrt(dat[[i]][,"X"]^2 + dat[[i]][,"Y"]^2 + dat[[i]][,"Z"]^2)
}
results_long <- pivot_longer(results, cols = -"landmark", names_to = "comparison")
results_long$landmark <- factor(results_long$landmark)
return(results_long)
}
meanshape.AKC[["plotting"]] <- calc_gpa_dist(meanshape.AKC)
meanshape.UKC[["plotting"]] <- calc_gpa_dist(meanshape.UKC)
meanshape.bite[["plotting"]] <- calc_gpa_dist(meanshape.bite)
meanshape.bite[["cutoff"]] <- data.frame("mean" = mean(meanshape.bite[["plotting"]]$value),
"sd" = sd(meanshape.bite[["plotting"]]$value))
meanshape.nose[["plotting"]] <- calc_gpa_dist(meanshape.nose)
meanshape.nose[["cutoff"]] <- data.frame("mean" = mean(meanshape.nose[["plotting"]]$value),
"sd" = sd(meanshape.nose[["plotting"]]$value))
meanshape.AKC[["plotting"]]$short <- ifelse(grepl("toy", meanshape.AKC[["plotting"]]$comparison,
ignore.case = T),
"toy",
ifelse(grepl("natural", meanshape.AKC[["plotting"]]$comparison,
ignore.case = T),
"natural", "other"))
meanshape.AKC[["plotting"]]$short <- factor(meanshape.AKC[["plotting"]]$short,
ordered = T, levels = c("toy", "natural", "other"))
meanshape.AKC[["cutoff"]] <- data.frame("mean" = mean(meanshape.AKC[["plotting"]]$value),
"sd" = sd(meanshape.AKC[["plotting"]]$value))
meanshape.UKC[["plotting"]]$short <- ifelse(grepl("companion", meanshape.UKC[["plotting"]]$comparison,
ignore.case = T),
"companion",
ifelse(grepl("natural", meanshape.UKC[["plotting"]]$comparison,
ignore.case = T),
"natural", "other"))
meanshape.UKC[["plotting"]]$short <- factor(meanshape.UKC[["plotting"]]$short,
ordered = T, levels = c("companion", "natural", "other"))
meanshape.UKC[["cutoff"]] <- data.frame("mean" = mean(meanshape.UKC[["plotting"]]$value),
"sd" = sd(meanshape.UKC[["plotting"]]$value))
AKC & UKC groups:
p_UKC_compare <- ggplot(meanshape.UKC[["plotting"]], aes(landmark, value, color = short)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.UKC[["cutoff"]]$mean+2*meanshape.UKC[["cutoff"]]$sd,
lty = 2) +
scale_color_viridis_d(name = "Comparison\nagainst:") +
annotate("segment", x = 1, xend = 20, y = 0.0085, yend = 0.0085,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0075, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.0085, yend = 0.0085,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0075, label = "Ventral features", color = "blue") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("UKC group comparisons") +
theme_bw() #+
#theme(legend.position = "bottom")
#theme(legend.position = "inside", legend.position.inside = c(0.75,0.65),
# legend.background = element_rect(color = "gray30", fill = "white", linetype="solid",
# linewidth = 0.2))
p_AKC_compare <- ggplot(meanshape.AKC[["plotting"]], aes(landmark, value, color = short)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.AKC[["cutoff"]]$mean+2*meanshape.AKC[["cutoff"]]$sd,
lty = 2) +
scale_color_viridis_d(name = "Comparison\nagainst:") +
annotate("segment", x = 1, xend = 20, y = 0.0085, yend = 0.0085,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0075, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.0085, yend = 0.0085,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0075, label = "Ventral features", color = "blue") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("AKC group comparisons") +
theme_bw() #+
#theme(legend.position = "bottom")
#theme(legend.position = "inside", legend.position.inside = c(0.75,0.65),
# legend.background = element_rect(color = "gray30", fill = "white", linetype="solid",
# linewidth = 0.2))
p_AKC_compare / p_UKC_compare

Nosework and Bitework:
p_bite_compare <- ggplot(meanshape.bite[["plotting"]], aes(landmark, value, color = comparison)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.bite[["cutoff"]]$mean+2*meanshape.bite[["cutoff"]]$sd,
lty = 2) +
annotate("segment", x = 1, xend = 20, y = 0.007, yend = 0.007,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0065, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.007, yend = 0.007,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0065, label = "Ventral features", color = "blue") +
scale_color_viridis_d(name = "Group-group\ncomparison") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("Bite-work group comparisons") +
theme_bw()
p_nose_compare <- ggplot(meanshape.nose[["plotting"]], aes(landmark, value, color = comparison)) +
geom_point(position = position_jitter(width = 0.1)) +
geom_hline(yintercept = meanshape.nose[["cutoff"]]$mean+2*meanshape.nose[["cutoff"]]$sd,
lty = 2) +
annotate("segment", x = 1, xend = 20, y = 0.007, yend = 0.007,
lty = 1, color = "red") +
annotate("text", x = 10, y = 0.0065, label = "Dorsal features", color = "red") +
annotate("segment", x = 21, xend = 38, y = 0.007, yend = 0.007,
lty = 1, color = "blue") +
annotate("text", x = 30, y = 0.0065, label = "Ventral features", color = "blue") +
scale_color_viridis_d(name = "Group-group\ncomparison") +
ylab("Group-mean landmark\ndistance squared") +
ggtitle("Scent-work group comparisons") +
theme_bw()
p_bite_compare / p_nose_compare + plot_layout(guides = "collect")

MANOVA
man_UKC <- manova(as.matrix(skulls[,1:5])~skulls$UKC)
summary.aov(man_UKC, test="Pillai")
Response PC.1 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.65020 0.081275 35.372 < 2.2e-16 ***
Residuals 107 0.24585 0.002298
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.2 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.073427 0.0091783 6.0866 1.902e-06 ***
Residuals 107 0.161352 0.0015080
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.3 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.016265 0.00203313 2.4906 0.01612 *
Residuals 107 0.087348 0.00081633
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.4 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.024974 0.00312178 5.7231 4.625e-06 ***
Residuals 107 0.058365 0.00054547
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Response PC.5 :
Df Sum Sq Mean Sq F value Pr(>F)
skulls$UKC 8 0.000574 0.00007179 0.1633 0.9951
Residuals 107 0.047029 0.00043952
#using Geomorph and doing pairwise comparisons
manUKC<-procD.lm(shape ~ UKC, data = reforge, RRPP = TRUE)
manAKC<-procD.lm(shape ~ AKC, data = reforge, RRPP = TRUE)
manBite<-procD.lm(shape ~ bitework, data = reforge, RRPP = TRUE)
manScent<-procD.lm(shape ~ scentwork, data = reforge, RRPP = TRUE)
hold.ukc<-manova(manUKC)
hold.akc<-manova(manAKC)
hold.bite<-manova(manBite)
hold.nose<-manova(manScent)
ukcpairs<-pairwise(manUKC, groups = reforge$UKC)
akcpairs<-pairwise(manAKC, groups = reforge$AKC)
bitepairs<-pairwise(manBite, groups = reforge$bitework)
nosepairs<-pairwise(manScent, groups = reforge$scentwork)
pvals.UKC<-summary(ukcpairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.UKC<-pvals.UKC$pairwise.tables$P[lower.tri(pvals.UKC$pairwise.tables$P)]
pvals.AKC<-summary(akcpairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.AKC<-pvals.AKC$pairwise.tables$P[lower.tri(pvals.AKC$pairwise.tables$P)]
pvals.bite<-summary(bitepairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.bite<-pvals.bite$pairwise.tables$P[lower.tri(pvals.bite$pairwise.tables$P)]
pvals.nose<-summary(nosepairs, test.type= "dist", confidence = 0.95, stat.table = TRUE)
pvals.nose<-pvals.nose$pairwise.tables$P[lower.tri(pvals.nose$pairwise.tables$P)]
pvals.ukc.adjusted<-p.adjust(pvals.UKC, method = "bonf")
pvals.akc.adjusted<-p.adjust(pvals.AKC, method = "bonf")
pvals.bite.adjusted<-p.adjust(pvals.bite, method = "bonf")
pvals.nose.adjusted<-p.adjust(pvals.nose, method = "bonf")
lda_UKC <- MASS::lda(skulls$UKC~as.matrix(skulls[,1:5]))
lda_UKC
Call:
lda(skulls$UKC ~ as.matrix(skulls[, 1:5]))
Prior probabilities of groups:
NATURAL Herding Terrier Companion Gun dog Guardian
0.19827586 0.23275862 0.08620690 0.12068966 0.05172414 0.09482759
Northern Breed Sighthound Scenthound
0.02586207 0.14655172 0.04310345
Group means:
as.matrix(skulls[, 1:5])PC.1 as.matrix(skulls[, 1:5])PC.2
NATURAL -0.11869015 -0.019063433
Herding -0.12101159 0.026436494
Terrier -0.05852371 0.001880539
Companion 0.09983210 0.018101105
Gun dog -0.11168214 0.041234328
Guardian -0.06180271 0.065382213
Northern Breed -0.06497801 0.049661060
Sighthound -0.15480497 0.029842740
Scenthound -0.12915491 0.049368307
as.matrix(skulls[, 1:5])PC.3 as.matrix(skulls[, 1:5])PC.4
NATURAL 0.04817564 -0.016530397
Herding 0.04577986 0.013615782
Terrier 0.04485686 0.032376419
Companion 0.02331451 0.011348484
Gun dog 0.04624008 0.007520818
Guardian 0.06595086 0.003014570
Northern Breed 0.06220728 -0.017512289
Sighthound 0.02964807 0.018704155
Scenthound 0.04321946 0.010813419
as.matrix(skulls[, 1:5])PC.5
NATURAL 0.1158473
Herding 0.1190599
Terrier 0.1152870
Companion 0.1182048
Gun dog 0.1204955
Guardian 0.1207284
Northern Breed 0.1170088
Sighthound 0.1147247
Scenthound 0.1131374
Coefficients of linear discriminants:
LD1 LD2 LD3 LD4 LD5
as.matrix(skulls[, 1:5])PC.1 21.465233 2.072852 2.157478 1.232714 0.61490553
as.matrix(skulls[, 1:5])PC.2 2.130815 -20.151953 14.104686 -8.508683 1.48405135
as.matrix(skulls[, 1:5])PC.3 -13.131521 1.216403 21.217452 26.189753 4.17032936
as.matrix(skulls[, 1:5])PC.4 10.561161 -31.562092 -23.132983 17.258716 -0.02015756
as.matrix(skulls[, 1:5])PC.5 2.636558 -2.368104 6.558913 4.347214 -46.98480500
Proportion of trace:
LD1 LD2 LD3 LD4 LD5
0.7331 0.1664 0.0818 0.0172 0.0015
Testing of Functional Groups


bite_yes_canine <- bite_data$canine[bite_data$Bitework == "yes"]
bite_no_canine <- bite_data$canine[bite_data$Bitework == "no"]
shapiro.test(bite_yes_canine)
Shapiro-Wilk normality test
data: bite_yes_canine
W = 0.95076, p-value = 0.5726
shapiro.test(bite_no_canine)
Shapiro-Wilk normality test
data: bite_no_canine
W = 0.97244, p-value = 0.08128
t.test(bite_yes_canine, bite_no_canine)
Welch Two Sample t-test
data: bite_yes_canine and bite_no_canine
t = -0.30306, df = 36.495, p-value = 0.7636
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-9.152725 6.771939
sample estimates:
mean of x mean of y
57.78286 58.97325
bite_yes_carnassial <- bite_data$carnassial[bite_data$Bitework == "yes"]
bite_no_carnassial <- bite_data$carnassial[bite_data$Bitework == "no"]
shapiro.test(bite_yes_carnassial)
Shapiro-Wilk normality test
data: bite_yes_carnassial
W = 0.92608, p-value = 0.2687
shapiro.test(bite_no_carnassial)
Shapiro-Wilk normality test
data: bite_no_carnassial
W = 0.96684, p-value = 0.03577
t.test(bite_yes_carnassial, bite_no_carnassial)
Welch Two Sample t-test
data: bite_yes_carnassial and bite_no_carnassial
t = -0.52911, df = 42.805, p-value = 0.5995
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-9.417946 5.503589
sample estimates:
mean of x mean of y
57.12857 59.08575
bite_natural_canine <- bite_data$canine[bite_data$Bitework == "NATURAL"]
shapiro.test(bite_natural_canine)
Shapiro-Wilk normality test
data: bite_natural_canine
W = 0.91337, p-value = 0.2672
bite_fox_canine <- bite_data$canine[bite_data$Bitework == "FOX"]
shapiro.test(bite_fox_canine)
Shapiro-Wilk normality test
data: bite_fox_canine
W = 0.82835, p-value = 0.03197
t.test(bite_yes_canine, bite_natural_canine)
Welch Two Sample t-test
data: bite_yes_canine and bite_natural_canine
t = -1.6371, df = 13.462, p-value = 0.1248
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-29.241240 3.979681
sample estimates:
mean of x mean of y
57.78286 70.41364
t.test(bite_yes_canine, bite_fox_canine)
Welch Two Sample t-test
data: bite_yes_canine and bite_fox_canine
t = -0.85342, df = 10.859, p-value = 0.4119
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-29.77613 13.15585
sample estimates:
mean of x mean of y
57.78286 66.09300
For the paper:
Figure 2: k-means cluster on PC plot

Figure 3: UKC comparison figure

Figure 4: AKC comparison figure

Figure 5: task-specific group results

LS0tCnRpdGxlOiAiR2VvbW9ycGhpYyBNb3JwaG9tZXRyaWNzIG9uIGNhbmlkIHNrdWxscyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gcnByb2pyb290OjpmaW5kX3JzdHVkaW9fcm9vdF9maWxlKCkpCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgZmlnLndpZHRoID0gOC41KQpvcHRpb25zKGtuaXRyLmdyYXBoaWNzLmVycm9yID0gRkFMU0UpCm9wdGlvbnMoa25pdHIua2FibGUuTkEgPSAnJykKb3B0aW9ucyhyZ2wudXNlTlVMTD1UUlVFKSAjIE5vdGU6IE9wZW5HTCBpcyBkZXByZWNpYXRlZCBvbiBNYWNzIGFuZCByZXF1aXJlcyBYUXVhcnR6IGluIG9yZGVyIHRvIHdvcmsgcHJvcGVybHkuIFRoaXMgd2lsbCBjYXVzZSBhbiBlcnJvciB3aGVuIGxvYWRpbmcgdGhlIE1vcnBobyBhbmQgZ2VvbW9ycGggcGFja2FnZXMuIFNldHRpbmcgdGhpcyBvcHRpb24gcmVtb3ZlcyB0aGUgZXJyb3IuIElmIHlvdSBoYXZlIFhRdWFydHogYW5kIGRvbid0IG1pbmQgaXQgb3BlbmluZywgc3VwcHJlc3MgdGhpcyBsaW5lIG9mIGNvZGUgdG8gcmVnYWluIGZ1bGwgZnVuY3Rpb25hbGl0eS4KCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGphbml0b3IpCmxpYnJhcnkoZ3Rvb2xzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoZ2VvbW9ycGgpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdTbGljZXJNb3JwaC9TbGljZXJNb3JwaFInKQpsaWJyYXJ5KFNsaWNlck1vcnBoUikKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2xpbmRzYXl3YWxkcm9wL211bmNoY29sb3JzJykKbGlicmFyeShtdW5jaGNvbG9ycykKYGBgCgojIyBSdW5uaW5nIHByaW5jaXBhbCBjb21wb25lbnQgYW5kIGstbWVhbnMgY2x1c3RlcmluZyBhbmFseXNlcwoKYGBge3J9CmRpci5jcmVhdGUoIi4vcmVzdWx0cyIsIHNob3dXYXJuaW5ncyA9IEYpCmRpci5jcmVhdGUoIi4vcmVzdWx0cy9maWd1cmVzIiwgc2hvd1dhcm5pbmdzID0gRikKc291cmNlKCIuL3NyYy9HTV9zdGF0cy5SIikKYGBgCgojIyBQbG90dGluZyBQQyBhbmQgay1tZWFucyByZXN1bHRzCgoKYGBge3IgcGxvdHRpbmctc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgU2V0dGluZyBVS0MgZ3JvdXAgb3JkZXIKVUtDX2dyb3VwX29yZGVyIDwtIGMoIk5BVFVSQUwiLCAiSGVyZGluZyIsICJUZXJyaWVyIiwgIkNvbXBhbmlvbiIsICJHdW4gZG9nIiwgCiAgICAgICAgICAgICAgICAgICAgICJHdWFyZGlhbiIsICJOb3J0aGVybiBCcmVlZCIsICJTaWdodGhvdW5kIiwgIlNjZW50aG91bmQiKQpBS0NfZ3JvdXBfb3JkZXIgPC0gYygiTkFUVVJBTCIsICJIZXJkaW5nIiwgIlRlcnJpZXIiLCAiVG95IiwgIlNwb3J0aW5nIiwgCiAgICAgICAgICAgICAgICAgICAgICJXb3JraW5nIiwgIk5vbi1zcG9ydGluZyIsICJIb3VuZCIpCgojIGdyb3VwaW5nIHBsb3RzIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBGaXJzdCBhZGRlZCB5b3VyIG9yZGVyZWQgY2xhc3NpZmllcnMgb250byB0aGUgUENBIGRhdGEKU2xpY2VyTW9ycGgucmVwYyRVS0MgPC0gY2xzZl9yZW9yZCRVS0MuYnJlZWRpbmcuU3RhbmRhcmQKU2xpY2VyTW9ycGgucmVwYyRBS0MgPC0gY2xzZl9yZW9yZCRBS0MuYnJlZWRpbmcuc3RhbmRhcmQKIyBTZXQgbWl4ZWQgYnJlZWQgdG8gTkEgZm9yIHBsb3R0aW5nLCB0aGVyZSBpcyBvbmx5IG9uZSEKU2xpY2VyTW9ycGgucmVwYyRVS0NbU2xpY2VyTW9ycGgucmVwYyRVS0MgPT0gIk1JWEVEIl0gPC0gTkEKU2xpY2VyTW9ycGgucmVwYyRBS0NbU2xpY2VyTW9ycGgucmVwYyRBS0MgPT0gIk1JWEVEIl0gPC0gTkEKIyBTZXR0aW5nIGZveCB0byBuYXR1cmFsIGZvciBBS0MvVUtDIGdyb3VwaW5nIHRvIHNpbXBsaWZ5IHBsb3RzClNsaWNlck1vcnBoLnJlcGMkVUtDW1NsaWNlck1vcnBoLnJlcGMkVUtDID09ICJGT1giXSA8LSAiTkFUVVJBTCIKU2xpY2VyTW9ycGgucmVwYyRBS0NbU2xpY2VyTW9ycGgucmVwYyRBS0MgPT0gIkZPWCJdIDwtICJOQVRVUkFMIgoKIyBDaGFuZ2luZyBZZXMgdG8gbG93ZXJjYXNlClNsaWNlck1vcnBoLnJlcGMkTm9zZXdvcmsgPC0gaWZlbHNlKGNsc2ZfcmVvcmQkTm9zZSA9PSJZZXMiIHwgY2xzZl9yZW9yZCROb3NlID09ICJubyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2xvd2VyKGNsc2ZfcmVvcmQkTm9zZSksIGNsc2ZfcmVvcmQkTm9zZSkKIyBDaGFuZ2luZyBOb3Nld29yayB0byBvcmRlcmVkIGZhY3RvcgpTbGljZXJNb3JwaC5yZXBjJE5vc2V3b3JrIDwtIGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJE5vc2V3b3JrLCBvcmRlcmVkID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoInllcyIsICJubyIsICJOQVRVUkFMIiwgIkZPWCIpKQojIENoYW5naW5nIFllcyB0byBsb3dlcmNhc2UgClNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmsgPC0gaWZlbHNlKGNsc2ZfcmVvcmQkQml0ZXdvcmsgPT0iWWVzIiB8IGNsc2ZfcmVvcmQkQml0ZXdvcmsgPT0gIm5vIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9sb3dlcihjbHNmX3Jlb3JkJEJpdGV3b3JrKSwgY2xzZl9yZW9yZCRCaXRld29yaykKIyBDaGFuZ2luZyBCaXRld29yayB0byBvcmRlcmVkIGZhY3RvcgpTbGljZXJNb3JwaC5yZXBjJEJpdGV3b3JrIDwtIGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEJpdGV3b3JrLCBvcmRlcmVkID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoInllcyIsICJubyIsICJOQVRVUkFMIiwgIkZPWCIpKQojIFB1dHRpbmcgY2x1c3RlciBsZXZlbHMgaW50byBhIGNvbHVtbiBhcyBhIGZhY3RvcgpTbGljZXJNb3JwaC5yZXBjJGNsdXN0ZXIgPC0gZmFjdG9yKGttJGNsdXN0ZXIpCgojIE5vdyB0byBlYXNlIHZpZXdpbmcgd2UnbGwgZmFjdG9yIHRoZW0gaW50byBicm9hZCBjYXRlZ29yaWVzCgojIFR1cm5pbmcgVUtDIGlzbnRvIGFuIG9yZGVyZWQgZmFjdG9yIGNvbHVtbgpTbGljZXJNb3JwaC5yZXBjJFVLQy5zaGFwZWZhY3QgPC0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkVUtDLCBvcmRlcmVkID0gVCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gVUtDX2dyb3VwX29yZGVyKQoKIyMgQUtDIG5leHQKU2xpY2VyTW9ycGgucmVwYyRBS0Muc2hhcGVmYWN0IDwtIGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEFLQywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gQUtDX2dyb3VwX29yZGVyKQoKU2xpY2VyTW9ycGgucmVwYyRBS0MuZmFjdCA8LSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRBS0MpCgojIyBTZXR0aW5nIHVwIGEgY29sdW1uIHRoYXQgaXMgZG9tZXN0aWMgKGRvZ3MpIGFuZCBuYXR1cmFsIChmb3hlcywgb3RoZXJzKQpTbGljZXJNb3JwaC5yZXBjJGRvbWVzdGljIDwtIGZhY3RvcihpZmVsc2UoU2xpY2VyTW9ycGgucmVwYyRVS0MgPT0gIk5BVFVSQUwiIHwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNsaWNlck1vcnBoLnJlcGMkVUtDID09ICJGT1giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOYXR1cmFsIiwgIkRvbWVzdGljYXRlZCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIGxldmVscyA9IGMoIkRvbWVzdGljYXRlZCIsICJOYXR1cmFsIikpCgojIyBDYWxjdWxhdGluZyBncm91cCBtZWFucyBmb3Igc2V0dGluZyBza3VsbHMgb24gcGxvdHM6IApVS0MgPC0gU2xpY2VyTW9ycGgucmVwY1shaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpLF0KVUtDIDwtIFVLQ1ssYygiUEMuMSIsICJQQy4yIiwgIlVLQyIpXQpVS0Nfc2NlbnRob3VuZF9tZWFuIDwtIGRhdGEuZnJhbWUoeCA9IG1lYW4oVUtDJFBDLjFbVUtDJFVLQyA9PSAiU2NlbnRob3VuZCJdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1lYW4oVUtDJFBDLjJbVUtDJFVLQyA9PSAiU2NlbnRob3VuZCJdKSkKVUtDX2NvbXBhbmlvbl9tZWFuIDwtIGRhdGEuZnJhbWUoeCA9IG1lYW4oVUtDJFBDLjFbVUtDJFVLQyA9PSAiQ29tcGFuaW9uIl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gbWVhbihVS0MkUEMuMltVS0MkVUtDID09ICJDb21wYW5pb24iXSkpCkFLQyA8LSBTbGljZXJNb3JwaC5yZXBjWyFpcy5uYShTbGljZXJNb3JwaC5yZXBjJEFLQyksXQpBS0MgPC0gQUtDWyxjKCJQQy4xIiwgIlBDLjIiLCAiQUtDIildCkFLQ19ob3VuZF9tZWFuIDwtIGRhdGEuZnJhbWUoeCA9IG1lYW4oQUtDJFBDLjFbQUtDJEFLQyA9PSAiSG91bmQiXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IG1lYW4oQUtDJFBDLjJbQUtDJEFLQyA9PSAiSG91bmQiXSkpCkFLQ19ub25zcG9ydGluZ19tZWFuIDwtIGRhdGEuZnJhbWUoeCA9IG1lYW4oQUtDJFBDLjFbQUtDJEFLQyA9PSAiTm9uLXNwb3J0aW5nIl0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtZWFuKEFLQyRQQy4yW0FLQyRBS0MgPT0gIk5vbi1zcG9ydGluZyJdKSkKQUtDX3RveV9tZWFuIDwtIGRhdGEuZnJhbWUoeCA9IG1lYW4oQUtDJFBDLjFbQUtDJEFLQyA9PSAiVG95Il0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBtZWFuKEFLQyRQQy4yW0FLQyRBS0MgPT0gIlRveSJdKSkKYGBgCgpLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdDogCgpgYGB7ciBrLW1lYW5zLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4ifQojbm93IGJ1aWxkIHRoZSBwbG90CnBrbWVhbiA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwY1shaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpLF0sIGFlcyhQQy4xLCBQQy4yLCBjb2xvciA9IGNsdXN0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBjbHVzdGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZG9tZXN0aWMpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICBzdGF0X2VsbGlwc2UoZ2VvbSA9ICJwb2x5Z29uIiwgYWVzKGZpbGwgPSBjbHVzdGVyLCBncm91cCA9IGNsdXN0ZXIpLCBsZXZlbCA9IDAuOTUsIGFscGhhID0gMC4xKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoNSwgMTkpLCBuYW1lID0gIiAiKSArCiAgc2NhbGVfY29sb3JfbXVuY2goY2hvaWNlID0gIk5pZXR6c2NoZSIsIGRpc2NyZXRlID0gVFJVRSxuYW1lID0gIkNsdXN0ZXIiKSArIAogIHNjYWxlX2ZpbGxfbXVuY2goY2hvaWNlID0gIk5pZXR6c2NoZSIsIGRpc2NyZXRlID0gVFJVRSwgbmFtZSA9ICJDbHVzdGVyIikgKwogIGdlb21fdGV4dF9yZXBlbChsYWJlbCA9IFNsaWNlck1vcnBoLnJlcGMkQnJlZWRbIWlzLm5hKFNsaWNlck1vcnBoLnJlcGMkVUtDKV0sIG1heC5vdmVybGFwcyA9IDQsIHNob3cubGVnZW5kID0gRkFMU0UpKwogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgeWxhYigiUEMgMiAoVmFyID0gMTMuMyUpIikgKwogIHRoZW1lX2J3KCkKcGttZWFuCmBgYAoKS2VubmVsLWNsdWIgZ3JvdXBpbmdzIGFuZCBQQyByZXN1bHRzOgoKYGBge3Iga2MtZ3JvdXBzLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4iLCBmaWcuaGVpZ2h0PTUuNSwgZmlnLndpZHRoPTEyfQojbm93IGJ1aWxkIHRoZSBwbG90CmtjX3BhbGV0dGUgPC0gbXVuY2hfcGFsZXR0ZSgiTXVyZGVyZXIiLCA4KQprY19wYWxldHRlIDwtIGMoa2NfcGFsZXR0ZSwga2NfcGFsZXR0ZVsyXSkKcFVLQyA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwY1shaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpLF0sIGFlcyhQQy4xLCBQQy4yLCBjb2xvciA9IFVLQy5zaGFwZWZhY3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBVS0Muc2hhcGVmYWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gVUtDLnNoYXBlZmFjdCkpICsgCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBhZXMoZ3JvdXAgPSBVS0Muc2hhcGVmYWN0KSwgbGV2ZWwgPSAwLjk1LCBhbHBoYSA9IDAuMikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKDE5LCAwLCAxLCAyLCA1LCA2LCA3LCA5LCAxMCksIG5hbWUgPSAiVUtDIEdyb3VwcyIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0ga2NfcGFsZXR0ZSwgbmFtZSA9ICJVS0MgR3JvdXBzIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBrY19wYWxldHRlLCBuYW1lID0gIlVLQyBHcm91cHMiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gU2xpY2VyTW9ycGgucmVwYyRCcmVlZFshaXMubmEoU2xpY2VyTW9ycGgucmVwYyRVS0MpXSwgCiAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDQsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAwLjEsIHhlbmQgPSBVS0NfY29tcGFuaW9uX21lYW4keCwgeSA9IC0wLjEsIHllbmQgPSBVS0NfY29tcGFuaW9uX21lYW4keSwKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0ga2NfcGFsZXR0ZVs0XSkgKwogIGFubm90YXRlKCJzZWdtZW50IiwgeCA9IC0wLjI0LCB4ZW5kID0gVUtDX3NjZW50aG91bmRfbWVhbiR4LCB5ID0gLTAuMSwgeWVuZCA9IFVLQ19zY2VudGhvdW5kX21lYW4keSwKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0ga2NfcGFsZXR0ZVs5XSkgKwogIHhsaW0oLTAuMzEsIE5BKSArCiAgeWxpbSgtMC4yMCwgTkEpICsKICB4bGFiKCJQQyAxIChWYXIgPSA1MC4yJSkiKSArIHlsYWIoIlBDIDIgKFZhciA9IDEzLjMlKSIpICsKICB0aGVtZV9idygpCnBBS0MgPC0gZ2dwbG90KFNsaWNlck1vcnBoLnJlcGNbIWlzLm5hKFNsaWNlck1vcnBoLnJlcGMkQUtDKSxdLCBhZXMoUEMuMSwgUEMuMiwgY29sb3IgPSBBS0Muc2hhcGVmYWN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gQUtDLnNoYXBlZmFjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IEFLQy5zaGFwZWZhY3QpKSArIAogIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICBzdGF0X2VsbGlwc2UoZ2VvbSA9ICJwb2x5Z29uIiwgYWVzKGZpbGwgPSBBS0Muc2hhcGVmYWN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gQUtDLnNoYXBlZmFjdCksIGxldmVsID0gMC45NSwgYWxwaGEgPSAwLjIpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygxOSwgMCwgMSwgMiwgNSwgNiwgNywgOSwgMTApLCBuYW1lID0gIkFLQyBHcm91cHMiKSArCiAgc2NhbGVfY29sb3JfbXVuY2goY2hvaWNlID0gIk11cmRlcmVyIiwgZGlzY3JldGUgPSBUUlVFLCBuYW1lID0gIkFLQyBHcm91cHMiKSArIAogIHNjYWxlX2ZpbGxfbXVuY2goY2hvaWNlID0gIk11cmRlcmVyIiwgZGlzY3JldGUgPSBUUlVFLCBuYW1lID0gIkFLQyBHcm91cHMiKSArCiAgZ2VvbV90ZXh0X3JlcGVsKGxhYmVsID0gU2xpY2VyTW9ycGgucmVwYyRCcmVlZFshaXMubmEoU2xpY2VyTW9ycGgucmVwYyRBS0MpXSwgbWF4Lm92ZXJsYXBzID0gNCwgc2hvdy5sZWdlbmQgPSBGQUxTRSkrCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gLTAuMjIsIHhlbmQgPSBBS0NfaG91bmRfbWVhbiR4LCB5ID0gLTAuMDUsIHllbmQgPSBBS0NfaG91bmRfbWVhbiR5LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSBrY19wYWxldHRlWzhdKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMC4xLCB4ZW5kID0gQUtDX25vbnNwb3J0aW5nX21lYW4keCwgeSA9IDAuMTUsIHllbmQgPSBBS0Nfbm9uc3BvcnRpbmdfbWVhbiR5LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSBrY19wYWxldHRlWzddKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMC4xNiwgeGVuZCA9IEFLQ190b3lfbWVhbiR4LCB5ID0gLTAuMDcsIHllbmQgPSBBS0NfdG95X21lYW4keSwKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0ga2NfcGFsZXR0ZVs0XSkgKwogIHhsaW0oLTAuMzEsIE5BKSArCiAgeWxpbShOQSwgMC4yMCkgKwogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgeWxhYigiUEMgMiAoVmFyID0gMTMuMyUpIikgKwogIHRoZW1lX2J3KCkgCgpwVUtDICsgcEFLQwpgYGAKCgojIyBEaXNjcmltaW5hbnQgRmFjdG9ycyBiZXR3ZWVuIGdyb3VwcwoKQ29tcGFyaXNvbiBvZiBsYW5kbWFya3MgYmV0d2VlbiBncm91cCBtZWFucwoKYGBge3J9CiMgRXh0cmFjdGluZyBmaXJzdCA1IFBDcyBhbmQgZWFjaCBrZW5uZWwtY2x1YiBncm91cGluZyBzY2hlbWUKc2t1bGxzIDwtIGNiaW5kKFNsaWNlck1vcnBoLnJlcGNbLCAxOjVdLCAKICAgICAgICAgICAgICAgICJVS0MiID0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkVUtDLCBvcmRlcmVkID0gVCwgbGV2ZWxzID0gVUtDX2dyb3VwX29yZGVyKSwgCiAgICAgICAgICAgICAgICAiQUtDIiA9IGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEFLQywgb3JkZXJlZCA9IFQsIGxldmVscyA9IEFLQ19ncm91cF9vcmRlciksCiAgICAgICAgICAgICAgICAiYml0ZXdvcmsiID0gZmFjdG9yKFNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmspLAogICAgICAgICAgICAgICAgInNjZW50d29yayIgPSBmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyROb3Nld29yaykpCnNrdWxscyA8LSBuYS5vbWl0KHNrdWxscykKCiMgQ3JlYXRlIGEgcGFpcmVkIGFuZCBvcmRlcmVkIEdQQS1ncm91cGluZyBvYmplY3QKbWl4ZHJvcDwtZ3BhJGNvb3Jkc1ssLG9yZGVyKGRpbW5hbWVzKGdwYSRjb29yZHMpW1szXV0pXQptaXhkcm9wPC1taXhkcm9wWywsLTk2XQoKcmVmb3JnZTwtZ2VvbW9ycGguZGF0YS5mcmFtZShzaGFwZSA9IG1peGRyb3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVUtDID0gbmEub21pdChmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRVS0MsIG9yZGVyZWQgPSBUKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQUtDID0gbmEub21pdChmYWN0b3IoU2xpY2VyTW9ycGgucmVwYyRBS0MsIG9yZGVyZWQgPSBUKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYml0ZXdvcmsgPSBuYS5vbWl0KGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJEJpdGV3b3JrWy05Nl0pKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2VudHdvcmsgPSBuYS5vbWl0KGZhY3RvcihTbGljZXJNb3JwaC5yZXBjJE5vc2V3b3JrWy05Nl0pKSkKCiMgQ3JlYXRlIGEgZnVuY3Rpb24gdGhhdCBzdWJzZXRzIHRoZSBtZWFuIGxhbmRtYXJrcyBmb3IgZWFjaCBncm91cCBhbmQgZG9lcyBhIHBhaXJ3aXNlIHN1YnRyYWN0aW9uIGFuZCBzcXVhcmluZyBvZiB0aG9zZSBtZWFucwptZWFuZm9yZ2UgPC0gZnVuY3Rpb24odGFyZ2V0LCBncm91cCkgewogICNzdWItc2V0dGluZyB0aGUgbWVhbnMgYW5kIHNlbmRpbmcgdGhlbSB0byBhIG5ldyBvYmplY3QKICBuZXcuY29vcmRzPC1jb29yZHMuc3Vic2V0KHRhcmdldFtbInNoYXBlIl1dLCBncm91cCA9IHRhcmdldFtbZ3JvdXBdXSkKICBvdXRwdXRfbWVhbnM8LWxhcHBseShuZXcuY29vcmRzLCBtc2hhcGUpCiAgI3BlcmZvcm0gdGhlIHN1YnRyYWN0aW9uIGFuZCBzcXVhcmluZyBvZiB0aGUgcGFpcndpc2UgY29tYmluYXRpb25zCiAgcmVzdWx0PC0gY29tYm4ob3V0cHV0X21lYW5zLCAyLCBmdW5jdGlvbih4KSAoeFtbMV1dLXhbWzJdXSleMiwgc2ltcGxpZnkgPSBGQUxTRSkKICAjcmVuYW1lIHRoZSBwYWlycyBzbyB0aGV5J3JlIHJlYWRhYmxlCiAgbmFtZXMocmVzdWx0KTwtY29tYm4obmFtZXMob3V0cHV0X21lYW5zKSwgMiwgZnVuY3Rpb24obikgcGFzdGUoblsxXSwgIi0iLCBuWzJdKSwgc2ltcGxpZnkgPSBUUlVFKQogIHJldHVybihyZXN1bHQpCn0KCiNub3cgcnVuIHRoZSBmdW5jdGlvbiBmb3IgZWFjaCBncm91cGluZyBzY2hlbWUuIEVhY2ggbWVtYmVyIG9mIHRoZSBsaXN0IHdpbGwgYmUgbmFtZWQgYmFzZWQgb24gd2hpY2ggdHdvIGdyb3VwcyBhcmUgY29tcGFyZWQKbWVhbnNoYXBlLlVLQzwtbWVhbmZvcmdlKHJlZm9yZ2UsIlVLQyIpCm1lYW5zaGFwZS5BS0M8LW1lYW5mb3JnZShyZWZvcmdlLCJBS0MiKQptZWFuc2hhcGUubm9zZTwtbWVhbmZvcmdlKHJlZm9yZ2UsInNjZW50d29yayIpCm1lYW5zaGFwZS5iaXRlPC1tZWFuZm9yZ2UocmVmb3JnZSwiYml0ZXdvcmsiKQoKYGBgCgpSZWZvcm1hdHRpbmcgZGF0YSBmb3IgdmlzdWFsaXphdGlvbjoKCmBgYHtyfQpjYWxjX2dwYV9kaXN0IDwtIGZ1bmN0aW9uKGRhdCl7CiAgcmVxdWlyZSh0aWR5cikKICBuIDwtIGxlbmd0aChuYW1lcyhkYXQpKQogIHJlc3VsdHMgPC0gbWF0cml4KE5BLCBucm93ID0gbnJvdyhkYXRbWzFdXSksIG5jb2wgPSBuKQogIHJlc3VsdHMgPC0gYXMuZGF0YS5mcmFtZShyZXN1bHRzKQogIHJlc3VsdHMgPC0gY2JpbmQoc2VxKDEsbnJvdyhyZXN1bHRzKSksIHJlc3VsdHMpCiAgY29sbmFtZXMocmVzdWx0cykgPC0gYygibGFuZG1hcmsiLG5hbWVzKGRhdCkpCiAgCiAgZm9yKGkgaW4gbmFtZXMoZGF0KSl7CiAgICByZXN1bHRzW1tpXV0gPC0gc3FydChkYXRbW2ldXVssIlgiXV4yICsgZGF0W1tpXV1bLCJZIl1eMiArIGRhdFtbaV1dWywiWiJdXjIpCiAgfQogIHJlc3VsdHNfbG9uZyA8LSBwaXZvdF9sb25nZXIocmVzdWx0cywgY29scyA9IC0ibGFuZG1hcmsiLCBuYW1lc190byA9ICJjb21wYXJpc29uIikKICByZXN1bHRzX2xvbmckbGFuZG1hcmsgPC0gZmFjdG9yKHJlc3VsdHNfbG9uZyRsYW5kbWFyaykKICByZXR1cm4ocmVzdWx0c19sb25nKQp9CgptZWFuc2hhcGUuQUtDW1sicGxvdHRpbmciXV0gPC0gY2FsY19ncGFfZGlzdChtZWFuc2hhcGUuQUtDKQptZWFuc2hhcGUuVUtDW1sicGxvdHRpbmciXV0gPC0gY2FsY19ncGFfZGlzdChtZWFuc2hhcGUuVUtDKQptZWFuc2hhcGUuYml0ZVtbInBsb3R0aW5nIl1dIDwtIGNhbGNfZ3BhX2Rpc3QobWVhbnNoYXBlLmJpdGUpCm1lYW5zaGFwZS5iaXRlW1siY3V0b2ZmIl1dIDwtIGRhdGEuZnJhbWUoIm1lYW4iID0gbWVhbihtZWFuc2hhcGUuYml0ZVtbInBsb3R0aW5nIl1dJHZhbHVlKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2QiID0gc2QobWVhbnNoYXBlLmJpdGVbWyJwbG90dGluZyJdXSR2YWx1ZSkpCm1lYW5zaGFwZS5ub3NlW1sicGxvdHRpbmciXV0gPC0gY2FsY19ncGFfZGlzdChtZWFuc2hhcGUubm9zZSkKbWVhbnNoYXBlLm5vc2VbWyJjdXRvZmYiXV0gPC0gZGF0YS5mcmFtZSgibWVhbiIgPSBtZWFuKG1lYW5zaGFwZS5ub3NlW1sicGxvdHRpbmciXV0kdmFsdWUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzZCIgPSBzZChtZWFuc2hhcGUubm9zZVtbInBsb3R0aW5nIl1dJHZhbHVlKSkKCm1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSRzaG9ydCA8LSBpZmVsc2UoZ3JlcGwoInRveSIsIG1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSRjb21wYXJpc29uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IFQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRveSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJuYXR1cmFsIiwgbWVhbnNoYXBlLkFLQ1tbInBsb3R0aW5nIl1dJGNvbXBhcmlzb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBUKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmF0dXJhbCIsICJvdGhlciIpKQptZWFuc2hhcGUuQUtDW1sicGxvdHRpbmciXV0kc2hvcnQgPC0gZmFjdG9yKG1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSRzaG9ydCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIGxldmVscyA9IGMoInRveSIsICJuYXR1cmFsIiwgIm90aGVyIikpCm1lYW5zaGFwZS5BS0NbWyJjdXRvZmYiXV0gPC0gZGF0YS5mcmFtZSgibWVhbiIgPSBtZWFuKG1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSR2YWx1ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNkIiA9IHNkKG1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSR2YWx1ZSkpCm1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSRzaG9ydCA8LSBpZmVsc2UoZ3JlcGwoImNvbXBhbmlvbiIsIG1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSRjb21wYXJpc29uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IFQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvbXBhbmlvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGdyZXBsKCJuYXR1cmFsIiwgbWVhbnNoYXBlLlVLQ1tbInBsb3R0aW5nIl1dJGNvbXBhcmlzb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibmF0dXJhbCIsICJvdGhlciIpKQptZWFuc2hhcGUuVUtDW1sicGxvdHRpbmciXV0kc2hvcnQgPC0gZmFjdG9yKG1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSRzaG9ydCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFQsIGxldmVscyA9IGMoImNvbXBhbmlvbiIsICJuYXR1cmFsIiwgIm90aGVyIikpCm1lYW5zaGFwZS5VS0NbWyJjdXRvZmYiXV0gPC0gZGF0YS5mcmFtZSgibWVhbiIgPSBtZWFuKG1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSR2YWx1ZSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNkIiA9IHNkKG1lYW5zaGFwZS5VS0NbWyJwbG90dGluZyJdXSR2YWx1ZSkpCgpgYGAKCkFLQyAmIFVLQyBncm91cHM6CmBgYHtyfQpwX1VLQ19jb21wYXJlIDwtIGdncGxvdChtZWFuc2hhcGUuVUtDW1sicGxvdHRpbmciXV0sIGFlcyhsYW5kbWFyaywgdmFsdWUsIGNvbG9yID0gc2hvcnQpKSArIAogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjEpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW5zaGFwZS5VS0NbWyJjdXRvZmYiXV0kbWVhbisyKm1lYW5zaGFwZS5VS0NbWyJjdXRvZmYiXV0kc2QsCiAgICAgICAgICAgICBsdHkgPSAyKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG5hbWUgPSAiQ29tcGFyaXNvblxuYWdhaW5zdDoiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMSwgeGVuZCA9IDIwLCB5ID0gMC4wMDg1LCB5ZW5kID0gMC4wMDg1LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSAicmVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEwLCB5ID0gMC4wMDc1LCBsYWJlbCA9ICJEb3JzYWwgZmVhdHVyZXMiLCBjb2xvciA9ICJyZWQiKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMjEsIHhlbmQgPSAzOCwgeSA9IDAuMDA4NSwgeWVuZCA9IDAuMDA4NSwKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0gImJsdWUiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMzAsIHkgPSAwLjAwNzUsIGxhYmVsID0gIlZlbnRyYWwgZmVhdHVyZXMiLCBjb2xvciA9ICJibHVlIikgKwogIHlsYWIoIkdyb3VwLW1lYW4gbGFuZG1hcmtcbmRpc3RhbmNlIHNxdWFyZWQiKSArCiAgZ2d0aXRsZSgiVUtDIGdyb3VwIGNvbXBhcmlzb25zIikgKwogIHRoZW1lX2J3KCkgIysKICAjdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCiAgI3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJpbnNpZGUiLCBsZWdlbmQucG9zaXRpb24uaW5zaWRlID0gYygwLjc1LDAuNjUpLAogICMgICAgICBsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvciA9ICJncmF5MzAiLCBmaWxsID0gIndoaXRlIiwgbGluZXR5cGU9InNvbGlkIiwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZXdpZHRoID0gMC4yKSkKCnBfQUtDX2NvbXBhcmUgPC0gZ2dwbG90KG1lYW5zaGFwZS5BS0NbWyJwbG90dGluZyJdXSwgYWVzKGxhbmRtYXJrLCB2YWx1ZSwgY29sb3IgPSBzaG9ydCkpICsgCiAgZ2VvbV9wb2ludChwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcih3aWR0aCA9IDAuMSkpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gbWVhbnNoYXBlLkFLQ1tbImN1dG9mZiJdXSRtZWFuKzIqbWVhbnNoYXBlLkFLQ1tbImN1dG9mZiJdXSRzZCwKICAgICAgICAgICAgIGx0eSA9IDIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobmFtZSA9ICJDb21wYXJpc29uXG5hZ2FpbnN0OiIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAxLCB4ZW5kID0gMjAsIHkgPSAwLjAwODUsIHllbmQgPSAwLjAwODUsCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9ICJyZWQiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMTAsIHkgPSAwLjAwNzUsIGxhYmVsID0gIkRvcnNhbCBmZWF0dXJlcyIsIGNvbG9yID0gInJlZCIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAyMSwgeGVuZCA9IDM4LCB5ID0gMC4wMDg1LCB5ZW5kID0gMC4wMDg1LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSAiYmx1ZSIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAzMCwgeSA9IDAuMDA3NSwgbGFiZWwgPSAiVmVudHJhbCBmZWF0dXJlcyIsIGNvbG9yID0gImJsdWUiKSArCiAgeWxhYigiR3JvdXAtbWVhbiBsYW5kbWFya1xuZGlzdGFuY2Ugc3F1YXJlZCIpICsKICBnZ3RpdGxlKCJBS0MgZ3JvdXAgY29tcGFyaXNvbnMiKSArCiAgdGhlbWVfYncoKSAjKwogICN0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICAgICN0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiaW5zaWRlIiwgbGVnZW5kLnBvc2l0aW9uLmluc2lkZSA9IGMoMC43NSwwLjY1KSwKICAgICMgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JheTMwIiwgZmlsbCA9ICJ3aGl0ZSIsIGxpbmV0eXBlPSJzb2xpZCIsCiAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDAuMikpCnBfQUtDX2NvbXBhcmUgLyBwX1VLQ19jb21wYXJlCmBgYApOb3Nld29yayBhbmQgQml0ZXdvcms6IAoKYGBge3J9CnBfYml0ZV9jb21wYXJlIDwtICBnZ3Bsb3QobWVhbnNoYXBlLmJpdGVbWyJwbG90dGluZyJdXSwgYWVzKGxhbmRtYXJrLCB2YWx1ZSwgY29sb3IgPSBjb21wYXJpc29uKSkgKyAKICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25faml0dGVyKHdpZHRoID0gMC4xKSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBtZWFuc2hhcGUuYml0ZVtbImN1dG9mZiJdXSRtZWFuKzIqbWVhbnNoYXBlLmJpdGVbWyJjdXRvZmYiXV0kc2QsCiAgICAgICAgICAgICBsdHkgPSAyKSArCiAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMSwgeGVuZCA9IDIwLCB5ID0gMC4wMDcsIHllbmQgPSAwLjAwNywKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0gInJlZCIpICsKICBhbm5vdGF0ZSgidGV4dCIsIHggPSAxMCwgeSA9IDAuMDA2NSwgbGFiZWwgPSAiRG9yc2FsIGZlYXR1cmVzIiwgY29sb3IgPSAicmVkIikgKwogICAgYW5ub3RhdGUoInNlZ21lbnQiLCB4ID0gMjEsIHhlbmQgPSAzOCwgeSA9IDAuMDA3LCB5ZW5kID0gMC4wMDcsCiAgICAgICAgICAgICBsdHkgPSAxLCBjb2xvciA9ICJibHVlIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDMwLCB5ID0gMC4wMDY1LCBsYWJlbCA9ICJWZW50cmFsIGZlYXR1cmVzIiwgY29sb3IgPSAiYmx1ZSIpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobmFtZSA9ICJHcm91cC1ncm91cFxuY29tcGFyaXNvbiIpICsKICB5bGFiKCJHcm91cC1tZWFuIGxhbmRtYXJrXG5kaXN0YW5jZSBzcXVhcmVkIikgKwogIGdndGl0bGUoIkJpdGUtd29yayBncm91cCBjb21wYXJpc29ucyIpICsKICB0aGVtZV9idygpCnBfbm9zZV9jb21wYXJlIDwtIGdncGxvdChtZWFuc2hhcGUubm9zZVtbInBsb3R0aW5nIl1dLCBhZXMobGFuZG1hcmssIHZhbHVlLCBjb2xvciA9IGNvbXBhcmlzb24pKSArIAogIGdlb21fcG9pbnQocG9zaXRpb24gPSBwb3NpdGlvbl9qaXR0ZXIod2lkdGggPSAwLjEpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1lYW5zaGFwZS5ub3NlW1siY3V0b2ZmIl1dJG1lYW4rMiptZWFuc2hhcGUubm9zZVtbImN1dG9mZiJdXSRzZCwKICAgICAgICAgICAgIGx0eSA9IDIpICsKICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAxLCB4ZW5kID0gMjAsIHkgPSAwLjAwNywgeWVuZCA9IDAuMDA3LAogICAgICAgICAgICAgbHR5ID0gMSwgY29sb3IgPSAicmVkIikgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEwLCB5ID0gMC4wMDY1LCBsYWJlbCA9ICJEb3JzYWwgZmVhdHVyZXMiLCBjb2xvciA9ICJyZWQiKSArCiAgICBhbm5vdGF0ZSgic2VnbWVudCIsIHggPSAyMSwgeGVuZCA9IDM4LCB5ID0gMC4wMDcsIHllbmQgPSAwLjAwNywKICAgICAgICAgICAgIGx0eSA9IDEsIGNvbG9yID0gImJsdWUiKSArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gMzAsIHkgPSAwLjAwNjUsIGxhYmVsID0gIlZlbnRyYWwgZmVhdHVyZXMiLCBjb2xvciA9ICJibHVlIikgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChuYW1lID0gIkdyb3VwLWdyb3VwXG5jb21wYXJpc29uIikgKwogIHlsYWIoIkdyb3VwLW1lYW4gbGFuZG1hcmtcbmRpc3RhbmNlIHNxdWFyZWQiKSArCiAgZ2d0aXRsZSgiU2NlbnQtd29yayBncm91cCBjb21wYXJpc29ucyIpICsKICB0aGVtZV9idygpCgpwX2JpdGVfY29tcGFyZSAvIHBfbm9zZV9jb21wYXJlICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKQpgYGAKCiMjIE1BTk9WQSAKCmBgYHtyfQptYW5fVUtDIDwtIG1hbm92YShhcy5tYXRyaXgoc2t1bGxzWywxOjVdKX5za3VsbHMkVUtDKQpzdW1tYXJ5LmFvdihtYW5fVUtDLCB0ZXN0PSJQaWxsYWkiKQoKI3VzaW5nIEdlb21vcnBoIGFuZCBkb2luZyBwYWlyd2lzZSBjb21wYXJpc29ucwptYW5VS0M8LXByb2NELmxtKHNoYXBlIH4gVUtDLCBkYXRhID0gcmVmb3JnZSwgUlJQUCA9IFRSVUUpCm1hbkFLQzwtcHJvY0QubG0oc2hhcGUgfiBBS0MsIGRhdGEgPSByZWZvcmdlLCBSUlBQID0gVFJVRSkKbWFuQml0ZTwtcHJvY0QubG0oc2hhcGUgfiBiaXRld29yaywgZGF0YSA9IHJlZm9yZ2UsIFJSUFAgPSBUUlVFKQptYW5TY2VudDwtcHJvY0QubG0oc2hhcGUgfiBzY2VudHdvcmssIGRhdGEgPSByZWZvcmdlLCBSUlBQID0gVFJVRSkKCmhvbGQudWtjPC1tYW5vdmEobWFuVUtDKQpob2xkLmFrYzwtbWFub3ZhKG1hbkFLQykKaG9sZC5iaXRlPC1tYW5vdmEobWFuQml0ZSkKaG9sZC5ub3NlPC1tYW5vdmEobWFuU2NlbnQpCgp1a2NwYWlyczwtcGFpcndpc2UobWFuVUtDLCBncm91cHMgPSByZWZvcmdlJFVLQykKYWtjcGFpcnM8LXBhaXJ3aXNlKG1hbkFLQywgZ3JvdXBzID0gcmVmb3JnZSRBS0MpCmJpdGVwYWlyczwtcGFpcndpc2UobWFuQml0ZSwgZ3JvdXBzID0gcmVmb3JnZSRiaXRld29yaykKbm9zZXBhaXJzPC1wYWlyd2lzZShtYW5TY2VudCwgZ3JvdXBzID0gcmVmb3JnZSRzY2VudHdvcmspCgoKcHZhbHMuVUtDPC1zdW1tYXJ5KHVrY3BhaXJzLCB0ZXN0LnR5cGU9ICJkaXN0IiwgY29uZmlkZW5jZSA9IDAuOTUsIHN0YXQudGFibGUgPSBUUlVFKQpwdmFscy5VS0M8LXB2YWxzLlVLQyRwYWlyd2lzZS50YWJsZXMkUFtsb3dlci50cmkocHZhbHMuVUtDJHBhaXJ3aXNlLnRhYmxlcyRQKV0KcHZhbHMuQUtDPC1zdW1tYXJ5KGFrY3BhaXJzLCB0ZXN0LnR5cGU9ICJkaXN0IiwgY29uZmlkZW5jZSA9IDAuOTUsIHN0YXQudGFibGUgPSBUUlVFKQpwdmFscy5BS0M8LXB2YWxzLkFLQyRwYWlyd2lzZS50YWJsZXMkUFtsb3dlci50cmkocHZhbHMuQUtDJHBhaXJ3aXNlLnRhYmxlcyRQKV0KcHZhbHMuYml0ZTwtc3VtbWFyeShiaXRlcGFpcnMsIHRlc3QudHlwZT0gImRpc3QiLCBjb25maWRlbmNlID0gMC45NSwgc3RhdC50YWJsZSA9IFRSVUUpCnB2YWxzLmJpdGU8LXB2YWxzLmJpdGUkcGFpcndpc2UudGFibGVzJFBbbG93ZXIudHJpKHB2YWxzLmJpdGUkcGFpcndpc2UudGFibGVzJFApXQpwdmFscy5ub3NlPC1zdW1tYXJ5KG5vc2VwYWlycywgdGVzdC50eXBlPSAiZGlzdCIsIGNvbmZpZGVuY2UgPSAwLjk1LCBzdGF0LnRhYmxlID0gVFJVRSkKcHZhbHMubm9zZTwtcHZhbHMubm9zZSRwYWlyd2lzZS50YWJsZXMkUFtsb3dlci50cmkocHZhbHMubm9zZSRwYWlyd2lzZS50YWJsZXMkUCldCgpwdmFscy51a2MuYWRqdXN0ZWQ8LXAuYWRqdXN0KHB2YWxzLlVLQywgbWV0aG9kID0gImJvbmYiKQpwdmFscy5ha2MuYWRqdXN0ZWQ8LXAuYWRqdXN0KHB2YWxzLkFLQywgbWV0aG9kID0gImJvbmYiKQpwdmFscy5iaXRlLmFkanVzdGVkPC1wLmFkanVzdChwdmFscy5iaXRlLCBtZXRob2QgPSAiYm9uZiIpCnB2YWxzLm5vc2UuYWRqdXN0ZWQ8LXAuYWRqdXN0KHB2YWxzLm5vc2UsIG1ldGhvZCA9ICJib25mIikKYGBgCgpgYGB7cn0KbGRhX1VLQyA8LSBNQVNTOjpsZGEoc2t1bGxzJFVLQ35hcy5tYXRyaXgoc2t1bGxzWywxOjVdKSkKbGRhX1VLQwpgYGAKCiMjIFRlc3Rpbmcgb2YgRnVuY3Rpb25hbCBHcm91cHMKCmBgYHtyIGZ4bi1ncm91cGluZ3MsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9My41LCBmaWcuY2FwPSJCaXRld29yayBhbmQgc2NlbnQgd29yay4ifQpmeG5fZ3JvdXBfcGFsZXR0ZSA8LSBtdW5jaF9wYWxldHRlKCJNdXJkZXJlciIsOClbYygxOjMsNSldCnBiaXRlIDwtIGdncGxvdChTbGljZXJNb3JwaC5yZXBjLCBhZXMoUEMuMSwgUEMuMiwgY29sb3IgPSBCaXRld29yaywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IEJpdGV3b3JrLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gQml0ZXdvcmspKSArIAogIGdlb21fcG9pbnQoKSArCiAgc3RhdF9lbGxpcHNlKGdlb20gPSAicG9seWdvbiIsIGxldmVsID0gMC45NSwgYWxwaGEgPSAwLjEpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYygyMSwgMjMsIDAsIDIpLCBuYW1lID0gIiAiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGZ4bl9ncm91cF9wYWxldHRlLCBuYW1lID0gIiAiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZ4bl9ncm91cF9wYWxldHRlLCBuYW1lID0gIiAiKSArCiAgeGxhYigiUEMgMSAoVmFyID0gNTAuMiUpIikgKyAKICB5bGFiKCIgIikgKwogIGdndGl0bGUoIlNlbGVjdGVkIGZvciBiaXRlIHdvcmsiKSArCiAgdGhlbWVfYncoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCnBzY2VudCA8LSBnZ3Bsb3QoU2xpY2VyTW9ycGgucmVwYywgYWVzKFBDLjEsIFBDLjIsIGNvbG9yID0gTm9zZXdvcmssIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOb3Nld29yaywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IE5vc2V3b3JrKSkgKyAKICBnZW9tX3BvaW50KCkgKwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBsZXZlbCA9IDAuOTUsIGFscGhhID0gMC4xKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjEsIDIzLCAwLCAyKSwgbmFtZSA9ICIgIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBmeG5fZ3JvdXBfcGFsZXR0ZSwgbmFtZSA9ICIgIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBmeG5fZ3JvdXBfcGFsZXR0ZSwgbmFtZSA9ICIgIikgKwogIHhsYWIoIlBDIDEgKFZhciA9IDUwLjIlKSIpICsgCiAgeWxhYigiUEMgMiAoVmFyID0gMTMuMyUpIikgKwogIGdndGl0bGUoIlNlbGVjdGVkIGZvciBzY2VudCB3b3JrIikgKwogIHRoZW1lX2J3KCkgCnBzY2VudCArIHBiaXRlICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCgpgYGAKCmBgYHtyIGJpdGUtZm9yY2UsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmJpdGVfZGF0YSA8LSBkYXRhLmZyYW1lKCJCcmVlZCIgPSBTbGljZXJNb3JwaC5yZXBjJEJyZWVkLCAKICAgICAgICAgICAgICAgICAgICAgICAgIkJpdGV3b3JrIiA9IFNsaWNlck1vcnBoLnJlcGMkQml0ZXdvcmssIAogICAgICAgICAgICAgICAgICAgICAgICAiRG9tZXN0aWMiID0gU2xpY2VyTW9ycGgucmVwYyRkb21lc3RpYywgCiAgICAgICAgICAgICAgICAgICAgICAgICJVS0MiID0gU2xpY2VyTW9ycGgucmVwYyRVS0MsIAogICAgICAgICAgICAgICAgICAgICAgICAiQUtDIiA9IFNsaWNlck1vcnBoLnJlcGMkQUtDLAogICAgICAgICAgICAgICAgICAgICAgICAiY2FuaW5lIiA9IFNsaWNlck1vcnBoLnJlcGMkYmZxX2NhbmluZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICJjYXJuYXNzaWFsIiA9IFNsaWNlck1vcnBoLnJlcGMkYmZxX2Nhcm5hc3NpYWwpCmJpdGVfZGF0YV9sb25nIDwtIHBpdm90X2xvbmdlcihiaXRlX2RhdGEsIGNvbHMgPSBjKCJjYW5pbmUiLCJjYXJuYXNzaWFsIikpCnBmb3JjZSA8LSBnZ3Bsb3QoYml0ZV9kYXRhX2xvbmcsIGFlcyhCaXRld29yaywgdmFsdWUsIGZpbGwgPSBuYW1lKSkgKyAKICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjYsIG91dGxpZXIuc2hhcGUgPSBOQSkgKyAKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBwb3NpdGlvbiA9IHBvc2l0aW9uX2ppdHRlcmRvZGdlKGppdHRlci53aWR0aCA9IDAuMiksIHNoYXBlID0gMjEpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBtdW5jaF9wYWxldHRlKCJZZWxsb3dMb2ciLDIpLCBuYW1lID0gIiAiKSArCiAgeWxhYigiQml0ZS1mb3JjZSBxdW90aWVudCIpICsgeGxhYigiU2VsZWN0ZWQgZm9yIGJpdGUgd29yayIpICsKICB0aGVtZV9idygpCnBmb3JjZQpgYGAKCmBgYHtyIGJpdGUtZm9yY2Utc3RhdHN9CmJpdGVfeWVzX2NhbmluZSA8LSBiaXRlX2RhdGEkY2FuaW5lW2JpdGVfZGF0YSRCaXRld29yayA9PSAieWVzIl0KYml0ZV9ub19jYW5pbmUgPC0gYml0ZV9kYXRhJGNhbmluZVtiaXRlX2RhdGEkQml0ZXdvcmsgPT0gIm5vIl0Kc2hhcGlyby50ZXN0KGJpdGVfeWVzX2NhbmluZSkKc2hhcGlyby50ZXN0KGJpdGVfbm9fY2FuaW5lKQp0LnRlc3QoYml0ZV95ZXNfY2FuaW5lLCBiaXRlX25vX2NhbmluZSkKCmJpdGVfeWVzX2Nhcm5hc3NpYWwgPC0gYml0ZV9kYXRhJGNhcm5hc3NpYWxbYml0ZV9kYXRhJEJpdGV3b3JrID09ICJ5ZXMiXQpiaXRlX25vX2Nhcm5hc3NpYWwgPC0gYml0ZV9kYXRhJGNhcm5hc3NpYWxbYml0ZV9kYXRhJEJpdGV3b3JrID09ICJubyJdCnNoYXBpcm8udGVzdChiaXRlX3llc19jYXJuYXNzaWFsKQpzaGFwaXJvLnRlc3QoYml0ZV9ub19jYXJuYXNzaWFsKQp0LnRlc3QoYml0ZV95ZXNfY2FybmFzc2lhbCwgYml0ZV9ub19jYXJuYXNzaWFsKQoKYml0ZV9uYXR1cmFsX2NhbmluZSA8LSBiaXRlX2RhdGEkY2FuaW5lW2JpdGVfZGF0YSRCaXRld29yayA9PSAiTkFUVVJBTCJdCnNoYXBpcm8udGVzdChiaXRlX25hdHVyYWxfY2FuaW5lKQpiaXRlX2ZveF9jYW5pbmUgPC0gYml0ZV9kYXRhJGNhbmluZVtiaXRlX2RhdGEkQml0ZXdvcmsgPT0gIkZPWCJdCnNoYXBpcm8udGVzdChiaXRlX2ZveF9jYW5pbmUpCgp0LnRlc3QoYml0ZV95ZXNfY2FuaW5lLCBiaXRlX25hdHVyYWxfY2FuaW5lKQp0LnRlc3QoYml0ZV95ZXNfY2FuaW5lLCBiaXRlX2ZveF9jYW5pbmUpCmBgYAoKIyMgRm9yIHRoZSBwYXBlcjogCgpGaWd1cmUgMjogay1tZWFucyBjbHVzdGVyIG9uIFBDIHBsb3QKCmBgYHtyIGZpZy0yLCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSJBLiBLLW1lYW5zIGNsdXN0ZXJpbmcgcGxvdC4iLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD05LjZ9CnBrbWVhbiArIHhsaW0oLTAuMjIsIDAuMjUpICsgdGhlbWUobGVnZW5kLm1hcmdpbj1tYXJnaW4oYygwLDAsMCwwKSkpCmdnc2F2ZSgiLi9yZXN1bHRzL2ZpZ3VyZXMvZmlndXJlMi5wZGYiLCBoZWlnaHQgPSA0LCB3aWR0aCA9IDkuNikKYGBgCgpGaWd1cmUgMzogVUtDIGNvbXBhcmlzb24gZmlndXJlCgpgYGB7ciBmaWctMywgZWNobz1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmNhcD0iQS4gSy1tZWFucyBjbHVzdGVyaW5nIHBsb3QuIEIuIEFLQyBncm91cGluZ3MuIEMuIFVLQyBncm91cGluZ3MuIiwgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD0xMn0KZGVzaWduMiA8LSAKICAiQUFBQQogICBBQUFBCiAgIEJCQkIiCihwVUtDICkgLyAKICBmcmVlKHBfVUtDX2NvbXBhcmUgKyBnZ3RpdGxlKCIgIikgKyAgCiAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSwgdmp1c3QgPSAwLjUpKSkgKyAKICBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICJBIikgKwogIHBsb3RfbGF5b3V0KGRlc2lnbiA9IGRlc2lnbjIpICYgdGhlbWUobGVnZW5kLm1hcmdpbj1tYXJnaW4oYygwLDAsMCwwKSkpCiAgCmdnc2F2ZSgiLi9yZXN1bHRzL2ZpZ3VyZXMvZmlndXJlMy5wZGYiLCBoZWlnaHQgPSAzLjQ1KjIsIHdpZHRoID0gMy4yNSoyKQpgYGAKCkZpZ3VyZSA0OiBBS0MgY29tcGFyaXNvbiBmaWd1cmUKCmBgYHtyIGZpZy00LCBlY2hvPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuY2FwPSIgQUtDIGdyb3VwaW5ncy4gIiwgZmlnLmhlaWdodD03LjUsIGZpZy53aWR0aD0xMn0KZGVzaWduMiA8LSAKICAiQUFBQQogICBBQUFBCiAgIEJCQkIiCnBBS0MgLyBmcmVlKHBfQUtDX2NvbXBhcmUgKyBnZ3RpdGxlKCIgIikgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkpKwogIHBsb3RfYW5ub3RhdGlvbih0YWdfbGV2ZWxzID0gIkEiKSArCiAgcGxvdF9sYXlvdXQoZGVzaWduID0gZGVzaWduMikgJiB0aGVtZShsZWdlbmQubWFyZ2luPW1hcmdpbihjKDAsMCwwLDApKSkKZ2dzYXZlKCIuL3Jlc3VsdHMvZmlndXJlcy9maWd1cmU0LnBkZiIsIGhlaWdodCA9IDMuNDUqMiwgd2lkdGggPSAzLjI1KjIpCmBgYAoKRmlndXJlIDU6IHRhc2stc3BlY2lmaWMgZ3JvdXAgcmVzdWx0cwoKYGBge3IgZmlnLTUsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5jYXA9IkEuIEstbWVhbnMgY2x1c3RlcmluZyBwbG90LiBCLiBBS0MgZ3JvdXBpbmdzLiBDLiBVS0MgZ3JvdXBpbmdzLiIsIGZpZy5oZWlnaHQ9Ny41LCBmaWcud2lkdGg9MTJ9CmRlc2lnbjIgPC0gCiAgIkFBQUEKICAgQkJCQgogICBCQkJCCiAgIENDQ0MiCihwc2NlbnQgKyBwYml0ZSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikgJiB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKSkgLyAKICAocF9ub3NlX2NvbXBhcmUgL3BfYml0ZV9jb21wYXJlICsgCiAgICAgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAwLjUsIGhqdXN0PTEpKSkgLyAKICBwZm9yY2UgICsgCiAgcGxvdF9hbm5vdGF0aW9uKHRhZ19sZXZlbHMgPSAiQSIpICsKICBwbG90X2xheW91dChkZXNpZ24gPSBkZXNpZ24yKSAmIHRoZW1lKGxlZ2VuZC5tYXJnaW49bWFyZ2luKGMoMCwwLDAsMCkpKQpnZ3NhdmUoIi4vcmVzdWx0cy9maWd1cmVzL2ZpZ3VyZTUucGRmIiwgd2lkdGggPSA5LjYsIGhlaWdodCA9IDExLjUyKQpgYGAKCgoKCgoK