Agenda

Announcements

  • MDSR Ch 9 programming notebook assigned
  • MDSR Ch 8 & 9 Exercises assigned
  • midterm exam
    • mix of in-class and take-home
    • in-class portion Wed 2/27
    • take-home portion due Fri 3/1 at 11:59pm
    • class will not meet on Friday 3/1

MDSR Ch 9 Errata / Tips

  • Some sections don’t require programming, but please still include the headers for navigation purposes
  • p. 206: the destfile argument probably won’t work for you as written. You can
    1. (recommended) create a folder called “data” in the same directory as your .Rmd file
    2. delete data/ and use destfile = fueleconomy.zip instead. You’ll need to update subsequent functions on p. 206 and 207 in kind when attempting to access files in a subdirectory called data
  • Section 9.2.2: my results aren’t identical to those in the book… maybe I made a mistake or maybe the data has changed. Namely, the clusters are similar but don’t exactly match on close inspection.

Statistical Learning (recall)

Q: What are some differences between Supervised and Unsupervised Learning?

Statistical Learning (recall)

Unsupervised Learning

Challenge of Unsupervised Learning

Some Examples

Principal Components Analysis (PCA)

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 6.14

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 6.14

US Arrests by State

data("USArrests")
head(USArrests)

PCA: US Arrest Data

First two principal components

  • Q: How do PC1 and PC2 associate with our four variables?
  • Note: See what happens if we DON’T standardize variables…
USArrests_pca <- USArrests %>%
  prcomp(scale = TRUE)  # standardize the variables
# the result is a list object
str(USArrests_pca)
List of 5
 $ sdev    : num [1:4] 1.575 0.995 0.597 0.416
 $ rotation: num [1:4, 1:4] -0.536 -0.583 -0.278 -0.543 0.418 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
  .. ..$ : chr [1:4] "PC1" "PC2" "PC3" "PC4"
 $ center  : Named num [1:4] 7.79 170.76 65.54 21.23
  ..- attr(*, "names")= chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
 $ scale   : Named num [1:4] 4.36 83.34 14.47 9.37
  ..- attr(*, "names")= chr [1:4] "Murder" "Assault" "UrbanPop" "Rape"
 $ x       : num [1:50, 1:4] -0.976 -1.931 -1.745 0.14 -2.499 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:50] "Alabama" "Alaska" "Arizona" "Arkansas" ...
  .. ..$ : chr [1:4] "PC1" "PC2" "PC3" "PC4"
 - attr(*, "class")= chr "prcomp"
# first two principal components
(-1) * USArrests_pca$rotation[, 1:2] %>% round(2)
          PC1   PC2
Murder   0.54 -0.42
Assault  0.58 -0.19
UrbanPop 0.28  0.87
Rape     0.54  0.17
# plot of the first two principal components
USArrests_pca$x %>%
  as.data.frame() %>%  # `ggplot2` expects a data frame object
  rownames_to_column() %>%
  ggplot(aes(x = -PC1, y = -PC2)) + 
  geom_text(aes(label = rowname), size = 3) + 
  xlab("Best Vector from PCA (approx. Violent Crime)") + 
  ylab("2nd Best Vector from PCA (approx. Urbanization)") + 
  ggtitle("Two-dimensional representation of US Arrests by State")

Proportion of variance explained

By Kevin Lenz (talk contribs) - Own work, CC BY-SA 2.5, https://commons.wikimedia.org/w/index.php?curid=1239742

By Kevin Lenz (talk contribs) - Own work, CC BY-SA 2.5, https://commons.wikimedia.org/w/index.php?curid=1239742

Scree Plot (US Arrest Data)

USArrests_pve <- 
  data.frame(sd = USArrests_pca$sdev) %>%
  rownames_to_column() %>%
  mutate(rowname = parse_number(rowname), 
         totalVar = sum(USArrests_pca$sdev^2), 
         pve = 100 * sd^2 / totalVar, 
         cusum = cumsum(pve))

# scree plot
USArrests_pve %>%
  ggplot(aes(x = rowname, y = pve)) + 
  geom_line(type = 3) + 
  xlab("Principal Component") + 
  ylab("Proportion of Variance Explained") + 
  ggtitle("Scree Plot of Principal Components for US Arrests Data") 

  
# cumulative PVE plot
USArrests_pve %>%
  ggplot(aes(x = rowname, y = cusum)) + 
  geom_line(type = 3) + 
  xlab("Principal Component") + 
  ylab("Proportion of Variance Explained") + 
  ggtitle("Cumulative Proportion of Variance Explained for US Arrests Data") 

NCI60 Data Example

Inspecting the data

require(ISLR)
data("NCI60")
cancerLabels <- NCI60$labs 
# recode immortalized cell lines
cancerLabels <- ifelse(test = grepl(pattern = "MCF7", x = cancerLabels),
                       yes = "BREAST", no = cancerLabels)
cancerLabels <- ifelse(test = grepl(pattern = "K562", x = cancerLabels),
                       yes = "LEUKEMIA", no = cancerLabels)
nciData <- NCI60$data 
# dimensions of the data
nrow(nciData)  # cases
[1] 64
ncol(nciData)  # variables
[1] 6830
# inspect NCI cancer labels
head(cancerLabels)
[1] "CNS"    "CNS"    "CNS"    "RENAL"  "BREAST" "CNS"   
# distribution of cancer cell lines available
tally(cancerLabels ~ 1)
            1
cancerLabels 1
    BREAST   9
    CNS      5
    COLON    7
    LEUKEMIA 8
    MELANOMA 8
    NSCLC    9
    OVARIAN  6
    PROSTATE 2
    RENAL    9
    UNKNOWN  1

Principal components analysis of NCI60 data

# perform pca on scaled genes
NCI_pca <- nciData %>%
  prcomp(scale = TRUE)  
# the result is a list object
str(NCI_pca)
List of 5
 $ sdev    : num [1:64] 27.9 21.5 19.8 17 16 ...
 $ rotation: num [1:6830, 1:64] -0.01068 -0.00231 -0.00588 0.00328 -0.00768 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:6830] "1" "2" "3" "4" ...
  .. ..$ : chr [1:64] "PC1" "PC2" "PC3" "PC4" ...
 $ center  : Named num [1:6830] -0.0191 -0.0278 -0.0199 -0.3287 0.0261 ...
  ..- attr(*, "names")= chr [1:6830] "1" "2" "3" "4" ...
 $ scale   : Named num [1:6830] 0.441 0.757 0.433 1.092 0.485 ...
  ..- attr(*, "names")= chr [1:6830] "1" "2" "3" "4" ...
 $ x       : num [1:64, 1:64] -19.7 -22.9 -27.2 -42.5 -55 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:64] "V1" "V2" "V3" "V4" ...
  .. ..$ : chr [1:64] "PC1" "PC2" "PC3" "PC4" ...
 - attr(*, "class")= chr "prcomp"

First few principal components of NCI60 data

# plot PC1 vs PC2
NCI_pca$x %>%
  as.data.frame() %>%  # `ggplot2` expects a data frame object
  ggplot(aes(x = PC1, y = PC2)) + 
  geom_point(aes(color = cancerLabels), size = 3) + 
  xlab("Best Vector from PCA") + 
  ylab("Second Best Vector from PCA") + 
  ggtitle("Two-dimensional representation of 6830 Genes (colored by actual cancer type)") 

# plot of PC1 vs PC3
NCI_pca$x %>%
  as.data.frame() %>%  # `ggplot2` expects a data frame object
  ggplot(aes(x = PC1, y = PC3)) + 
  geom_point(aes(color = cancerLabels), size = 3) + 
  xlab("Best Vector from PCA") + 
  ylab("Third Best Vector from PCA") +
  ggtitle("Two-dimensional representation of 6830 Genes (colored by actual cancer type)")

# SD and variance explained by each PC
summary(NCI_pca)
Importance of components:
                          PC1     PC2     PC3     PC4     PC5     PC6     PC7     PC8     PC9    PC10    PC11
Standard deviation     27.853 21.4814 19.8205 17.0326 15.9718 15.7211 14.4715 13.5443 13.1440 12.7386 12.6867
Proportion of Variance  0.114  0.0676  0.0575  0.0425  0.0374  0.0362  0.0307  0.0269  0.0253  0.0238  0.0236
Cumulative Proportion   0.114  0.1812  0.2387  0.2812  0.3185  0.3547  0.3853  0.4122  0.4375  0.4613  0.4848
                          PC12    PC13    PC14    PC15    PC16    PC17    PC18    PC19    PC20    PC21    PC22
Standard deviation     12.1577 11.8302 11.6255 11.4378 11.0005 10.6567 10.4888 10.4352 10.3219 10.1461 10.0544
Proportion of Variance  0.0216  0.0205  0.0198  0.0192  0.0177  0.0166  0.0161  0.0159  0.0156  0.0151  0.0148
Cumulative Proportion   0.5065  0.5270  0.5467  0.5659  0.5836  0.6002  0.6163  0.6323  0.6479  0.6630  0.6778
                         PC23   PC24   PC25   PC26   PC27   PC28   PC29   PC30   PC31   PC32   PC33    PC34
Standard deviation     9.9027 9.6477 9.5076 9.3325 9.2732 9.0900 8.9812 8.7500 8.5996 8.4474 8.3730 8.21579
Proportion of Variance 0.0144 0.0136 0.0132 0.0127 0.0126 0.0121 0.0118 0.0112 0.0108 0.0104 0.0103 0.00988
Cumulative Proportion  0.6921 0.7057 0.7190 0.7317 0.7443 0.7564 0.7682 0.7794 0.7903 0.8007 0.8110 0.82087
                          PC35    PC36    PC37    PC38    PC39    PC40    PC41   PC42    PC43   PC44    PC45
