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