Standard deviation     8.15731 7.97465 7.90446 7.82127 7.72156 7.58603 7.45619 7.3444 7.10449 7.0131 6.95839
Proportion of Variance 0.00974 0.00931 0.00915 0.00896 0.00873 0.00843 0.00814 0.0079 0.00739 0.0072 0.00709
Cumulative Proportion  0.83061 0.83992 0.84907 0.85803 0.86676 0.87518 0.88332 0.8912 0.89861 0.9058 0.91290
                         PC46    PC47    PC48    PC49    PC50    PC51    PC52    PC53    PC54    PC55    PC56
Standard deviation     6.8663 6.80744 6.64763 6.61607 6.40793 6.21984 6.20326 6.06706 5.91805 5.91233 5.73539
Proportion of Variance 0.0069 0.00678 0.00647 0.00641 0.00601 0.00566 0.00563 0.00539 0.00513 0.00512 0.00482
Cumulative Proportion  0.9198 0.92659 0.93306 0.93947 0.94548 0.95114 0.95678 0.96216 0.96729 0.97241 0.97723
                          PC57   PC58    PC59    PC60    PC61    PC62    PC63     PC64
Standard deviation     5.47261 5.2921 5.02117 4.68398 4.17567 4.08212 4.04124 2.15e-14
Proportion of Variance 0.00438 0.0041 0.00369 0.00321 0.00255 0.00244 0.00239 0.00e+00
Cumulative Proportion  0.98161 0.9857 0.98940 0.99262 0.99517 0.99761 1.00000 1.00e+00
# proportion of variance explained (PVE) of each PC
NCI_pve <- 
  data.frame(sd = NCI_pca$sdev) %>%
  rownames_to_column() %>%
  mutate(rowname = parse_number(rowname), 
         totalVar = sum(NCI_pca$sdev^2), 
         pve = 100 * sd^2 / totalVar, 
         cusum = cumsum(pve))
# scree plot
NCI_pve %>%
  ggplot(aes(x = rowname, y = pve)) + 
  geom_line(type = 3) + 
  xlab("Principal Component") + 
  ylab("Proportion of Variance Explained") + 
  ggtitle("Scree Plot of Principal Components for NCI60 Data") 
Ignoring unknown parameters: type

  
# cumulative PVE plot
NCI_pve %>%
  ggplot(aes(x = rowname, y = cusum)) + 
  geom_line(type = 3) + 
  xlab("Principal Component") + 
  ylab("Proportion of Variance Explained") + 
  ggtitle("Cumulative Proportion of Variance Explained for NCI60 Data") 
Ignoring unknown parameters: type

Clustering

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.10

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.10

Hierarchical clustering

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.11

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.11

Distance & Linkage:

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.11

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.11

Hierarchical clustering

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.10

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.10

Cluster analysis of NCI60 data

Hierarchical Clustering
# scale the data (centered with SD 1)
NCI_std <-
  scale(nciData) %>%
  as.data.frame()
# the variables have inherited some unfortunate names (just column number)
favstats(~ `1`, data = NCI_std)
favstats(~ `2`, data = NCI_std)
NCI_dist <- dist(NCI_std)
# # plot dedrogram (average linkage)
# NCI_dist %>%
#   hclust(method = "average") %>%
#   plot(cex = 0.9, labels = cancerLabels, main = "NCI60 Dendogram with Average Linkage")
# construct dendogram (complete linkage)
NCI_dendo <-
  NCI_dist %>%
  hclust(method = "complete")
# print dendogram info (distance & method)
print(NCI_dendo)

Call:
hclust(d = ., method = "complete")

Cluster method   : complete 
Distance         : euclidean 
Number of objects: 64 
# plot dedrogram (complete linkage)
NCI_dendo %>%
  plot(cex = 0.9, labels = cancerLabels, lwd = 2,
       main = "NCI60 Dendogram with Complete Linkage")

Cut Dendogram to define clusters
  • Complete dendogram can be used to produce clusters
  • establish cut point based on dissimilarity index (vertical axis)
  • software can choose cut based on requested number of clusters
  • Q: Did we learn anything from our clusters?
# Cut dendogram to produce clusters
NCI_dendo %>%
  plot(labels = cancerLabels, lwd = 2,
       main = "NCI60 Dendogram with Complete Linkage (5 clusters)") %>%
  abline(h = 135, col = "red", lwd = 3)

# Cut dendogram--Hierarchical clusters
NCI_DendoClusters <- cutree(tree = NCI_dendo, k = 5)
# clustering patterns (Leukemia & melanoma; not so much breast)
tally(cancerLabels ~ NCI_DendoClusters)
            NCI_DendoClusters
cancerLabels 1 2 3 4 5
    BREAST   0 3 0 4 2
    CNS      3 2 0 0 0
    COLON    2 0 0 5 0
    LEUKEMIA 0 0 8 0 0
    MELANOMA 2 0 0 0 6
    NSCLC    7 1 0 0 1
    OVARIAN  6 0 0 0 0
    PROSTATE 2 0 0 0 0
    RENAL    8 1 0 0 0
    UNKNOWN  1 0 0 0 0

K-means clustering

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.6

image credit: James et al (2013) http://www-bcf.usc.edu/~gareth/ISL/ Fig 10.6

K-means Clustering

set.seed(2)
# perform kmeans clustering (k = 5 clusters)
NCI_kmean <-
  NCI_std %>%
  kmeans(centers = 5, nstart = 20)
# what are we working with
str(NCI_kmean)
List of 9
 $ cluster     : Named int [1:64] 2 2 2 2 2 2 2 2 2 2 ...
  ..- attr(*, "names")= chr [1:64] "V1" "V2" "V3" "V4" ...
 $ centers     : num [1:5, 1:6830] 0.0205 0.2232 0.5643 -0.2946 -0.4695 ...
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : chr [1:5] "1" "2" "3" "4" ...
  .. ..$ : chr [1:6830] "1" "2" "3" "4" ...
 $ totss       : num 430290
 $ withinss    : num [1:5] 37150 154545 10326 82235 44071
 $ tot.withinss: num 328326
 $ betweenss   : num 101964
 $ size        : int [1:5] 9 27 4 16 8
 $ iter        : int 3
 $ ifault      : int 0
 - attr(*, "class")= chr "kmeans"
# compare Hierarchical Clusters with K-Means Clusters
NCI_KMeanClusters <- NCI_kmean$cluster
# both methods match for one cluster, but others are noisier
tally(NCI_DendoClusters ~ NCI_KMeanClusters)
                 NCI_KMeanClusters
NCI_DendoClusters  1  2  3  4  5
                1  1 20  0 10  0
                2  0  7  0  0  0
                3  0  0  0  0  8
                4  0  0  4  5  0
                5  8  0  0  1  0

Hierarchical Clustering on first 7 principal components

NCI_pca_hcluster <-
  NCI_pca$x[, 1:7] %>%
  dist() %>%
  hclust()
# plot
NCI_pca_hcluster %>%
  plot(labels = cancerLabels, lwd = 2,
       main = "Hierarcical Clustering on First Seven Principal Components")

tally(cancerLabels ~ cutree(NCI_pca_hcluster, k = 7))
            cutree(NCI_pca_hcluster, k = 7)
cancerLabels 1 2 3 4 5 6 7
    BREAST   2 1 0 0 0 4 2
    CNS      4 1 0 0 0 0 0
    COLON    0 0 7 0 0 0 0
    LEUKEMIA 0 0 0 6 2 0 0
    MELANOMA 1 0 0 0 0 0 7
    NSCLC    5 1 3 0 0 0 0
    OVARIAN  5 1 0 0 0 0 0
    PROSTATE 2 0 0 0 0 0 0
    RENAL    8 1 0 0 0 0 0
    UNKNOWN  0 1 0 0 0 0 0

Practical issues in clustering

Recommendations

  • experiment with different choices of linkage, standardized/not, etc, and look for patterns or structures that consistently emerge
  • cluster random subsets of the data to get sense of robustness to outliers
  • most importantly, be careful when reporting results of cluster analysis
    • not absolute truth about the data (much less the population)
    • it’s more of a starting point to generate scientific questions for study on (ideally) independent data
LS0tCnRpdGxlOiAiVW5zdXBlcnZpc2VkIExlYXJuaW5nIgpzdWJ0aXRsZTogIk1EU1IgQ2ggOSAmIElTTFIgQ2ggMTAiCm91dHB1dDogCiAgc2xpZHlfcHJlc2VudGF0aW9uOiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdCAgCi0tLQoKCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCm9wdGlvbnMoZGlnaXRzPTQpCgojIHBhY2thZ2VzIHVzZWQKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShJU0xSKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeShnZ2RlbmRybykKCgojIHVzZXItZGVmaW5lZCBmdW5jdGlvbnMgCgoKIyBpbnB1dHMgc3VtbWFyeQpkYXRhKCJVU0FycmVzdHMiKQpkYXRhKCJOQ0k2MCIpCgpgYGAKCgojIyBBZ2VuZGEKCgojIyMjIEFubm91bmNlbWVudHMKCi0gTURTUiBDaCA5IHByb2dyYW1taW5nIG5vdGVib29rIGFzc2lnbmVkCi0gTURTUiBDaCA4ICYgOSBFeGVyY2lzZXMgYXNzaWduZWQKLSBtaWR0ZXJtIGV4YW0KICAgIC0gbWl4IG9mIGluLWNsYXNzIGFuZCB0YWtlLWhvbWUKICAgIC0gaW4tY2xhc3MgcG9ydGlvbiBXZWQgMi8yNwogICAgLSB0YWtlLWhvbWUgcG9ydGlvbiBkdWUgRnJpIDMvMSBhdCAxMTo1OXBtCiAgICAtIGNsYXNzIHdpbGwgbm90IG1lZXQgb24gRnJpZGF5IDMvMSAKCiMjIyMgTURTUiBDaCA5IEVycmF0YSAvIFRpcHMKCi0gU29tZSBzZWN0aW9ucyBkb24ndCByZXF1aXJlIHByb2dyYW1taW5nLCBidXQgcGxlYXNlIHN0aWxsIGluY2x1ZGUgdGhlIGhlYWRlcnMgZm9yIG5hdmlnYXRpb24gcHVycG9zZXMKLSBwLiAyMDY6IHRoZSBgZGVzdGZpbGVgIGFyZ3VtZW50IHByb2JhYmx5IHdvbid0IHdvcmsgZm9yIHlvdSBhcyB3cml0dGVuLiAgWW91IGNhbgogICAgMS4gKHJlY29tbWVuZGVkKSBjcmVhdGUgYSBmb2xkZXIgY2FsbGVkICJkYXRhIiBpbiB0aGUgc2FtZSBkaXJlY3RvcnkgYXMgeW91ciAuUm1kIGZpbGUKICAgIDIuIGRlbGV0ZSBgZGF0YS9gIGFuZCB1c2UgYGRlc3RmaWxlID0gZnVlbGVjb25vbXkuemlwYCBpbnN0ZWFkLiAgWW91J2xsIG5lZWQgdG8gdXBkYXRlIHN1YnNlcXVlbnQgZnVuY3Rpb25zIG9uIHAuIDIwNiBhbmQgMjA3IGluIGtpbmQgd2hlbiBhdHRlbXB0aW5nIHRvIGFjY2VzcyBmaWxlcyBpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgYGRhdGFgCi0gU2VjdGlvbiA5LjIuMjogbXkgcmVzdWx0cyBhcmVuJ3QgaWRlbnRpY2FsIHRvIHRob3NlIGluIHRoZSBib29rLi4uIG1heWJlIEkgbWFkZSBhIG1pc3Rha2Ugb3IgbWF5YmUgdGhlIGRhdGEgaGFzIGNoYW5nZWQuICBOYW1lbHksIHRoZSBjbHVzdGVycyBhcmUgc2ltaWxhciBidXQgZG9uJ3QgKmV4YWN0bHkqIG1hdGNoIG9uIGNsb3NlIGluc3BlY3Rpb24uICAKCgojIyBTdGF0aXN0aWNhbCBMZWFybmluZyAocmVjYWxsKQoKUTogV2hhdCBhcmUgc29tZSBkaWZmZXJlbmNlcyBiZXR3ZWVuICoqU3VwZXJ2aXNlZCoqIGFuZCAqKlVuc3VwZXJ2aXNlZCoqIExlYXJuaW5nPwoKCgojIyBTdGF0aXN0aWNhbCBMZWFybmluZyAocmVjYWxsKQoKLSBzdGF0aXN0aWNhbCBsZWFybmluZyByZWZlcnMgdG8gYSB2YXN0IHNldCBvZiB0b29scyBmb3IgdW5kZXJzdGFuZGluZyBkYXRhCi0gVHlwaWNhbCBkaXN0aW5jdGlvbiBiZXR3ZWVuICoqU3VwZXJ2aXNlZCoqIGFuZCAqKlVuc3VwZXJ2aXNlZCoqIGxlYXJuaW5nCiAgICAtICoqU3VwZXJ2aXNlZCoqIGxlYXJuaW5nIHByZWRpY3RpdmUgb3IgaW5mZXJlbnRpYWwgbW9kZWxpbmcKICAgICAgICAtIHRoZXJlIGlzIGEgcmVzcG9uc2UgdmFyaWFibGUgKFkpCiAgICAgICAgLSBwcmVkaWN0aXZlIG1vZGVsaW5nOiB3ZSB3YW50IHRvIGFudGljaXBhdGUgdGhlIHJlc3BvbnNlIChZKSBhaGVhZCBvZiB0aW1lIGJhc2VkIG9uIGtub3dsZWRnZSBvZiBhIHNldCBvZiBtZWFzdXJhYmxlIGlucHV0cyAoWCdzKQogICAgICAgIC0gaW5mZXJlbnRpYWwgbW9kZWxpbmc6IHdlIHdhbnQgdG8gdW5kZXJzdGFuZCB0aGUgd2F5IG91ciByZXNwb25zZSAoWSkgY2hhbmdlcyBhcyB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIChYJ3MpIGNoYW5nZQogICAgLSAqKlVuc3VwZXJ2aXNlZCoqIGxlYXJuaW5nIG1ldGhvZHMgY291bGQgYmUgY29uc2lkZXJlZCAiZGF0YSBkaXNjb3ZlcnkiIG1vZGVscyAKICAgICAgICAtIHRoZXJlIGlzIE5PIHJlc3BvbnNlIHZhcmlhYmxlIChZKQogICAgICAgIC0gd2UgYXJlIGludGVyZXN0ZWQgaW4gZXhwb3NpbmcgaW50ZXJlc3RpbmcgcmVsYXRpb25zaGlwcy9ncm91cHMvY2x1c3RlcnMgYW1vbmcgc2V2ZXJhbCBleHBsYW5hdG9yeSB2YXJpYWJsZXMgKFgncykKCgojIyBVbnN1cGVydmlzZWQgTGVhcm5pbmcgCgotIE9mdGVuIGNvbnNpZGVyZWQgd2l0aGluIHRoZSBkb21haW4gb2YgRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcwogICAgLSBTZWFyY2ggZm9yIHVzZWZ1bCBzdHJ1Y3R1cmUgYW1vbmcgZXhwbGFuYXRvcnkgdmFyaWFibGVzOiBYMSwgWDIsIC4uLixYcC4gCiAgICAtIEluZm9ybWF0aXZlIHdheSB0byB2aXN1YWxpemUgdGhlIGRhdGE/IAogICAgLSBTdWJncm91cHMgYW1vbmcgdGhlIHZhcmlhYmxlcyBvciBhbW9uZyB0aGUgb2JzZXJ2YXRpb25zPyAgCi0gKipEaW1lbnNpb24gcmVkdWN0aW9uKioKICAgIC0gZXhwcmVzcyBsb3ctZGltZW5zaW9uYWwgcmVwcmVzZW50YXRpb24gb2YgdGhlIGRhdGEgdGhhdCBleHBsYWlucyBhIGdvb2QgZnJhY3Rpb24gb2YgdGhlIHZhcmlhbmNlCiAgICAtIGNvbW1vbmx5IHVzZWQgZm9yIGRhdGEgdmlzdWFsaXphdGlvbiBvciBwcmUtcHJvY2Vzc2luZyBiZWZvcmUgc3VwZXJ2aXNlZCBsZWFybmluZyB0ZWNobmlxdWVzIGFyZSBhcHBsaWVkCiAgICAtIFByaW5jaXBsZSBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSktLWRpc2N1c3NlZCBoZXJlCiAgICAtIFNpbmd1bGFyIHZhbHVlIGRlY29tcG9zaXRpb24gKFNWRCktLXNlZSBNRFNSIENoIDksIGZvciBleGFtcGxlCi0gKipDbHVzdGVyaW5nKioKICAgIC0gYnJvYWQgY2xhc3Mgb2YgbWV0aG9kcyBmb3IgKipkaXNjb3ZlcmluZyB1bmtub3duIHN1Ymdyb3VwcyoqIHdpdGhpbiBkYXRhCiAgICAtIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nOiAKICAgIC0gay1tZWFuczogc3BlY2lmeSBudW1iZXIgb2YgZ3JvdXBzCi0gUTogd2hlbiB3b3VsZCB0aGlzIGJlIHVzZWZ1bD8KCgojIyBDaGFsbGVuZ2Ugb2YgVW5zdXBlcnZpc2VkIExlYXJuaW5nCgotIE5vICJncm91ZCB0cnV0aCIKLSBpbiBzdXBlcnZpc2VkIGxlYXJuaW5nIHdlIGNhbiBjaGVjayBvdXIgd29yayBieSBldmFsdWF0aW5nIHByZWRpY3Rpb25zIHVzaW5nIGNyb3NzLXZhbGlkYXRpb24sIGluZGVwZW5kZW50IHRlc3Qgc2V0LCBldGMuCi0gdHlwaWNhbGx5IG5vIG1lY2hhbmlzbSB0byAiY2hlY2sgb3VyIHdvcmsiIGluIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBiZWNhdXNlIHRoZSB0cnVlIGFuc3dlciBpcyB1bmtub3duCgoKIyMgU29tZSBFeGFtcGxlcyAKCi0gQSBjYW5jZXIgcmVzZWFyY2hlciBtaWdodCBhc3NheSBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzIGluIDEwMCBwYXRpZW50cyB3aXRoIGJyZWFzdCBjYW5jZXIuIEhlIG9yIHNoZSBtaWdodCB0aGVuIGxvb2sgZm9yIHN1Ymdyb3VwcyBhbW9uZyB0aGUgYnJlYXN0IGNhbmNlciBzYW1wbGVzLCBvciBhbW9uZyB0aGUgZ2VuZXMsIGluIG9yZGVyIHRvIG9idGFpbiBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkaXNlYXNlLiAKCi0gQSBzZWFyY2ggZW5naW5lIChlLmcuLCBHb29nbGUpIG1pZ2h0IGNob29zZSB3aGF0IHNlYXJjaCByZXN1bHRzIHRvIGRpc3BsYXkgdG8gYSBwYXJ0aWN1bGFyIGluZGl2aWR1YWwgYmFzZWQgb24gdGhlIGNsaWNrIGhpc3RvcmllcyBvZiBvdGhlciBpbmRpdmlkdWFscyB3aXRoIHNpbWlsYXIgc2VhcmNoIHBhdHRlcm5zLiAKCi0gTWFwcGluZyBldm9sdXRpb25hcnkgcmVsYXRpb25zaGlwcyBhbW9uZyB2YXJpb3VzIGJpb2xvZ2ljYWwgc3BlY2llcyBvciBvdGhlciBlbnRpdGllcy0tdGhlaXIgcGh5bG9nZW55LS1iYXNlZCB1cG9uIHNpbWlsYXJpdGllcyBhbmQgZGlmZmVyZW5jZXMgaW4gdGhlaXIgcGh5c2ljYWwgb3IgZ2VuZXRpYyBjaGFyYWN0ZXJpc3RpY3MuCgoKIyMgUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMgKFBDQSkKCi0gKipHb2FsKio6IHN1bW1hcml6ZSBhIGxhcmdlIHNldCBvZiBjb3JyZWxhdGVkIHZhcmlhYmxlcyB3aXRoIGEgc21hbGxlciBudW1iZXIgb2YgcmVwcmVzZW50YXRpdmUgdmFyaWFibGVzIHRoYXQgY29sbGVjdGl2ZWx5IGV4cGxhaW4gbW9zdCBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIG9yaWdpbmFsIGRhdGEgc2V0Ci0gKipJbnR1aXRpb24qKjoKICAgIC0gbWFueSBvZiBvdXIgdmFyaWFibGVzIGFyZSBjb3JyZWxhdGVkIG9yIHBvc3NpYmx5IHJlZHVuZGFudAogICAgLSBwZXJoYXBzIHdlIGNhbiByb3RhdGUgb3VyIGRhdGEgdG8gaWRlbnRpZnkgCiAgICAgICAgMS4gKFBDMSkgdGhlIGF4aXMgdGhhdCBtYXhpbWl6ZXMgdmFyaWFiaWxpdHkgKGZpcnN0IHByaW5jaXBhbCBjb21wb25lbnQtLVBDMSkKICAgICAgICAyLiAoUEMyKSBhbiBheGlzIHBlcnBlbmRpY3VsYXIgdG8gUEMxIHRoYXQgbWF4aW1pemVzIHRoZSByZW1haW5pbmcgdmFyaWFiaWxpdHkKICAgICAgICAzLiBhbmQgc28gb24uLi4gdW50aWwgd2UgZXhwbGFpbiBhbGwgb2YgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEKICAgIC0gaG9wZWZ1bGx5LCB3ZSBjYW4gZXhwbGFpbiBtb3N0IChvciBhIGxvdCkgb2YgdGhlIHRvdGFsIHZhcmlhYmlsaXR5IHdpdGggb25seSB0aGUgKipmaXJzdCBmZXcqKiBwcmluY2lwYWwgY29tcG9uZW50cwogICAgLSBzb21ldGltZXMgd2UgY2FuIGV2ZW4gYXR0ZW1wdCB0byBpbnRlcnByZXQgaG93IHByaW5jaXBhbCBjb21wb25lbnRzIGFzc29jaWF0ZSAoaS5lLiwgImxvYWQiKSB3aXRoIHJlc3BlY3QgdG8gb3RoZXIgdmFyaWFibGVzIGluIHRoZSBkYXRhIAotICoqTWV0aG9kKio6IFBDQSBjYW4gYmUgZG9uZSBieSBlaWdlbnZhbHVlIGRlY29tcG9zaXRpb24gb2YgYSBkYXRhIGNvdmFyaWFuY2UgKG9yIGNvcnJlbGF0aW9uKSBtYXRyaXggb3Igc2luZ3VsYXIgdmFsdWUgZGVjb21wb3NpdGlvbiAoU1ZEKSBvZiBhIGRhdGEgbWF0cml4LCB1c3VhbGx5IGFmdGVyIGEgbm9ybWFsaXphdGlvbiBzdGVwIG9mIHRoZSBpbml0aWFsIGRhdGEuIAoKIVtpbWFnZSBjcmVkaXQ6IEphbWVzIGV0IGFsICgyMDEzKSA8aHR0cDovL3d3dy1iY2YudXNjLmVkdS9+Z2FyZXRoL0lTTC8+IEZpZyA2LjE0XShwY2EtSVNMUi02LTE0LnBuZykKCiMjIFVTIEFycmVzdHMgYnkgU3RhdGUKCi0gd2Ugd2FudCB0byBjb21wYXJlIHN0YXRlcyBvbiB0aGVzZSBmb3VyIHZhcmlhYmxlcyAKICAgIC0gTXVyZGVyCiAgICAtIEFzc2F1bHQKICAgIC0gUmFwZQogICAgLSBVcmJhblBvcCAoJSBvZiBzdGF0ZSBsaXZpbmcgaW4gdXJiYW4gYXJlYXMpCi0gUTogd2hpY2ggdmFyaWFibGUocykgd291bGQgeW91IGV4cGVjdCB0byBoYXZlIHRoZSBsYXJnZXN0ICoqdmFyaWFuY2UqKgoKYGBge3J9CmRhdGEoIlVTQXJyZXN0cyIpCmhlYWQoVVNBcnJlc3RzKQpgYGAKCiMjIFBDQTogVVMgQXJyZXN0IERhdGEKCi0gbWVhc3VyZW1lbnQgdW5pdHMgJiBzY2FsZXMgc2lnbmlmaWNhbnRseSBpbXBhY3QgdmFyaWFuY2UKLSB3ZSBvZnRlbiB3YW50IHRvIHN0YW5kYXJkaXplIHRoZSB2YXJpYWJsZXMgZmlyc3QgCiAgICAtIG1lYW4gPSAwOyBzZCA9IDEgKGNvbnZlcnQgbWVhc3VyZW1lbnRzIHRvIHotc2NvcmVzKQogICAgLSB0aGlzIGFtb3VudHMgdG8gZ2l2aW5nIGVhY2ggdmFyaWFibGUgZXF1YWwgd2VpZ2h0CiAgICAtIG11dGVzIGVmZmVjdCBvZiB1bml0IGNvbnZlcnNpb24gKGttIHRvIG1ldGVycyBpbXBhY3RzIHZhcmlhbmNlIGVzdGltYXRlKQotIG1pZ2h0IG5vdCBzdGFuZGFyZGl6ZSBpZiBhbGwgdmFyaWFibGVzIGFyZSBtZWFzdXJlZCBvbiBzYW1lIHNjYWxlCgoKIyMjIyBGaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMKCi0gUTogSG93IGRvIFBDMSBhbmQgUEMyIGFzc29jaWF0ZSB3aXRoIG91ciBmb3VyIHZhcmlhYmxlcz8gCi0gTm90ZTogU2VlIHdoYXQgaGFwcGVucyBpZiB3ZSBET04nVCBzdGFuZGFyZGl6ZSB2YXJpYWJsZXMuLi4KCmBgYHtyfQpVU0FycmVzdHNfcGNhIDwtIFVTQXJyZXN0cyAlPiUKICBwcmNvbXAoc2NhbGUgPSBUUlVFKSAgIyBzdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzCgojIHRoZSByZXN1bHQgaXMgYSBsaXN0IG9iamVjdApzdHIoVVNBcnJlc3RzX3BjYSkKCiMgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzCigtMSkgKiBVU0FycmVzdHNfcGNhJHJvdGF0aW9uWywgMToyXSAlPiUgcm91bmQoMikKYGBgCgpgYGB7cn0KIyBwbG90IG9mIHRoZSBmaXJzdCB0d28gcHJpbmNpcGFsIGNvbXBvbmVudHMKVVNBcnJlc3RzX3BjYSR4ICU+JQogIGFzLmRhdGEuZnJhbWUoKSAlPiUgICMgYGdncGxvdDJgIGV4cGVjdHMgYSBkYXRhIGZyYW1lIG9iamVjdAogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIGdncGxvdChhZXMoeCA9IC1QQzEsIHkgPSAtUEMyKSkgKyAKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm93bmFtZSksIHNpemUgPSAzKSArIAogIHhsYWIoIkJlc3QgVmVjdG9yIGZyb20gUENBIChhcHByb3guIFZpb2xlbnQgQ3JpbWUpIikgKyAKICB5bGFiKCIybmQgQmVzdCBWZWN0b3IgZnJvbSBQQ0EgKGFwcHJveC4gVXJiYW5pemF0aW9uKSIpICsgCiAgZ2d0aXRsZSgiVHdvLWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIG9mIFVTIEFycmVzdHMgYnkgU3RhdGUiKQoKYGBgCgoKIyMgUHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgCgotIChyZWNhbGwpICoqR29hbCoqOiBzdW1tYXJpemUgYSBsYXJnZSBzZXQgb2YgY29ycmVsYXRlZCB2YXJpYWJsZXMgd2l0aCBhIHNtYWxsZXIgbnVtYmVyIG9mIHJlcHJlc2VudGF0aXZlIHZhcmlhYmxlcyB0aGF0IGNvbGxlY3RpdmVseSBleHBsYWluIG1vc3Qgb2YgdGhlIHZhcmlhYmlsaXR5IGluIHRoZSBvcmlnaW5hbCBkYXRhIHNldAotIEluIGdlbmVyYWwsIGEgJG4gXHRpbWVzIHAkIGRhdGEgbWF0cml4ICoqWCoqIGhhcyAkXHRleHR7bWlufShuLTEsIHApJCBkaXN0aW5jZSBwcmluY2lwYWwgY29tcG9uZW50cwogICAgLSB3ZSB3YW50IHNtYWxsZXN0IG51bWJlciBvZiBQQydzIHRvIGdldCBhIGdvb2QgdW5kZXJzdGFuZGluZyBvZiB0aGUgZGF0YQogICAgLSB0aGVyZSBpc24ndCByZWFsbHkgYW4gb3B0aW1hbCBzb2x1dGlvbiB0byB0aGlzIHByb2JsZW0uLi4KLSAqKlByb3BvcnRpb24gb2YgdmFyaWFiaWxpdHkgZXhwbGFpbmVkIChQVkUpKioKICAgIC0gQXNzZXNzIFBWRSBmb3IgZWFjaCBvZiBvdXIgcHJpbmNpcGFsIGNvbXBvbmVudCB2ZWN0b3JzCiAgICAtIENhbiB3ZSBmaW5kIGEgcG9pbnQgb2YgZGltaW5pc2hpbmcgcmV0dXJuIGFtb25nIHByaW5jaXBhbCBjb21wb25lbnRzCiAgICAgICAgLSB3ZSB3YW50IGZld2VzdCBudW1iZXIgb2YgUEMncyB0aGF0IHN0aWxsIGRvIGEgZ29vZCBqb2IgcmVwcmVzZW50aW5nIHRoZSBkYXRhCiAgICAgICAgLSBpbmZvcm1hbGx5LCB3ZSBsb29rIGZvciBhbiAiZWxib3ciIGluIHRoZSAqKnNjcmVlIHBsb3QqKgoKIVtCeSBLZXZpbiBMZW56ICh0YWxrIGNvbnRyaWJzKSAtIE93biB3b3JrLCBDQyBCWS1TQSAyLjUsIDxodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93L2luZGV4LnBocD9jdXJpZD0xMjM5NzQyPl0oc2NyZWUuanBnKQoKIyMgU2NyZWUgUGxvdCAoVVMgQXJyZXN0IERhdGEpCgotIFE6IEhvdyBkaWQgd2UgZG8/Ci0gUTogSG93IG1hbnkgcHJpbmNpcGFsIGNvbXBvbmVudHMgZG8geW91IHRoaW5rIHdlIHNob3VsZCBjb25zaWRlcj8KCgpgYGB7ciBldmFsPUZBTFNFfQpVU0FycmVzdHNfcHZlIDwtIAogIGRhdGEuZnJhbWUoc2QgPSBVU0FycmVzdHNfcGNhJHNkZXYpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JQogIG11dGF0ZShyb3duYW1lID0gcGFyc2VfbnVtYmVyKHJvd25hbWUpLCAKICAgICAgICAgdG90YWxWYXIgPSBzdW0oVVNBcnJlc3RzX3BjYSRzZGV2XjIpLCAKICAgICAgICAgcHZlID0gMTAwICogc2ReMiAvIHRvdGFsVmFyLCAKICAgICAgICAgY3VzdW0gPSBjdW1zdW0ocHZlKSkKCiMgc2NyZWUgcGxvdApVU0FycmVzdHNfcHZlICU+JQogIGdncGxvdChhZXMoeCA9IHJvd25hbWUsIHkgPSBwdmUpKSArIAogIGdlb21fbGluZSh0eXBlID0gMykgKyAKICB4bGFiKCJQcmluY2lwYWwgQ29tcG9uZW50IikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCIpICsgCiAgZ2d0aXRsZSgiU2NyZWUgUGxvdCBvZiBQcmluY2lwYWwgQ29tcG9uZW50cyBmb3IgVVMgQXJyZXN0cyBEYXRhIikgCgogIAojIGN1bXVsYXRpdmUgUFZFIHBsb3QKVVNBcnJlc3RzX3B2ZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByb3duYW1lLCB5ID0gY3VzdW0pKSArIAogIGdlb21fbGluZSh0eXBlID0gMykgKyAKICB4bGFiKCJQcmluY2lwYWwgQ29tcG9uZW50IikgKyAKICB5bGFiKCJQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCIpICsgCiAgZ2d0aXRsZSgiQ3VtdWxhdGl2ZSBQcm9wb3J0aW9uIG9mIFZhcmlhbmNlIEV4cGxhaW5lZCBmb3IgVVMgQXJyZXN0cyBEYXRhIikgCgpgYGAKCgoKIyMgTkNJNjAgRGF0YSBFeGFtcGxlCgotIFRoZSBOQ0ktNjAgY2FuY2VyIGNlbGwgbGluZSBwYW5lbCBpcyBhIGdyb3VwIG9mIDYwIGh1bWFuIGNhbmNlciBjZWxsIGxpbmVzIHVzZWQgYnkgdGhlIE5hdGlvbmFsIENhbmNlciBJbnN0aXR1dGUgKE5DSSkgZm9yIHRoZSBzY3JlZW5pbmcgb2YgY29tcG91bmRzIHRvIGRldGVjdCBwb3RlbnRpYWwgYW50aWNhbmNlciBhY3Rpdml0eS4KICAgIC0gPGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL05DSS02MD4KICAgIC0gNjQgY2FuY2VyIGNlbGwgbGluZXMKICAgIC0gNjgzMCBnZW5lIGV4cHJlc3Npb24gbWVhc3VyZW1lbnRzCi0gUTogV2h5IHdvdWxkIHdlIGNhcmUgYWJvdXQgZGltZW5zaW9uIHJlZHVjdGlvbiBoZXJlPyAod2hhdCdzIGEgImNhc2UiIGluIHRoZSBkYXRhPykKCgoKIyMgSW5zcGVjdGluZyB0aGUgZGF0YQoKYGBge3J9CnJlcXVpcmUoSVNMUikKZGF0YSgiTkNJNjAiKQoKY2FuY2VyTGFiZWxzIDwtIE5DSTYwJGxhYnMgCgojIHJlY29kZSBpbW1vcnRhbGl6ZWQgY2VsbCBsaW5lcwpjYW5jZXJMYWJlbHMgPC0gaWZlbHNlKHRlc3QgPSBncmVwbChwYXR0ZXJuID0gIk1DRjciLCB4ID0gY2FuY2VyTGFiZWxzKSwKICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiQlJFQVNUIiwgbm8gPSBjYW5jZXJMYWJlbHMpCmNhbmNlckxhYmVscyA8LSBpZmVsc2UodGVzdCA9IGdyZXBsKHBhdHRlcm4gPSAiSzU2MiIsIHggPSBjYW5jZXJMYWJlbHMpLAogICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJMRVVLRU1JQSIsIG5vID0gY2FuY2VyTGFiZWxzKQoKbmNpRGF0YSA8LSBOQ0k2MCRkYXRhIAoKIyBkaW1lbnNpb25zIG9mIHRoZSBkYXRhCm5yb3cobmNpRGF0YSkgICMgY2FzZXMKbmNvbChuY2lEYXRhKSAgIyB2YXJpYWJsZXMKCiMgaW5zcGVjdCBOQ0kgY2FuY2VyIGxhYmVscwpoZWFkKGNhbmNlckxhYmVscykKCiMgZGlzdHJpYnV0aW9uIG9mIGNhbmNlciBjZWxsIGxpbmVzIGF2YWlsYWJsZQp0YWxseShjYW5jZXJMYWJlbHMgfiAxKQoKYGBgCgo8IS0tIERheTIgLS0+CgojIyBQcmluY2lwYWwgY29tcG9uZW50cyBhbmFseXNpcyBvZiBOQ0k2MCBkYXRhCgotIGNvdWxkIG1ha2UgYSBjYXNlIGVpdGhlciB3YXkgZm9yIHN0YW5kYXJkaXppbmcgdmFyaWFibGVzIHNpbmNlIGFsbCBtZXRyaWNzIGFyZSBvbiBzYW1lIHNjYWxlIGluIHRoaXMgY2FzZQotIFE6IEhvdyBtYW55IHRvdGFsIHByaW5jaXBhbCBjb21wb25lbnRzIHBvc3NpYmxlPwogICAgLSBXZSBoYXZlIDY0IFBDJ3MgdGhpcyB0aW1lCiAgICAtIFVTIEFycmVzdHMgaGFkIDQgUEMncwoKYGBge3J9CiMgcGVyZm9ybSBwY2Egb24gc2NhbGVkIGdlbmVzCk5DSV9wY2EgPC0gbmNpRGF0YSAlPiUKICBwcmNvbXAoc2NhbGUgPSBUUlVFKSAgCgojIHRoZSByZXN1bHQgaXMgYSBsaXN0IG9iamVjdApzdHIoTkNJX3BjYSkKYGBgCgoKIyMgRmlyc3QgZmV3IHByaW5jaXBhbCBjb21wb25lbnRzIG9mIE5DSTYwIGRhdGEKCmBgYHtyfQojIHBsb3QgUEMxIHZzIFBDMgpOQ0lfcGNhJHggJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAgIyBgZ2dwbG90MmAgZXhwZWN0cyBhIGRhdGEgZnJhbWUgb2JqZWN0CiAgZ2dwbG90KGFlcyh4ID0gUEMxLCB5ID0gUEMyKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhbmNlckxhYmVscyksIHNpemUgPSAzKSArIAogIHhsYWIoIkJlc3QgVmVjdG9yIGZyb20gUENBIikgKyAKICB5bGFiKCJTZWNvbmQgQmVzdCBWZWN0b3IgZnJvbSBQQ0EiKSArIAogIGdndGl0bGUoIlR3by1kaW1lbnNpb25hbCByZXByZXNlbnRhdGlvbiBvZiA2ODMwIEdlbmVzIChjb2xvcmVkIGJ5IGFjdHVhbCBjYW5jZXIgdHlwZSkiKSAKCgojIHBsb3Qgb2YgUEMxIHZzIFBDMwpOQ0lfcGNhJHggJT4lCiAgYXMuZGF0YS5mcmFtZSgpICU+JSAgIyBgZ2dwbG90MmAgZXhwZWN0cyBhIGRhdGEgZnJhbWUgb2JqZWN0CiAgZ2dwbG90KGFlcyh4ID0gUEMxLCB5ID0gUEMzKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGNhbmNlckxhYmVscyksIHNpemUgPSAzKSArIAogIHhsYWIoIkJlc3QgVmVjdG9yIGZyb20gUENBIikgKyAKICB5bGFiKCJUaGlyZCBCZXN0IFZlY3RvciBmcm9tIFBDQSIpICsKICBnZ3RpdGxlKCJUd28tZGltZW5zaW9uYWwgcmVwcmVzZW50YXRpb24gb2YgNjgzMCBHZW5lcyAoY29sb3JlZCBieSBhY3R1YWwgY2FuY2VyIHR5cGUpIikKYGBgCgoKCmBgYHtyfQojIFNEIGFuZCB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBQQwpzdW1tYXJ5KE5DSV9wY2EpCgojIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIChQVkUpIG9mIGVhY2ggUEMKTkNJX3B2ZSA8LSAKICBkYXRhLmZyYW1lKHNkID0gTkNJX3BjYSRzZGV2KSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBtdXRhdGUocm93bmFtZSA9IHBhcnNlX251bWJlcihyb3duYW1lKSwgCiAgICAgICAgIHRvdGFsVmFyID0gc3VtKE5DSV9wY2Ekc2Rldl4yKSwgCiAgICAgICAgIHB2ZSA9IDEwMCAqIHNkXjIgLyB0b3RhbFZhciwgCiAgICAgICAgIGN1c3VtID0gY3Vtc3VtKHB2ZSkpCgojIHNjcmVlIHBsb3QKTkNJX3B2ZSAlPiUKICBnZ3Bsb3QoYWVzKHggPSByb3duYW1lLCB5ID0gcHZlKSkgKyAKICBnZW9tX2xpbmUodHlwZSA9IDMpICsgCiAgeGxhYigiUHJpbmNpcGFsIENvbXBvbmVudCIpICsgCiAgeWxhYigiUHJvcG9ydGlvbiBvZiBWYXJpYW5jZSBFeHBsYWluZWQiKSArIAogIGdndGl0bGUoIlNjcmVlIFBsb3Qgb2YgUHJpbmNpcGFsIENvbXBvbmVudHMgZm9yIE5DSTYwIERhdGEiKSAKCiAgCiMgY3VtdWxhdGl2ZSBQVkUgcGxvdApOQ0lfcHZlICU+JQogIGdncGxvdChhZXMoeCA9IHJvd25hbWUsIHkgPSBjdXN1bSkpICsgCiAgZ2VvbV9saW5lKHR5cGUgPSAzKSArIAogIHhsYWIoIlByaW5jaXBhbCBDb21wb25lbnQiKSArIAogIHlsYWIoIlByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIikgKyAKICBnZ3RpdGxlKCJDdW11bGF0aXZlIFByb3BvcnRpb24gb2YgVmFyaWFuY2UgRXhwbGFpbmVkIGZvciBOQ0k2MCBEYXRhIikgCgpgYGAKCgoKCgoKIyMgQ2x1c3RlcmluZwoKLSBCb3RoIHByaW5jaXBhbCBjb21wb25lbnRzIGFuZCBjbHVzdGVyaW5nIHNlZWsgdG8gc2ltcGxpZnkgdGhlIGRhdGEgdmlhIGEgc21hbGwgbnVtYmVyIG9mIHN1bW1hcmllcywgYnV0IHRoZSBtZWNoYW5pc21zIGRpZmZlcgogICAgLSBQQ0EgKCYgU1ZEKSBhdHRlbXB0cyB0byBmaW5kIGEgbG93LWRpbWVuc2lvbmFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBvYnNlcnZhdGlvbnMgdGhhdCBleHBsYWluIGEgZ29vZCBmcmFjdGlvbiBvZiB0aGUgdmFyaWFuY2UKICAgIC0gQ2x1c3RlcmluZyBsb29rcyB0byBmaW5kIHN1Ymdyb3VwcyBvciBkZWZpbmUgc2ltaWxhcml0eSBhbW9uZyB0aGUgb2JzZXJ2YXRpb25zCi0gKipDbHVzdGVyaW5nKiogYWxnb3JpdGhtcyBpbXBhcnQgb3JnYW5pemluZyBzdHJ1Y3R1cmUgZm9yIGRlc2NyaWJpbmcgZGVncmVlcyBvZiBzaW1pbGFyaXR5IGJldHdlZW4gZGlmZmVyZW50IHRoaW5ncwogICAgLSAqKkhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nKiogZG9lcyBub3Qgc3BlY2lmeSBhIGRlc2lyZWQgbnVtYmVyIG9mIGNsdXN0ZXJzLCBpbnN0ZWFkIG1hcHMgc2ltaWxhcml0eSBhbW9uZyBhbGwgKm4qIChkaXN0aW5jdCkgb2JzZXJ2YXRpb25zCiAgICAtIGEgKipkZW5kb2dyYW0qKjogYSB0cmVlLWJhc2VkIG9yZ2FuaXppbmcgc3RydWN0dXJlIGZvciBkZXNjcmliaW5nL3Zpc3VhbGl6aW5nIHRob3NlIGRlZ3JlZXMgb2Ygc2ltaWxhcml0eSwKICAgICAgLSBkb2Vzbid0IG1hdHRlciBob3cgdGhvc2UgcmVsYXRpb25zaGlwcyBjYW1lIHRvIGJlCiAgICAgIC0gVGhlIHRyZWUgbWF5IG9yIG1heSBub3QgcmVmbGVjdCBzb21lIGRlZXBlciByZWxhdGlvbnNoaXAgYW1vbmcgdGhlIG9iamVjdHMgbWVhc3VyZWQKICAgICAgLSAqTG9va3MqIGxpa2UgYSBkZWNpc2lvbiB0cmVlLCBidXQgaXQgYXBwcm9hY2hlcyB0aGUgcHJvYmxlbSBmcm9tIHRoZSBvcHBvc2l0ZSBkaXJlY3Rpb24KICAgIC0gKiogKksqLW1lYW5zKiogY2x1c3RlcmluZyBzZWVrcyB0byBwYXJ0aXRpb24gb2JzZXJ2YXRpb25zIGludG8gYSBwcmUtc3BlY2lmaWVkIG51bWJlciBvZiAoSykgY2x1c3RlcnMKCgohW2ltYWdlIGNyZWRpdDogSmFtZXMgZXQgYWwgKDIwMTMpIDxodHRwOi8vd3d3LWJjZi51c2MuZWR1L35nYXJldGgvSVNMLz4gRmlnIDEwLjEwXShkZW5kb2dyYW0tSVNMUi0xMC0xMC5wbmcpCgojIyBIaWVyYXJjaGljYWwgY2x1c3RlcmluZwoKLSBhcHByb3ByaWF0ZSB3aGVuIGNhc2VzIGFyZSBkZWNyaWJlZCBieSBhIHNldCBvZiBudW1lcmljYWwgdmFyaWFibGVzIChub25lIG9mIHdoaWNoIGlzIGEgKnJlc3BvbnNlKikKLSBNZXRob2QgKGFnZ2xvbW9yYXRpdmUvYm90dG9tLXVwIGNsdXN0ZXJpbmcpOgogICAgMS4gQmVnaW4gd2l0aCAqbiogY2FzZXMgZWFjaCBtZWFzdXJlZCBhcyBhIHBvaW50IGluIENhcnRlc2lhbiBzcGFjZSBhbmQgY2FsY3VsYXRlIGFsbCAke25cY2hvb3NlMn0gPSBcZnJhY3tuKG4tMSl9ezJ9JCBwYWlyd2lzZSBkaXNzaW1pbGFyaXRpZXMuICAqKlRyZWF0IGVhY2ggb2JzZXJ2YXRpb24gYXMgaXRzIG93biBjbHVzdGVyLioqCiAgICAyLiBGb3IgJGkgPSBuLCAobi0xKSwgLi4uLCAyJDoKICAgICAgICBBLiBFeGFtaW5lIGFsbCBwYWlyd2lzZSBpbnRlci1jbHVzdGVyIGRpc3NpbWlsYXJpdGllcyBhbW9uZyB0aGUgKmkqIGNsdXN0ZXJzIGFuZCBmdXNlIHRoZSB0d28gY2x1c3RlcnMgdGhhdCBhcmUgKipsZWFzdCBkaXNzaW1pbGFyKiouICAKICAgICAgICBCLiBDb21wdXRlIG5ldyBwYWlyd2lzZSBpbnRlci1jbHVzdGVyIGRpc3NpbWlsYXJpdGllcyBhbW9uZyB0aGUgJGktMSQgcmVtYWluaW5nIGNsdXN0ZXJzCi0gZm9yIGFuIGluZGl2aWR1YWwgcXVhbnRpdGF0dmUgdmFyaWFibGUsIGl0J3MgZWFzeSB0byBtZWFzdXJlIGRpc3RhbmNlIGJldHdlZW4gY2FzZXMKLSB3aXRoIG11bHRpcGxlIHZhcmlhYmxlcywgd2UgbmVlZCB0byBhZGp1c3QgZm9yIGRpZmZlcmVudCBzY2FsZXMgYW5kIHVuaXRzCiAgICAtIG5vICJiZXN0IiBzb2x1dGlvbi4uLiB1c3VhbGx5IHJlcXVpcmVzIGRvbWFpbiBleHBlcnRpc2UKICAgIC0gd2l0aCBubyBvdGhlciBpbmZvcm1hdGlvbiwgRXVjbGlkZWFuIGRpc3RhbmNlIGlzIGEgc2Vuc2libGUgZGVmYXVsdCAoYnV0IHRoZXJlIGFyZSBvdGhlcnMpCgohW2ltYWdlIGNyZWRpdDogSmFtZXMgZXQgYWwgKDIwMTMpIDxodHRwOi8vd3d3LWJjZi51c2MuZWR1L35nYXJldGgvSVNMLz4gRmlnIDEwLjExXShoY2x1c3QtSVNMUi0xMC0xMS5wbmcpCgoKIyMgRGlzdGFuY2UgJiBMaW5rYWdlOiAKCiFbaW1hZ2UgY3JlZGl0OiBKYW1lcyBldCBhbCAoMjAxMykgPGh0dHA6Ly93d3ctYmNmLnVzYy5lZHUvfmdhcmV0aC9JU0wvPiBGaWcgMTAuMTFdKGhjbHVzdC1JU0xSLTEwLTExLnBuZykKCi0gbXVsdGlwbGUgbGlua2FnZXMgYXJlIGF2YWlsYWJsZSBmb3IgY2FsY3VsYXRpbmcgaW50ZXItY2x1c3RlciBkaXNzaW1pbGFyaXRpZXMuIAotIG1vc3QgY29tbW9uIGxpbmthZ2VzIChiYWxhbmNlZCBkZW5kb2dyYW1zKQogICAgLSBjb21wbGV0ZSBsaW5rYWdlOiBsYXJnZXN0IGludGVyLWNsdXN0ZXIgZGlzc2ltaWxhcml0eQogICAgLSBhdmVyYWdlIGxpbmthZ2U6IG1lYW4gaW50ZXItY2x1c3RlciBkaXNzaW1pbGFyaXR5Ci0gbGVzcyBjb21tb24gbGlua2FnZXMKICAgIC0gc2luZ2xlIGxpbmthZ2U6IHNtYWxsZXN0IGludGVyLWNsdXN0ZXIgZGlzc2ltaWxhcml0eSAKICAgIC0gY2VudHJvaWQgbGlua2FnZTogZGlzc2ltaWxhcml0eSBhbW9uZyB0aGUgY2VudHJvaWRzIG9mIGNsdXN0ZXJzIAoKCiMjIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCgotIFRoZSB0cmVlIGlzICJncm93biIgaW4gcmV2ZXJzZS0tZnJvbSBib3R0b20gdG8gdG9wCi0gVGhlICoqZGlzc2ltaWxhcml0eSoqIGJldHdlZW4gY2x1c3RlcnMgaW5kaWNhdGVzIHRoZSBoZWlnaHQgaW4gdGhlIGRlbmRvZ3JhbSBhdCB3aGljaCB0aGUgZnVzaW9uIHNob3VsZCBiZSBwbGFjZWQuCiAgICAtIFE6IHdoaWNoIGlzIG1vcmUgc2ltaWxhciB0byBvYnMgIzI/CiAgICAgICAgLSBPYnMgIzcKICAgICAgICAtIE9icyAjOQogICAgLSBROiB3aGljaCBwYWlyIG9mIG9ic2VydmF0aW9ucyBpcyBtb3JlIHNpbWlsYXI/CiAgICAgICAgLSB7MywgNn0KICAgICAgICAtIHs5LCAyfQotIFdoZXJlIG1pZ2h0IHdlICJjdXQiIHRoZSBkZW5kb2dyYW0gdG8gZGVmaW5lIGNsdXN0ZXJzPwoKCiFbaW1hZ2UgY3JlZGl0OiBKYW1lcyBldCBhbCAoMjAxMykgPGh0dHA6Ly93d3ctYmNmLnVzYy5lZHUvfmdhcmV0aC9JU0wvPiBGaWcgMTAuMTBdKGRlbmRvZ3JhbS1JU0xSLTEwLTEwLnBuZykgCgo8IS0tIERheTMgLS0+CgojIyBDbHVzdGVyIGFuYWx5c2lzIG9mIE5DSTYwIGRhdGEKCi0gdGhlIHJlc2NhbGVkIHZhcmlhYmxlcyBpbmhlcml0ZWQgdW5mb3J0dW5hdGUgbmFtZXMgKGp1c3QgYSBjb2x1bW4gbnVtYmVyKQogICAgLSBROiBXaGF0IG1lYW5zICYgc2QncyBkbyB5b3UgZXhwZWN0IGZvciB2YXJpYWJsZXMgYDFgIGFuZCBgMmA/IChkb2VzIGl0IG1hdGNoPykKLSBjbHVzdGVyaW5nIGFsZ29yaXRobQogICAgLSBjYWxjdWxhdGUgYWxsIHBvaW50LXRvLXBvaW50IGRpc3RhbmNlcyAoZS5nLiwgcGFpcndpc2UgZGlzc2ltaWxhcml0aWVzKQogICAgLSBiZWdpbiBieSB0cmVhdGluZyBlYWNoIHBvaW50IGFzIGEgY2x1c3RlcgogICAgLSBpdGVyYXRpdmVseSBmdXNlIGNsdXN0ZXJzIHRoYXQgYXJlIGxlYXN0IGRpc3NpbWlsYXIgYWNjb3JkaW5nIHRvIGxpbmthZ2UgY2hvc2VuCgoKIyMjIyMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgc2NhbGUgdGhlIGRhdGEgKGNlbnRlcmVkIHdpdGggU0QgMSkKTkNJX3N0ZCA8LQogIHNjYWxlKG5jaURhdGEpICU+JQogIGFzLmRhdGEuZnJhbWUoKQoKIyB0aGUgdmFyaWFibGVzIGhhdmUgaW5oZXJpdGVkIHNvbWUgdW5mb3J0dW5hdGUgbmFtZXMgKGp1c3QgY29sdW1uIG51bWJlcikKZmF2c3RhdHMofiBgMWAsIGRhdGEgPSBOQ0lfc3RkKQpmYXZzdGF0cyh+IGAyYCwgZGF0YSA9IE5DSV9zdGQpCgpOQ0lfZGlzdCA8LSBkaXN0KE5DSV9zdGQpCgojICMgcGxvdCBkZWRyb2dyYW0gKGF2ZXJhZ2UgbGlua2FnZSkKIyBOQ0lfZGlzdCAlPiUKIyAgIGhjbHVzdChtZXRob2QgPSAiYXZlcmFnZSIpICU+JQojICAgcGxvdChjZXggPSAwLjksIGxhYmVscyA9IGNhbmNlckxhYmVscywgbWFpbiA9ICJOQ0k2MCBEZW5kb2dyYW0gd2l0aCBBdmVyYWdlIExpbmthZ2UiKQoKIyBjb25zdHJ1Y3QgZGVuZG9ncmFtIChjb21wbGV0ZSBsaW5rYWdlKQpOQ0lfZGVuZG8gPC0KICBOQ0lfZGlzdCAlPiUKICBoY2x1c3QobWV0aG9kID0gImNvbXBsZXRlIikKCiMgcHJpbnQgZGVuZG9ncmFtIGluZm8gKGRpc3RhbmNlICYgbWV0aG9kKQpwcmludChOQ0lfZGVuZG8pCgojIHBsb3QgZGVkcm9ncmFtIChjb21wbGV0ZSBsaW5rYWdlKQpOQ0lfZGVuZG8gJT4lCiAgcGxvdChjZXggPSAwLjksIGxhYmVscyA9IGNhbmNlckxhYmVscywgbHdkID0gMiwKICAgICAgIG1haW4gPSAiTkNJNjAgRGVuZG9ncmFtIHdpdGggQ29tcGxldGUgTGlua2FnZSIpCgpgYGAKCgoKIyMjIyMgQ3V0IERlbmRvZ3JhbSB0byBkZWZpbmUgY2x1c3RlcnMKCi0gQ29tcGxldGUgZGVuZG9ncmFtIGNhbiBiZSB1c2VkIHRvIHByb2R1Y2UgY2x1c3RlcnMKLSBlc3RhYmxpc2ggY3V0IHBvaW50IGJhc2VkIG9uIGRpc3NpbWlsYXJpdHkgaW5kZXggKHZlcnRpY2FsIGF4aXMpCi0gc29mdHdhcmUgY2FuIGNob29zZSBjdXQgYmFzZWQgb24gcmVxdWVzdGVkIG51bWJlciBvZiBjbHVzdGVycwotIFE6IERpZCB3ZSBsZWFybiBhbnl0aGluZyBmcm9tIG91ciBjbHVzdGVycz8KCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CiMgQ3V0IGRlbmRvZ3JhbSB0byBwcm9kdWNlIGNsdXN0ZXJzCk5DSV9kZW5kbyAlPiUKICBwbG90KGxhYmVscyA9IGNhbmNlckxhYmVscywgbHdkID0gMiwKICAgICAgIG1haW4gPSAiTkNJNjAgRGVuZG9ncmFtIHdpdGggQ29tcGxldGUgTGlua2FnZSAoNSBjbHVzdGVycykiKSAlPiUKICBhYmxpbmUoaCA9IDEzNSwgY29sID0gInJlZCIsIGx3ZCA9IDMpCgojIEN1dCBkZW5kb2dyYW0tLUhpZXJhcmNoaWNhbCBjbHVzdGVycwpOQ0lfRGVuZG9DbHVzdGVycyA8LSBjdXRyZWUodHJlZSA9IE5DSV9kZW5kbywgayA9IDUpCgoKIyBjbHVzdGVyaW5nIHBhdHRlcm5zIChMZXVrZW1pYSAmIG1lbGFub21hOyBub3Qgc28gbXVjaCBicmVhc3QpCnRhbGx5KGNhbmNlckxhYmVscyB+IE5DSV9EZW5kb0NsdXN0ZXJzKQoKCmBgYAoKCgojIyBLLW1lYW5zIGNsdXN0ZXJpbmcKCi0gR29hbDogcGFydGl0aW9uIHRoZSBvYnNlcnZhdGlvbnMgaW50byBhIHByZS1zcGVjaWZpZWQgbnVtYmVyIG9mICgqSyopIG5vbi1vdmVybGFwcGluZyBjbHVzdGVycwogICAgLSBtaW5pbWl6ZSB3aXRoaW4tY2x1c3RlciB2YXJpYXRpb24KICAgIC0gZWFjaCBvYnNlcnZhdGlvbiBpcyBhc3NpZ25lZCB0byBleGFjdGx5IG9uZSBjbHVzdGVyCiAgICAtIHNpbWlsYXIgdG8gY2xhc3NpZmljYXRpb24sIGJ1dCB0aGVyZSdzIG5vIHJlc3BvbnNlIHZhcmlhYmxlLCBzbyBtZWFuaW5nIG9mIGNsdXN0ZXJzIGlzIGluZmVycmVkIGltcGxpY2l0bHkKLSBNZXRob2QgKHNlZSBmaWd1cmUpOgogICAgMS4gUmFuZG9tbHkgYXNzaWduIGVhY2ggb2YgdGhlIG9ic2VydmF0aW9ucyB0byBjbHVzdGVycyAxIHRocm91Z2ggSwogICAgMi4gSXRlcmF0ZSB1bnRpbCBjbHVzdGVyIGFzc2lnbm1lbnRzIHN0b3AgY2hhbmdpbmc6CiAgICAgICAgQS4gRm9yIGVhY2ggb2YgdGhlICpLKiBjbHVzdGVycywgY29tcHV0ZSB0aGUgY2x1c3RlciAqY2VudHJvaWQqICh2ZWN0b3Igb2YgKnAqIGZlYXR1cmUgbWVhbnMgZm9yIHRoZSBvYnNlcnZhdGlvbnMgaW4gdGhlICprKnRoIGNsdXN0ZXIpCiAgICAgICAgQi4gQXNzaWduIGVhY2ggb2JzZXJ2YXRpb24gdG8gdGhlIGNsdXN0ZXIgd2hvc2UgY2VudHJvaWQgaXMgY2xvc2VzdCAoZS5nLiwgaW4gRXVjbGlkZWFuIGRpc3RhbmNlKQogICAgMy4gKHN0cm9uZ2x5IHJlY29tbWVuZGVkKSBSdW4gYWxnb3JpdGhtIG11bHRpcGxlIHRpbWVzIGZyb20gZGlmZmVyZW50IHJhbmRvbSBpbml0aWFsIGNvbmZpZ3VyYXRpb25zIHRvIHRlbXBlciBpbXBhY3Qgb2YgcmFuZG9tbmVzcyBpbiBzdGVwIDEuICBBcmd1bWVudCBgbnN0YXJ0YCBpcyBhdmFpbGFibGUgaW4gYGttZWFucygpYCBmdW5jdGlvbiBmb3IgdGhpcyBwdXJwb3NlLgotIENsdXN0ZXIgaW50ZXJwcmV0YXRpb246CiAgICAtIHJlbWVtYmVyIHRoaXMgaXMgcGFydCBvZiBFREEgdG8gdW5kZXJzdGFuZCBzdHJ1Y3R1cmUgaW4gb3VyIGRhdGEKICAgIC0gcGxvdCB0aGUgY2x1c3RlcnMKICAgIC0gaW52ZXN0aWdhdGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgY2x1c3RlcnMKCiFbaW1hZ2UgY3JlZGl0OiBKYW1lcyBldCBhbCAoMjAxMykgPGh0dHA6Ly93d3ctYmNmLnVzYy5lZHUvfmdhcmV0aC9JU0wvPiBGaWcgMTAuNl0oa21lYW5zLUlTTFItMTAtNi5wbmcpCgoKIyMgSy1tZWFucyBDbHVzdGVyaW5nCgotIFN1cHBvc2Ugd2UgY29uc2lkZXIgay1tZWFucyB3aXRoIDUgY2x1c3RlcnMKLSBROiBIb3cgZG9lcyByZXN1bHQgY29tcGFyZSB3aXRoIG91ciBIaWVyLiBDbHVzdC4gRGVuZG9ncmFtPwoKYGBge3J9CnNldC5zZWVkKDIpCgojIHBlcmZvcm0ga21lYW5zIGNsdXN0ZXJpbmcgKGsgPSA1IGNsdXN0ZXJzKQpOQ0lfa21lYW4gPC0KICBOQ0lfc3RkICU+JQogIGttZWFucyhjZW50ZXJzID0gNSwgbnN0YXJ0ID0gMjApCgojIHdoYXQgYXJlIHdlIHdvcmtpbmcgd2l0aApzdHIoTkNJX2ttZWFuKQoKIyBjb21wYXJlIEhpZXJhcmNoaWNhbCBDbHVzdGVycyB3aXRoIEstTWVhbnMgQ2x1c3RlcnMKTkNJX0tNZWFuQ2x1c3RlcnMgPC0gTkNJX2ttZWFuJGNsdXN0ZXIKCiMgYm90aCBtZXRob2RzIG1hdGNoIGZvciBvbmUgY2x1c3RlciwgYnV0IG90aGVycyBhcmUgbm9pc2llcgp0YWxseShOQ0lfRGVuZG9DbHVzdGVycyB+IE5DSV9LTWVhbkNsdXN0ZXJzKQoKYGBgCgoKIyMgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgb24gZmlyc3QgNyBwcmluY2lwYWwgY29tcG9uZW50cwoKLSBIb3cgbWlnaHQgd2UgY29tYmluZSBtZXRob2RzPwogICAgLSBQQ0EgZm9yIGRpbWVuc2lvbiByZWR1Y3Rpb24KICAgIC0gQ2x1c3RlciB0byBhc3Nlc3Mgc2ltaWxhcml0eQoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9Nn0KTkNJX3BjYV9oY2x1c3RlciA8LQogIE5DSV9wY2EkeFssIDE6N10gJT4lCiAgZGlzdCgpICU+JQogIGhjbHVzdCgpCgojIHBsb3QKTkNJX3BjYV9oY2x1c3RlciAlPiUKICBwbG90KGxhYmVscyA9IGNhbmNlckxhYmVscywgbHdkID0gMiwKICAgICAgIG1haW4gPSAiSGllcmFyY2ljYWwgQ2x1c3RlcmluZyBvbiBGaXJzdCBTZXZlbiBQcmluY2lwYWwgQ29tcG9uZW50cyIpCgp0YWxseShjYW5jZXJMYWJlbHMgfiBjdXRyZWUoTkNJX3BjYV9oY2x1c3RlciwgayA9IDcpKQpgYGAKCgoKIyMgUHJhY3RpY2FsIGlzc3VlcyBpbiBjbHVzdGVyaW5nCgotIERlY2lzaW9ucyB0byBiZSBtYWRlLi4uCiAgICAtIFN0YW5kYXJkaXplIHZhcmlhYmxlcz8KICAgIC0gSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcgZGVjaXNpb25zCiAgICAgICAgLSBXaGljaCBkaXNzaW1pbGFyaXR5IG1lYXN1cmU/CiAgICAgICAgLSBXaGF0IHR5cGUgb2YgbGlua2FnZT8KICAgICAgICAtIFdoZXJlIG1pZ2h0IHdlICJjdXQiIHRoZSBkZW5kb2dyYW0gdG8gZGVmaW5lIGNsdXN0ZXJzPwogICAgLSBLLW1lYW5zIGRlY2lzaW9uCiAgICAgICAgLSBob3cgdG8gY2hvb3NlIEs/Ci0gSW50ZWdyaXR5IG9mIHRoZSBjbHVzdGVycyBvYnRhaW5lZAogICAgLSBoYXJkIHRvIHZhbGlkYXRlIGNsdXN0ZXJzCiAgICAtIG5vIGNvbnNlbnN1cyBvbiBhc3Nlc3Npbmcgd2hldGhlciBjbHVzdGVyIGlzIGFydGlmYWN0IG9mIGNoYW5jZSAoZS5nLiBwLXZhbHVlKQogICAgLSBzZW5zaXRpdmUgdG8gZXh0cmVtZSBvYnNlcnZhdGlvbnMgKGFuZCBtdWx0aXZhcmlhdGUgb3V0bGllcnMgYXJlbid0IGFsd2F5cyBlYXN5IHRvIHNwb3QpCgojIyMjIFJlY29tbWVuZGF0aW9ucwoKLSBleHBlcmltZW50IHdpdGggZGlmZmVyZW50IGNob2ljZXMgb2YgbGlua2FnZSwgc3RhbmRhcmRpemVkL25vdCwgZXRjLCBhbmQgbG9vayBmb3IgcGF0dGVybnMgb3Igc3RydWN0dXJlcyB0aGF0IGNvbnNpc3RlbnRseSBlbWVyZ2UKLSBjbHVzdGVyIHJhbmRvbSBzdWJzZXRzIG9mIHRoZSBkYXRhIHRvIGdldCBzZW5zZSBvZiByb2J1c3RuZXNzIHRvIG91dGxpZXJzCi0gbW9zdCBpbXBvcnRhbnRseSwgYmUgY2FyZWZ1bCB3aGVuIHJlcG9ydGluZyByZXN1bHRzIG9mIGNsdXN0ZXIgYW5hbHlzaXMKICAgIC0gbm90IGFic29sdXRlIHRydXRoIGFib3V0IHRoZSBkYXRhIChtdWNoIGxlc3MgdGhlIHBvcHVsYXRpb24pCiAgICAtIGl0J3MgbW9yZSBvZiBhIHN0YXJ0aW5nIHBvaW50IHRvIGdlbmVyYXRlIHNjaWVudGlmaWMgcXVlc3Rpb25zIGZvciBzdHVkeSBvbiAoaWRlYWxseSkgaW5kZXBlbmRlbnQgZGF0YQoKCgo=