Change log


## Install necessary packages with:
# install.packages("tidyverse")
# install.packages("ggrepel")
# if (!requireNamespace("remotes", quietly = TRUE)) {
#     install.packages("remotes")
# }
# remotes::install_github("statOmics/HDDAData")
# remotes::install_github("vqv/ggbiplot")

library(tidyverse)
theme_set(theme_light())

library(ggbiplot)
library(ggrepel)
library(HDDAData)

1 Introduction

The first part of this lab demonstrates the influence of standardizing the data (i.e. working on the correlation matrix vs. working on the covariance matrix). Pay attention to what the output looks like and how it links to the biplot.)

2 PCA demonstration

2.1 Data prep

We will load the trees dataset (from base R, see ?trees for more info) that contains the height of a tree (in feet), the girth (or diameter in inches) and the volume (in cubic feet) of the tree. For the purpose of this exercise, we will convert the continuous volume variable to a categorical variable (a factor in R lingo). A tree will be considered large if its volume is bigger than 25 cubic feet, and small otherwise.

# Load data
data(trees)

# Convert volume to factor
trees$vol_fac <- as.factor(ifelse(trees$Volume > 25, "large", "small"))

# Preview data
head(trees)
summary(trees)
#>      Girth           Height       Volume       vol_fac  
#>  Min.   : 8.30   Min.   :63   Min.   :10.20   large:14  
#>  1st Qu.:11.05   1st Qu.:72   1st Qu.:19.40   small:17  
#>  Median :12.90   Median :76   Median :24.20             
#>  Mean   :13.25   Mean   :76   Mean   :30.17             
#>  3rd Qu.:15.25   3rd Qu.:80   3rd Qu.:37.30             
#>  Max.   :20.60   Max.   :87   Max.   :77.00

Now suppose that the height was actually measured in miles instead of feet.

## Create new column in trees with height in miles
trees$height_miles <- trees$Height / 5280

# Create matrix using height_miles and Girth variables
tree_mx <- cbind("height_miles" = trees$height_miles, "girth" = trees$Girth)
head(tree_mx)
#>      height_miles girth
#> [1,]   0.01325758   8.3
#> [2,]   0.01231061   8.6
#> [3,]   0.01193182   8.8
#> [4,]   0.01363636  10.5
#> [5,]   0.01534091  10.7
#> [6,]   0.01571970  10.8

Always good to visualize the data. Here we plot the height (in miles) vs. the girth for each tree and size the dots according to their volume. We also use a color aesthetic to distinguish “large” and “small” trees.

trees_plot <- ggplot(trees) +
  geom_point(aes(Girth, height_miles, size = Volume, col = vol_fac),
             alpha = 0.6) +
  labs(x = "Girth (inches)", y = "Height (miles)",
       color = "Volume class")

trees_plot +
  ggtitle("Visualizing the original trees data")

Pay attention to the units on the axis and the (very) different orders of the units.

Q: looking at this plot, can you make a guess in which direction the largest variation lies, i.e. in which direction the first principal component would lie?

To help with visualization of the PCs later on, we also make the plot using the centered and scaled data. The scale function can be used for this, so that all variables have mean 0 and unit variance.

## Center and scale data
trees_scaled <- scale(tree_mx, center = TRUE, scale = TRUE)

trees_scaled_plot <- trees_scaled %>%
  ## Convert to data.frame and re-add Volume columns for plotting
  data.frame(Volume = trees$Volume, vol_fac = trees$vol_fac) %>%
  ggplot() +
  geom_point(aes(girth, height_miles, size = Volume, col = vol_fac),
             alpha = 0.6) +
  labs(x = "Girth (inches), standardized", y = "Height (miles), standardized",
       color = "")

trees_scaled_plot +
  ggtitle("Visualizing the scaled trees data") +
  coord_equal(xlim = c(-2.3, 2.3), ylim = c(-2.3, 2.3))

2.2 Run PCA

We run PCA on the height (in miles) and girth variables and inspect the results.

# Run PCA with prcomp function, which uses SVD internally (see ?prcomp)
# Note that prcomp centers the matrix internally by default but does not scale it
# (center = TRUE, scale. = FALSE)
tree_pca <- prcomp(tree_mx)
summary(tree_pca)
#> Importance of components:
#>                          PC1      PC2
#> Standard deviation     3.138 0.001031
#> Proportion of Variance 1.000 0.000000
#> Cumulative Proportion  1.000 1.000000

Note: The first component explains almost 100% of the variability in the data.

The loadings of the PCA are stored in the $rotation slot of the prcomp result, while the $sdev slot contains the standard deviations of the principal components.

tree_pca$rotation
#>                       PC1           PC2
#> height_miles 0.0001996911 -0.9999999801
#> girth        0.9999999801  0.0001996911
tree_pca$sdev
#> [1] 3.138138679 0.001031321

Remember that the Principal Components are linear combinations of the original variables. The loadings tell you what the contribution (or weight) of each variable is to the PC. Here we see that the first PC is completely dominated by the girth variable, while the second component is basically (the negative) height variable. Since the PCs are ordered by the amount of variance they retain from the original data, we would conclude that most of the variance in the data comes from the girth variable.

Q: Is this in line with what you expected from the original plot? Why not? (Think about the units we are using here!)

The result from prcomp also contains an $x slot, which contains the projected values of the original data matrix onto the principal components (also called the PC scores). This is what we will use to construct the PCA plot.

2.2.1 Visualize Principal Components

Scale the PC loadings by their standard deviations (singular values) to project them back to the original data space. We also transpose the rotation matrix so that the variables are in columns and PCs in the rows, so that we can overlay them on the original trees plot.

## Transpose the loadings so that the PCs are in the rows, for plotting
pc_loadings <- t(tree_pca$rotation) * tree_pca$sdev

## Reuse plot from before and add PCs
trees_scaled_plot +
  geom_segment(
    data = data.frame(pc_loadings),
    aes(x = 0, xend = girth, y = 0, yend = height_miles),
    arrow = arrow(length=unit(0.1,"cm"))
  ) +
  geom_text(
    data = data.frame(pc_loadings),
    aes(x = girth, y = height_miles, label = rownames(pc_loadings)),
    vjust = 1.5
  ) +
  ggtitle("Trees data overlayed with PCs",
          subtitle = "Based on PCA on non-standardized data")

From this plot we see that the PCs are not in the directions we expected. PC1 should point in the direction of greatest variability.

2.2.2 Visualize PCA with biplot

ggbiplot(tree_pca, groups = trees$vol_fac, alpha = 0) +
  ## Add points layer to color and size points
  geom_point(aes(col = trees$vol_fac, size = trees$Volume), alpha = 0.6) +
  labs(size = "Volume", col = "") +
  theme(aspect.ratio = 0.6) +
  ggtitle("Biplot for the PCA on non-standardized data")

We see that trees high in volume tend to have a high tree girth, but the height does not give any information on tree volume. This is likely wrong, since we know that the height of a tree should have at least some influence on its volume. The problem here is that because of the 2 very different unit measures (miles and inches), the influence of the girth is inflated just because the order of the scale is much larger.

This is also reflected in the variances of these variables:

round(diag(cov(tree_mx)), 8)
#> height_miles        girth 
#>   0.00000146   9.84791398

We see that the variance of the girth variable is several orders of magnitude larger than that of the height (again, because of the different units) and this is reflected in the PCA.

We could use the same units, or we could standardize the variables by dividing by their standard deviations. Both will lead to a more balanced picture of the variability. Of course in this case one can argue that the variables should have the same unit but not be standardized, which may be a valid argument, were it not that we are measuring two different things (the height and the diameter). So even if we used the same units, it is recommended to also standardize the variables.

Imagine if we would be measuring the mass (\(kg\)) of the tree and the girth (\(m\)) of the tree, the scale on which both should be measured is no longer clear, since niether kilograms can be converted to meters nor meters converted to kilograms. In this case we have a clear argument to work on the standardized variables.

2.3 Redo PCA on standardized variables

We will leave the height on the miles scale, but now we will standardize the variables before performing the PCA. I.e. in addition to centering the matrix (subtracting the column means), we also divide it by its column standard deviations. Note that these operations can be done in one go with the prcomp function by specifying center = TRUE and scale. = TRUE (note the .!).

## Compute PCA on centered and scaled matrix
tree_pca_scaled <- prcomp(tree_mx, center = TRUE, scale. = TRUE)
summary(tree_pca_scaled)
#> Importance of components:
#>                           PC1    PC2
#> Standard deviation     1.2326 0.6933
#> Proportion of Variance 0.7596 0.2404
#> Cumulative Proportion  0.7596 1.0000
tree_pca_scaled$rotation
#>                    PC1        PC2
#> height_miles 0.7071068 -0.7071068
#> girth        0.7071068  0.7071068
tree_pca_scaled$sdev
#> [1] 1.2325908 0.6933397

We will again plot the original data, but this time using the scaled and centered values, and overlay the plot with the PCs.

pc_scaled_loadings <- t(tree_pca_scaled$rotation) * tree_pca_scaled$sdev

## Reuse plot from before and add PCs
trees_scaled_plot +
  geom_segment(
    data = data.frame(pc_scaled_loadings),
    aes(x = 0, xend = girth, y = 0, yend = height_miles),
    arrow = arrow(length=unit(0.2,"cm"))
  ) +
  geom_text(
    data = data.frame(pc_scaled_loadings),
    aes(x = girth, y = height_miles, label = rownames(pc_scaled_loadings)),
    nudge_x = 0.1, nudge_y = 0.2
  ) +
  coord_equal(xlim = c(-2.3, 2.3), ylim = c(-2.1, 2.1)) +
  ggtitle("Trees data overlayed with PCs",
          subtitle = "Based on PCA on standardized data")

This is more in line with our expectations. PC1 point in the direction of greatest variability, with PC2 orthogonal and pointing in the direction of second greatest variabilty.

Also note from the lengths of the PC vectors that the contributions of height and girth are equal to both PCs.

The biplot:

ggbiplot(tree_pca_scaled, groups = trees$vol_fac, alpha = 0) +
  ## Add points layer to color and size points
  geom_point(aes(col = trees$vol_fac, size = trees$Volume), alpha = 0.6) +
  labs(size = "Volume", col = "") +
  theme(aspect.ratio = 0.5) +
  ggtitle("Biplot for the PCA on standardized data")

We now get a much more realistic result, where the height and girth variables have more equal contributions to the PCs.

PC1 can be interpreted as separating small and large volume trees. A potential explanation of PC2 would be the separation between trees that have a similar volume but differ in their height-to-girth ratios, i.e. short wide trees and long thin trees.

Note that we would get the exact same result (apart maybe from the signs) if we used height on the original feet scale. Since the conversion is just a multiplication by a constant, scaling the column by its standard deviation will give the same result. (You can verify this for yourself by redoing the PCA using the original height column from trees, without converting it to miles.)

3 Exercises

3.1 Heavy metals near the Schelde

Data prep

At the department of analytical and physical chemistry, researchers wanted to investigate the pollution of grasslands in the vicinity of the river Schelde. Concentrations of 8 heavy metals were measured on 19 different locations, each time at a depth of 5 cm and at a depth of 20 cm; the data set is called heavymetals. Vicinity to the river was 0 (far) or 1 (close).

Load in the data as follows:

data("heavymetals")

## Recode the "river" variable
heavymetals$river <- ifelse(heavymetals$river, "close", "far")

heavymetals
dim(heavymetals)
#> [1] 19 18

Note that there are 2 columns per heavy metal, one for the measurement at 5 cm depth and one at 20 cm depth.

Tasks

1. Conduct a PCA using standardized variables. How many PCs would you retain? Motivate the answer/interpret.

Think about which columns you need from the original data!

Solution

First do the PCA, excluding the location and river columns.

## Remove 'location' and 'river' columns when creating matrix
heavymetals_mx <- dplyr::select(heavymetals, -location, -river) %>%
  as.matrix()

## Run PCA on centered and scaled data
heavymetals_pca <- prcomp(heavymetals_mx, center = TRUE, scale. = TRUE)
summary(heavymetals_pca)
#> Importance of components:
#>                           PC1    PC2     PC3     PC4     PC5    PC6     PC7
#> Standard deviation     3.4784 1.4209 0.83048 0.71075 0.50170 0.4020 0.31765
#> Proportion of Variance 0.7562 0.1262 0.04311 0.03157 0.01573 0.0101 0.00631
#> Cumulative Proportion  0.7562 0.8824 0.92549 0.95706 0.97279 0.9829 0.98920
#>                            PC8     PC9    PC10   PC11    PC12    PC13    PC14
#> Standard deviation     0.26517 0.19480 0.18158 0.1324 0.08871 0.06573 0.04041
#> Proportion of Variance 0.00439 0.00237 0.00206 0.0011 0.00049 0.00027 0.00010
#> Cumulative Proportion  0.99359 0.99597 0.99803 0.9991 0.99961 0.99988 0.99999
#>                           PC15     PC16
#> Standard deviation     0.01510 0.003029
#> Proportion of Variance 0.00001 0.000000
#> Cumulative Proportion  1.00000 1.000000

To choose the number of PCs to retain, we look at the proportion of variance that each PC explains. This can be visualized using what is known as a scree plot.

## Calculate total variance by summing the PC variances (sdev's squared)
tot_var <- sum(heavymetals_pca$sdev^2)

## Create data.frame of the proportion of variance explained by each PC
heavymetals_prop_var <- data.frame(
  PC = 1:ncol(heavymetals_pca$x),
  var = heavymetals_pca$sdev^2
) %>%
  ## Using `mutate` to calculate prop. var and cum. prop. var
  mutate(
    prop_var = var / tot_var,
    cum_prop_var = cumsum(var / tot_var)
  )

head(heavymetals_prop_var)

## Plot the proportion of variance explained by each PC
ggplot(heavymetals_prop_var, aes(PC, prop_var)) +
  geom_point() +
  geom_line() +
  geom_vline(xintercept = 2.5, col = "firebrick") +
  scale_x_continuous(breaks = 1:ncol(heavymetals_pca$x)) +
  labs(y = "Proportion of variance") +
  ggtitle("Proportion of variance explained by each PC",
          subtitle = "Heavy metals data")


## Plot the cumulative proportion of variance explained by each PC
ggplot(heavymetals_prop_var, aes(PC, cum_prop_var)) +
  geom_point() +
  geom_line() +
  geom_vline(xintercept = 2.5, col = "firebrick") +
  scale_x_continuous(breaks = 1:ncol(heavymetals_pca$x)) +
  labs(y = "Proportion of variance") +
  ggtitle("Cumulative proportion of variance explained by each PC",
          subtitle = "Heavy metals data")

We decide to keep the first 2 PCs (indicated by the red vertical line), as this coincides with the “elbow” in the scree plot. This leaves us with 88% of the total variance from the original data, which is not bad at all given that we went from 16 to only 2 dimensions! Other common cutoffs are to keep e.g. 90%, 95% or 99% of the original variance. Which would give us 3, 4 or 7 PCs respectively. However, for the purpose of this exercise, the first 2 will be enough.

2. Make a biplot using the retained PCs and interpret. Is there a relationhsip between vicinity to the river and polution with certain metals?

Hint: Try to color or label the data points by their vicinity to the river (using the river variable) to aid with interpretation.

Solution

Making the biplot for the first 2 PCs.

ggbiplot(heavymetals_pca, groups = heavymetals$river) +
  labs(color = "Vicinity to river") +
  ggtitle("Biplot for the heavy metals PCA")

The distinction that is immediately clear is that the levels of Manganese (Mn) seem to be higher in the areas far from the river (at both depths), compared to all other metals.

We can also see this from the loadings, where Mn20 is the only measurement that is negatively correlated with PC1, while Mn5 is barely correlated with PC1.

A potential hypothesis would be that as we move farther away from the river, the concentration of Mn increases with depth.

heavymetals_pca$rotation[, 1:2]
#>              PC1         PC2
#> Cd5   0.26720624  0.08778802
#> Cd20  0.22392129 -0.32389630
#> Cr5   0.26244938  0.13211704
#> Cr20  0.26172408 -0.15414563
#> Cu5   0.27742726  0.03099257
#> Cu20  0.27837188 -0.12633948
#> Fe5   0.24752720  0.30864106
#> Fe20  0.25426152  0.10817270
#> Mn5   0.05295412  0.58384522
#> Mn20 -0.15735932  0.49103123
#> Ni5   0.23967596  0.30017207
#> Ni20  0.26890893  0.06795434
#> Pb5   0.27211702  0.03660204
#> Pb20  0.27863288 -0.11559441
#> Zn5   0.27807226  0.01288582
#> Zn20  0.27271722 -0.17524279
3. Calculate the loadings and scores of the PCA using the SVD (function svd). Verify that the loadings and scores obtained using the SVD approach are equal to those obtained using the prcomp function.
Solution

First perform the SVD. Remember to center and scale the data matrix!

heavymetals_scaled <- scale(heavymetals_mx)
heavymetals_svd <- svd(heavymetals_scaled)

Now compare the PC loadings with the right singular vectors \(\mathbf{V}\) and the scores with the projections \(\mathbf{Z_k} = \mathbf{XV_k}\).

## Remove dimnames for comparison
all.equal(unname(heavymetals_pca$rotation), heavymetals_svd$v)
#> [1] TRUE

## Calculate projections
heavymetals_Zk <- heavymetals_scaled %*% heavymetals_svd$v
heavymetals_scores <- unname(heavymetals_pca$x)

all.equal(heavymetals_Zk, heavymetals_scores)
#> [1] TRUE

This again shows that PCA is nothing more than an SVD on the (centered and scaled) data matrix!

3.2 Employment by industry in European countries

Using the same data as in Lab 1.

Data prep

The "industries" dataset contains data on the distribution of employment between 9 industrial sectors, in 26 European countries. The dataset stems from the Cold-War era; the data are expressed as percentages. Load the data and explore its contents.

## Load 'industries' data from the HDDAData package
data("industries")

# Explore contents
industries
summary(industries)
#>    country           agriculture        mining      manufacturing  
#>  Length:26          Min.   : 2.70   Min.   :0.100   Min.   : 7.90  
#>  Class :character   1st Qu.: 7.70   1st Qu.:0.525   1st Qu.:23.00  
#>  Mode  :character   Median :14.45   Median :0.950   Median :27.55  
#>                     Mean   :19.13   Mean   :1.254   Mean   :27.01  
#>                     3rd Qu.:23.68   3rd Qu.:1.800   3rd Qu.:30.20  
#>                     Max.   :66.80   Max.   :3.100   Max.   :41.20  
#>   power.supply     construction       services        finance      
#>  Min.   :0.1000   Min.   : 2.800   Min.   : 5.20   Min.   : 0.500  
#>  1st Qu.:0.6000   1st Qu.: 7.525   1st Qu.: 9.25   1st Qu.: 1.225  
#>  Median :0.8500   Median : 8.350   Median :14.40   Median : 4.650  
#>  Mean   :0.9077   Mean   : 8.165   Mean   :12.96   Mean   : 4.000  
#>  3rd Qu.:1.1750   3rd Qu.: 8.975   3rd Qu.:16.88   3rd Qu.: 5.925  
#>  Max.   :1.9000   Max.   :11.500   Max.   :19.10   Max.   :11.300  
#>  social.sector     transport    
#>  Min.   : 5.30   Min.   :3.200  
#>  1st Qu.:16.25   1st Qu.:5.700  
#>  Median :19.65   Median :6.700  
#>  Mean   :20.02   Mean   :6.546  
#>  3rd Qu.:24.12   3rd Qu.:7.075  
#>  Max.   :32.40   Max.   :9.400

Tasks

1. Perform a PCA. How many PCs would you retain? Explain.
Solution

Perform the PCA on the centered and scaled data matrix, after removing the country column.

industries_pca <- prcomp(industries[, -1], scale. = TRUE)
summary(industries_pca)
#> Importance of components:
#>                           PC1    PC2    PC3    PC4     PC5    PC6     PC7
#> Standard deviation     1.8674 1.4595 1.0483 0.9972 0.73703 0.6192 0.47514
#> Proportion of Variance 0.3875 0.2367 0.1221 0.1105 0.06036 0.0426 0.02508
#> Cumulative Proportion  0.3875 0.6241 0.7462 0.8568 0.91711 0.9597 0.98480
#>                           PC8      PC9
#> Standard deviation     0.3699 0.006755
#> Proportion of Variance 0.0152 0.000010
#> Cumulative Proportion  1.0000 1.000000
## Calculate total variance by summing the PC variances (sdev's squared)
tot_var <- sum(industries_pca$sdev^2)

## Create data.frame of the proportion of variance explained by each PC
industries_prop_var <- data.frame(
  PC = 1:ncol(industries_pca$x),
  var = industries_pca$sdev^2
) %>%
  ## Using `mutate` to calculate prop. var and cum. prop. var
  mutate(
    prop_var = var / tot_var,
    cum_prop_var = cumsum(var / tot_var)
  )

industries_prop_var

## Plot the proportion of variance explained by each PC
ggplot(industries_prop_var, aes(PC, prop_var)) +
  geom_point() +
  geom_line() +
  geom_vline(xintercept = 5.5, col = "firebrick") +
  scale_x_continuous(breaks = 1:ncol(industries_pca$x)) +
  labs(y = "Proportion of variance") +
  ggtitle("Proportion of variance explained by each PC",
          subtitle = "Industries data")


## Plot the cumulative proportion of variance explained by each PC
ggplot(industries_prop_var, aes(PC, cum_prop_var)) +
  geom_point() +
  geom_line() +
  geom_vline(xintercept = 5.5, col = "firebrick") +
  scale_x_continuous(breaks = 1:ncol(industries_pca$x)) +
  labs(y = "Proportion of variance") +
  ggtitle("Cumulative proportion of variance explained by each PC",
          subtitle = "Industries data")

In this case, retaining the first 5-7 PCs would be more appropriate.

2. What could you say about e.g. Denmark based on the biplot?
Solution

Construct the biplot. We don’t really have a grouping variable to color the points, but we can add labels with the country names.

industries_biplot <- ggbiplot(industries_pca,
  labels = industries$country, labels.size = 2
) +
  ggtitle("Biplot for the industries PCA") +
  xlim(c(-3.4, 3.4)) +
  ylim(c(-2.2, 2.2))
industries_biplot

The biplot shows how the work forces of the countries are distributed among the different industries.

The employment in Denmark seems to be mainly concentrated in the finance, services and social sectors.

3. Try to interpret the first 2 PCs.
Solution

We can use the biplot and loadings to interpret the PCs. The biplot is given above, while the loadings can be accessed from the $rotation slot:

industries_pca$rotation[, 1:2]
#>                        PC1         PC2
#> agriculture    0.523790989  0.05359389
#> mining         0.001323458  0.61780714
#> manufacturing -0.347495131  0.35505360
#> power.supply  -0.255716182  0.26109606
#> construction  -0.325179319  0.05128845
#> services      -0.378919663 -0.35017206
#> finance       -0.074373583 -0.45369785
#> social.sector -0.387408806 -0.22152120
#> transport     -0.366822713  0.20259185

The first PC seems to be largely driven by the agriculture industry, i.e. it is separating countries mostly based on their employment in agriculture () Thus, countries situated on the positive side of PC1 will likely have a higher-than-average employment in agriculture. On the other hand, countries on the negative side of PC1 are less agriculture-based economies and have higher employments in e.g. the social sector.

Indeed, if we rank the countries by their agriculture employment, we largely recover the order of the countries along PC1, with Turkey clearly being the most agriculture-focused (remember that this data is from the Cold War era)!

## Show ranking of agriculture industry
industries %>%
  dplyr::select(country, agriculture, social.sector, mining, finance) %>%
  arrange(desc(agriculture))

We could say that the first PC separates agriculture-based economies from non-agriculture-based. The fact that most other industries are negatively correlated with the first PC, seems to indicate that countries either have a large agriculture industry or distribute their work force more equally among the other industries.

The 2 main exceptions are the mining and finance industries, which are (almost) perpendicular to PC1.

The second PC is mostly driven by the difference between more services-based (on the negative side) and more industry-based (positive PC) economies. With the main drivers being the mining and finance sectors.

Keep in mind however that these 2 PCs “only” explain 38.7% and 23.7% of the total variance, respectively. So there are likely still many patterns we are missing.

Extra: the average country

Where would a country with average employment across all industries lie on the biplot?

Solution

First calculate the “average” country by computing the means of each variable in the industries data. But remember, we centered and scaled the data before calculating the PCA (through the center = TRUE and scale. = TRUE arguments in prcomp). So we have to do the same procedure with our new country. Of course, subtracting the averages from the average country results in all 0’s. So we can represent our average country by a vector of 0’s for each feature.

avg_country <- rep(0, ncol(industries) - 1) # -1 for the 'country' column

Next, we project our new country on to the PCA space, using

\[ Z = XV_{k}\]

where \(V\) are the PC loadings (or right singular vectors in SVD terms) and \(k\) are the chosen number of dimensions (2 in this case).

Of course, since \(X\) here consists entirely of zeros, the resulting projection will also be 0 everywhere, and you can see why the average country will lie in the center of the biplot!

(avg_country_pc <- avg_country %*% industries_pca$rotation)
#>      PC1 PC2 PC3 PC4 PC5 PC6 PC7 PC8 PC9
#> [1,]   0   0   0   0   0   0   0   0   0
## Reformat a bit to make it consistent with the data in the biplot
avg_country_biplot <- data.frame(
  xvar = avg_country_pc[, "PC1"],
  yvar = avg_country_pc[, "PC2"],
  labels = "AVERAGE", row.names = NULL
)

## Add the average country to the biplot
industries_biplot +
  geom_point(
    data = avg_country_biplot, size = 3,
    shape = 23, fill = "dodgerblue"
  ) +
  geom_label_repel(
    data = avg_country_biplot, aes(label = labels),
    nudge_x = 1, nudge_y = 0.5, size = 4,
    color = "dodgerblue"
  ) + labs(subtitle = "Average country highlighted")

4 Further reading

Here are some further resources that can help with the interpretation of the PCA and its link with the SVD:

Session info

Session info
#> [1] "2024-10-07 12:42:13 CEST"
#> ─ Session info ───────────────────────────────────────────────────────────────
#>  setting  value
#>  version  R version 4.4.0 RC (2024-04-16 r86468)
#>  os       macOS Big Sur 11.6
#>  system   aarch64, darwin20
#>  ui       X11
#>  language (EN)
#>  collate  en_US.UTF-8
#>  ctype    en_US.UTF-8
#>  tz       Europe/Brussels
#>  date     2024-10-07
#>  pandoc   3.1.1 @ /Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/ (via rmarkdown)
#> 
#> ─ Packages ───────────────────────────────────────────────────────────────────
#>  package     * version date (UTC) lib source
#>  bookdown      0.40    2024-07-02 [1] CRAN (R 4.4.0)
#>  bslib         0.8.0   2024-07-29 [1] CRAN (R 4.4.0)
#>  cachem        1.1.0   2024-05-16 [1] CRAN (R 4.4.0)
#>  cli           3.6.3   2024-06-21 [1] CRAN (R 4.4.0)
#>  colorspace    2.1-1   2024-07-26 [1] CRAN (R 4.4.0)
#>  digest        0.6.37  2024-08-19 [1] CRAN (R 4.4.1)
#>  dplyr       * 1.1.4   2023-11-17 [1] CRAN (R 4.4.0)
#>  evaluate      1.0.0   2024-09-17 [1] CRAN (R 4.4.1)
#>  fansi         1.0.6   2023-12-08 [1] CRAN (R 4.4.0)
#>  farver        2.1.2   2024-05-13 [1] CRAN (R 4.4.0)
#>  fastmap       1.2.0   2024-05-15 [1] CRAN (R 4.4.0)
#>  forcats     * 1.0.0   2023-01-29 [1] CRAN (R 4.4.0)
#>  generics      0.1.3   2022-07-05 [1] CRAN (R 4.4.0)
#>  ggbiplot    * 0.55    2024-10-02 [1] Github (vqv/ggbiplot@f7ea76d)
#>  ggplot2     * 3.5.1   2024-04-23 [1] CRAN (R 4.4.0)
#>  ggrepel     * 0.9.6   2024-09-07 [1] CRAN (R 4.4.1)
#>  glue          1.8.0   2024-09-30 [1] CRAN (R 4.4.1)
#>  gtable        0.3.5   2024-04-22 [1] CRAN (R 4.4.0)
#>  HDDAData    * 1.0.1   2024-10-02 [1] Github (statOmics/HDDAData@b832c71)
#>  highr         0.11    2024-05-26 [1] CRAN (R 4.4.0)
#>  hms           1.1.3   2023-03-21 [1] CRAN (R 4.4.0)
#>  htmltools     0.5.8.1 2024-04-04 [1] CRAN (R 4.4.0)
#>  jquerylib     0.1.4   2021-04-26 [1] CRAN (R 4.4.0)
#>  jsonlite      1.8.9   2024-09-20 [1] CRAN (R 4.4.1)
#>  knitr         1.48    2024-07-07 [1] CRAN (R 4.4.0)
#>  labeling      0.4.3   2023-08-29 [1] CRAN (R 4.4.0)
#>  lifecycle     1.0.4   2023-11-07 [1] CRAN (R 4.4.0)
#>  lubridate   * 1.9.3   2023-09-27 [1] CRAN (R 4.4.0)
#>  magrittr      2.0.3   2022-03-30 [1] CRAN (R 4.4.0)
#>  munsell       0.5.1   2024-04-01 [1] CRAN (R 4.4.0)
#>  pillar        1.9.0   2023-03-22 [1] CRAN (R 4.4.0)
#>  pkgconfig     2.0.3   2019-09-22 [1] CRAN (R 4.4.0)
#>  plyr        * 1.8.9   2023-10-02 [1] CRAN (R 4.4.0)
#>  purrr       * 1.0.2   2023-08-10 [1] CRAN (R 4.4.0)
#>  R6            2.5.1   2021-08-19 [1] CRAN (R 4.4.0)
#>  Rcpp          1.0.13  2024-07-17 [1] CRAN (R 4.4.0)
#>  readr       * 2.1.5   2024-01-10 [1] CRAN (R 4.4.0)
#>  rlang         1.1.4   2024-06-04 [1] CRAN (R 4.4.0)
#>  rmarkdown     2.28    2024-08-17 [1] CRAN (R 4.4.0)
#>  rstudioapi    0.16.0  2024-03-24 [1] CRAN (R 4.4.0)
#>  sass          0.4.9   2024-03-15 [1] CRAN (R 4.4.0)
#>  scales      * 1.3.0   2023-11-28 [1] CRAN (R 4.4.0)
#>  sessioninfo   1.2.2   2021-12-06 [1] CRAN (R 4.4.0)
#>  stringi       1.8.4   2024-05-06 [1] CRAN (R 4.4.0)
#>  stringr     * 1.5.1   2023-11-14 [1] CRAN (R 4.4.0)
#>  tibble      * 3.2.1   2023-03-20 [1] CRAN (R 4.4.0)
#>  tidyr       * 1.3.1   2024-01-24 [1] CRAN (R 4.4.0)
#>  tidyselect    1.2.1   2024-03-11 [1] CRAN (R 4.4.0)
#>  tidyverse   * 2.0.0   2023-02-22 [1] CRAN (R 4.4.0)
#>  timechange    0.3.0   2024-01-18 [1] CRAN (R 4.4.0)
#>  tzdb          0.4.0   2023-05-12 [1] CRAN (R 4.4.0)
#>  utf8          1.2.4   2023-10-22 [1] CRAN (R 4.4.0)
#>  vctrs         0.6.5   2023-12-01 [1] CRAN (R 4.4.0)
#>  withr         3.0.1   2024-07-31 [1] CRAN (R 4.4.0)
#>  xfun          0.47    2024-08-17 [1] CRAN (R 4.4.0)
#>  yaml          2.3.10  2024-07-26 [1] CRAN (R 4.4.0)
#> 
#>  [1] /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/library
#> 
#> ──────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiTGFiIDI6IFByaW5jaXBhbCBDb21wb25lbnQgQW5hbHlzaXMiCnN1YnRpdGxlOiAiSGlnaCBEaW1lbnNpb25hbCBEYXRhIEFuYWx5c2lzIHByYWN0aWNhbHMiCmF1dGhvcjogIkFkYXB0ZWQgYnkgTWlsYW4gTWFsZmFpdCIKZGF0ZTogIjEwIEZlYiAyMDIyIDxici8+IChMYXN0IHVwZGF0ZWQ6IDIwMjItMDItMTApIgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBjYWNoZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGNvbGxhcHNlID0gVFJVRSwKICBjb21tZW50ID0gIiM+IiwKICBmaWcuYWxpZ24gPSAiY2VudGVyIiwKICBvdXQud2lkdGggPSAiMTAwJSIKKQpgYGAKCiMjIyBbQ2hhbmdlIGxvZ10oaHR0cHM6Ly9naXRodWIuY29tL3N0YXRPbWljcy9IRERBL2NvbW1pdHMvbWFzdGVyL0xhYjItUENBLlJtZCkgey19CgoqKioKCmBgYHtyIGxpYnJhcmllcywgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KIyMgSW5zdGFsbCBuZWNlc3NhcnkgcGFja2FnZXMgd2l0aDoKIyBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQojIGluc3RhbGwucGFja2FnZXMoImdncmVwZWwiKQojIGlmICghcmVxdWlyZU5hbWVzcGFjZSgicmVtb3RlcyIsIHF1aWV0bHkgPSBUUlVFKSkgewojICAgICBpbnN0YWxsLnBhY2thZ2VzKCJyZW1vdGVzIikKIyB9CiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInN0YXRPbWljcy9IRERBRGF0YSIpCiMgcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoInZxdi9nZ2JpcGxvdCIpCgpsaWJyYXJ5KHRpZHl2ZXJzZSkKdGhlbWVfc2V0KHRoZW1lX2xpZ2h0KCkpCgpsaWJyYXJ5KGdnYmlwbG90KQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkoSEREQURhdGEpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KClRoZSBmaXJzdCBwYXJ0IG9mIHRoaXMgbGFiIGRlbW9uc3RyYXRlcyB0aGUgaW5mbHVlbmNlIG9mIHN0YW5kYXJkaXppbmcgdGhlIGRhdGEgKGkuZS4gd29ya2luZyBvbiB0aGUKY29ycmVsYXRpb24gbWF0cml4IHZzLiB3b3JraW5nIG9uIHRoZSBjb3ZhcmlhbmNlIG1hdHJpeCkuIFBheSBhdHRlbnRpb24gdG8gd2hhdCB0aGUgb3V0cHV0IGxvb2tzCmxpa2UgYW5kIGhvdyBpdCBsaW5rcyB0byB0aGUgYmlwbG90LikKCiMgUENBIGRlbW9uc3RyYXRpb24KCiMjIERhdGEgcHJlcAoKV2Ugd2lsbCBsb2FkIHRoZSBgdHJlZXNgIGRhdGFzZXQgKGZyb20gYmFzZSBgUmAsIHNlZSBgP3RyZWVzYCBmb3IgbW9yZSBpbmZvKSB0aGF0IGNvbnRhaW5zIHRoZQpoZWlnaHQgb2YgYSB0cmVlIChpbiAqZmVldCopLCB0aGUgZ2lydGggKG9yIGRpYW1ldGVyIGluICppbmNoZXMqKSBhbmQgdGhlIHZvbHVtZSAoaW4gKmN1YmljIGZlZXQqKQpvZiB0aGUgdHJlZS4gRm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgZXhlcmNpc2UsIHdlIHdpbGwgY29udmVydCB0aGUgY29udGludW91cyAqdm9sdW1lKiB2YXJpYWJsZSB0byBhCmNhdGVnb3JpY2FsIHZhcmlhYmxlIChhIGBmYWN0b3JgIGluIFIgbGluZ28pLiBBIHRyZWUgd2lsbCBiZSBjb25zaWRlcmVkIGxhcmdlIGlmIGl0cyB2b2x1bWUgaXMKYmlnZ2VyIHRoYW4gMjUgY3ViaWMgZmVldCwgYW5kIHNtYWxsIG90aGVyd2lzZS4KCmBgYHtyfQojIExvYWQgZGF0YQpkYXRhKHRyZWVzKQoKIyBDb252ZXJ0IHZvbHVtZSB0byBmYWN0b3IKdHJlZXMkdm9sX2ZhYyA8LSBhcy5mYWN0b3IoaWZlbHNlKHRyZWVzJFZvbHVtZSA+IDI1LCAibGFyZ2UiLCAic21hbGwiKSkKCiMgUHJldmlldyBkYXRhCmhlYWQodHJlZXMpCnN1bW1hcnkodHJlZXMpCmBgYAoKTm93IHN1cHBvc2UgdGhhdCB0aGUgaGVpZ2h0IHdhcyBhY3R1YWxseSBtZWFzdXJlZCBpbiAqKm1pbGVzKiogaW5zdGVhZCBvZiBmZWV0LgoKYGBge3J9CiMjIENyZWF0ZSBuZXcgY29sdW1uIGluIHRyZWVzIHdpdGggaGVpZ2h0IGluIG1pbGVzCnRyZWVzJGhlaWdodF9taWxlcyA8LSB0cmVlcyRIZWlnaHQgLyA1MjgwCgojIENyZWF0ZSBtYXRyaXggdXNpbmcgaGVpZ2h0X21pbGVzIGFuZCBHaXJ0aCB2YXJpYWJsZXMKdHJlZV9teCA8LSBjYmluZCgiaGVpZ2h0X21pbGVzIiA9IHRyZWVzJGhlaWdodF9taWxlcywgImdpcnRoIiA9IHRyZWVzJEdpcnRoKQpoZWFkKHRyZWVfbXgpCmBgYAoKQWx3YXlzIGdvb2QgdG8gdmlzdWFsaXplIHRoZSBkYXRhLiBIZXJlIHdlIHBsb3QgdGhlIGhlaWdodCAoaW4gbWlsZXMpIHZzLiB0aGUgZ2lydGggZm9yIGVhY2ggdHJlZQphbmQgc2l6ZSB0aGUgZG90cyBhY2NvcmRpbmcgdG8gdGhlaXIgdm9sdW1lLiBXZSBhbHNvIHVzZSBhIGNvbG9yIGFlc3RoZXRpYyB0byBkaXN0aW5ndWlzaCAibGFyZ2UiCmFuZCAic21hbGwiIHRyZWVzLgoKYGBge3IgdHJlZXMtcGxvdH0KdHJlZXNfcGxvdCA8LSBnZ3Bsb3QodHJlZXMpICsKICBnZW9tX3BvaW50KGFlcyhHaXJ0aCwgaGVpZ2h0X21pbGVzLCBzaXplID0gVm9sdW1lLCBjb2wgPSB2b2xfZmFjKSwKICAgICAgICAgICAgIGFscGhhID0gMC42KSArCiAgbGFicyh4ID0gIkdpcnRoIChpbmNoZXMpIiwgeSA9ICJIZWlnaHQgKG1pbGVzKSIsCiAgICAgICBjb2xvciA9ICJWb2x1bWUgY2xhc3MiKQoKdHJlZXNfcGxvdCArCiAgZ2d0aXRsZSgiVmlzdWFsaXppbmcgdGhlIG9yaWdpbmFsIHRyZWVzIGRhdGEiKQoKYGBgCgpQYXkgYXR0ZW50aW9uIHRvIHRoZSB1bml0cyBvbiB0aGUgYXhpcyBhbmQgdGhlICh2ZXJ5KSBkaWZmZXJlbnQgb3JkZXJzIG9mIHRoZSB1bml0cy4KCj4gUTogbG9va2luZyBhdCB0aGlzIHBsb3QsIGNhbiB5b3UgbWFrZSBhIGd1ZXNzIGluIHdoaWNoIGRpcmVjdGlvbiB0aGUgbGFyZ2VzdCB2YXJpYXRpb24gbGllcywgaS5lLgo+IGluIHdoaWNoIGRpcmVjdGlvbiB0aGUgZmlyc3QgcHJpbmNpcGFsIGNvbXBvbmVudCB3b3VsZCBsaWU/CgpUbyBoZWxwIHdpdGggdmlzdWFsaXphdGlvbiBvZiB0aGUgUENzIGxhdGVyIG9uLCB3ZSBhbHNvIG1ha2UgdGhlIHBsb3QgdXNpbmcgdGhlICoqY2VudGVyZWQgYW5kCnNjYWxlZCoqIGRhdGEuIFRoZSBgc2NhbGVgIGZ1bmN0aW9uIGNhbiBiZSB1c2VkIGZvciB0aGlzLCBzbyB0aGF0IGFsbCB2YXJpYWJsZXMgaGF2ZSBtZWFuIDAgYW5kIHVuaXQKdmFyaWFuY2UuCgpgYGB7cn0KIyMgQ2VudGVyIGFuZCBzY2FsZSBkYXRhCnRyZWVzX3NjYWxlZCA8LSBzY2FsZSh0cmVlX214LCBjZW50ZXIgPSBUUlVFLCBzY2FsZSA9IFRSVUUpCgp0cmVlc19zY2FsZWRfcGxvdCA8LSB0cmVlc19zY2FsZWQgJT4lCiAgIyMgQ29udmVydCB0byBkYXRhLmZyYW1lIGFuZCByZS1hZGQgVm9sdW1lIGNvbHVtbnMgZm9yIHBsb3R0aW5nCiAgZGF0YS5mcmFtZShWb2x1bWUgPSB0cmVlcyRWb2x1bWUsIHZvbF9mYWMgPSB0cmVlcyR2b2xfZmFjKSAlPiUKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoZ2lydGgsIGhlaWdodF9taWxlcywgc2l6ZSA9IFZvbHVtZSwgY29sID0gdm9sX2ZhYyksCiAgICAgICAgICAgICBhbHBoYSA9IDAuNikgKwogIGxhYnMoeCA9ICJHaXJ0aCAoaW5jaGVzKSwgc3RhbmRhcmRpemVkIiwgeSA9ICJIZWlnaHQgKG1pbGVzKSwgc3RhbmRhcmRpemVkIiwKICAgICAgIGNvbG9yID0gIiIpCgp0cmVlc19zY2FsZWRfcGxvdCArCiAgZ2d0aXRsZSgiVmlzdWFsaXppbmcgdGhlIHNjYWxlZCB0cmVlcyBkYXRhIikgKwogIGNvb3JkX2VxdWFsKHhsaW0gPSBjKC0yLjMsIDIuMyksIHlsaW0gPSBjKC0yLjMsIDIuMykpCmBgYAoKIyMgUnVuIFBDQQoKV2UgcnVuIFBDQSBvbiB0aGUgKmhlaWdodCAoaW4gbWlsZXMpKiBhbmQgKmdpcnRoKiB2YXJpYWJsZXMgYW5kIGluc3BlY3QgdGhlIHJlc3VsdHMuCgpgYGB7cn0KIyBSdW4gUENBIHdpdGggcHJjb21wIGZ1bmN0aW9uLCB3aGljaCB1c2VzIFNWRCBpbnRlcm5hbGx5IChzZWUgP3ByY29tcCkKIyBOb3RlIHRoYXQgcHJjb21wIGNlbnRlcnMgdGhlIG1hdHJpeCBpbnRlcm5hbGx5IGJ5IGRlZmF1bHQgYnV0IGRvZXMgbm90IHNjYWxlIGl0CiMgKGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IEZBTFNFKQp0cmVlX3BjYSA8LSBwcmNvbXAodHJlZV9teCkKc3VtbWFyeSh0cmVlX3BjYSkKYGBgCgoqKk5vdGU6KiogVGhlIGZpcnN0IGNvbXBvbmVudCBleHBsYWlucyBhbG1vc3QgMTAwJSBvZiB0aGUgdmFyaWFiaWxpdHkgaW4gdGhlIGRhdGEuCgpUaGUgKipsb2FkaW5ncyoqIG9mIHRoZSBQQ0EgYXJlIHN0b3JlZCBpbiB0aGUgYCRyb3RhdGlvbmAgc2xvdCBvZiB0aGUgYHByY29tcGAgcmVzdWx0LCB3aGlsZSB0aGUKYCRzZGV2YCBzbG90IGNvbnRhaW5zIHRoZSBzdGFuZGFyZCBkZXZpYXRpb25zIG9mIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cy4KCmBgYHtyfQp0cmVlX3BjYSRyb3RhdGlvbgp0cmVlX3BjYSRzZGV2CmBgYAoKUmVtZW1iZXIgdGhhdCB0aGUgUHJpbmNpcGFsIENvbXBvbmVudHMgYXJlICoqbGluZWFyIGNvbWJpbmF0aW9ucyoqIG9mIHRoZSBvcmlnaW5hbCB2YXJpYWJsZXMuIFRoZQpsb2FkaW5ncyB0ZWxsIHlvdSB3aGF0IHRoZSBjb250cmlidXRpb24gKG9yIHdlaWdodCkgb2YgZWFjaCB2YXJpYWJsZSBpcyB0byB0aGUgUEMuIEhlcmUgd2Ugc2VlIHRoYXQKdGhlIGZpcnN0IFBDIGlzIGNvbXBsZXRlbHkgZG9taW5hdGVkIGJ5IHRoZSBnaXJ0aCB2YXJpYWJsZSwgd2hpbGUgdGhlIHNlY29uZCBjb21wb25lbnQgaXMgYmFzaWNhbGx5Cih0aGUgbmVnYXRpdmUpIGhlaWdodCB2YXJpYWJsZS4gU2luY2UgdGhlIFBDcyBhcmUgb3JkZXJlZCBieSB0aGUgYW1vdW50IG9mIHZhcmlhbmNlIHRoZXkgcmV0YWluIGZyb20KdGhlIG9yaWdpbmFsIGRhdGEsIHdlIHdvdWxkIGNvbmNsdWRlIHRoYXQgbW9zdCBvZiB0aGUgdmFyaWFuY2UgaW4gdGhlIGRhdGEgY29tZXMgZnJvbSB0aGUgZ2lydGgKdmFyaWFibGUuCgo+IFE6IElzIHRoaXMgaW4gbGluZSB3aXRoIHdoYXQgeW91IGV4cGVjdGVkIGZyb20gdGhlIG9yaWdpbmFsIHBsb3Q/IFdoeSBub3Q/IChUaGluayBhYm91dCB0aGUgdW5pdHMKPiB3ZSBhcmUgdXNpbmcgaGVyZSEpCgpUaGUgcmVzdWx0IGZyb20gYHByY29tcGAgYWxzbyBjb250YWlucyBhbiBgJHhgIHNsb3QsIHdoaWNoIGNvbnRhaW5zIHRoZSBwcm9qZWN0ZWQgdmFsdWVzIG9mIHRoZQpvcmlnaW5hbCBkYXRhIG1hdHJpeCBvbnRvIHRoZSBwcmluY2lwYWwgY29tcG9uZW50cyAoYWxzbyBjYWxsZWQgdGhlICpQQyBzY29yZXMqKS4gVGhpcyBpcyB3aGF0IHdlCndpbGwgdXNlIHRvIGNvbnN0cnVjdCB0aGUgUENBIHBsb3QuCgojIyMgVmlzdWFsaXplIFByaW5jaXBhbCBDb21wb25lbnRzCgpTY2FsZSB0aGUgUEMgbG9hZGluZ3MgYnkgdGhlaXIgc3RhbmRhcmQgZGV2aWF0aW9ucyAoc2luZ3VsYXIgdmFsdWVzKSB0byBwcm9qZWN0IHRoZW0gYmFjayB0byB0aGUKb3JpZ2luYWwgZGF0YSBzcGFjZS4gV2UgYWxzbyB0cmFuc3Bvc2UgdGhlIGByb3RhdGlvbmAgbWF0cml4IHNvIHRoYXQgdGhlIHZhcmlhYmxlcyBhcmUgaW4gY29sdW1ucwphbmQgUENzIGluIHRoZSByb3dzLCBzbyB0aGF0IHdlIGNhbiBvdmVybGF5IHRoZW0gb24gdGhlIG9yaWdpbmFsIHRyZWVzIHBsb3QuCgpgYGB7ciB0cmVlLXBsb3QtUEMtbG9hZGluZ3N9CiMjIFRyYW5zcG9zZSB0aGUgbG9hZGluZ3Mgc28gdGhhdCB0aGUgUENzIGFyZSBpbiB0aGUgcm93cywgZm9yIHBsb3R0aW5nCnBjX2xvYWRpbmdzIDwtIHQodHJlZV9wY2Ekcm90YXRpb24pICogdHJlZV9wY2Ekc2RldgoKIyMgUmV1c2UgcGxvdCBmcm9tIGJlZm9yZSBhbmQgYWRkIFBDcwp0cmVlc19zY2FsZWRfcGxvdCArCiAgZ2VvbV9zZWdtZW50KAogICAgZGF0YSA9IGRhdGEuZnJhbWUocGNfbG9hZGluZ3MpLAogICAgYWVzKHggPSAwLCB4ZW5kID0gZ2lydGgsIHkgPSAwLCB5ZW5kID0gaGVpZ2h0X21pbGVzKSwKICAgIGFycm93ID0gYXJyb3cobGVuZ3RoPXVuaXQoMC4xLCJjbSIpKQogICkgKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBkYXRhLmZyYW1lKHBjX2xvYWRpbmdzKSwKICAgIGFlcyh4ID0gZ2lydGgsIHkgPSBoZWlnaHRfbWlsZXMsIGxhYmVsID0gcm93bmFtZXMocGNfbG9hZGluZ3MpKSwKICAgIHZqdXN0ID0gMS41CiAgKSArCiAgZ2d0aXRsZSgiVHJlZXMgZGF0YSBvdmVybGF5ZWQgd2l0aCBQQ3MiLAogICAgICAgICAgc3VidGl0bGUgPSAiQmFzZWQgb24gUENBIG9uIG5vbi1zdGFuZGFyZGl6ZWQgZGF0YSIpCmBgYAoKRnJvbSB0aGlzIHBsb3Qgd2Ugc2VlIHRoYXQgdGhlIFBDcyBhcmUgbm90IGluIHRoZSBkaXJlY3Rpb25zIHdlIGV4cGVjdGVkLiBQQzEgc2hvdWxkIHBvaW50IGluIHRoZQpkaXJlY3Rpb24gb2YgZ3JlYXRlc3QgdmFyaWFiaWxpdHkuCgojIyMgVmlzdWFsaXplIFBDQSB3aXRoIGJpcGxvdAoKYGBge3IgdHJlZS1wY2EtYmlwbG90fQpnZ2JpcGxvdCh0cmVlX3BjYSwgZ3JvdXBzID0gdHJlZXMkdm9sX2ZhYywgYWxwaGEgPSAwKSArCiAgIyMgQWRkIHBvaW50cyBsYXllciB0byBjb2xvciBhbmQgc2l6ZSBwb2ludHMKICBnZW9tX3BvaW50KGFlcyhjb2wgPSB0cmVlcyR2b2xfZmFjLCBzaXplID0gdHJlZXMkVm9sdW1lKSwgYWxwaGEgPSAwLjYpICsKICBsYWJzKHNpemUgPSAiVm9sdW1lIiwgY29sID0gIiIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAwLjYpICsKICBnZ3RpdGxlKCJCaXBsb3QgZm9yIHRoZSBQQ0Egb24gbm9uLXN0YW5kYXJkaXplZCBkYXRhIikKYGBgCgpXZSBzZWUgdGhhdCB0cmVlcyBoaWdoIGluIHZvbHVtZSB0ZW5kIHRvIGhhdmUgYSBoaWdoIHRyZWUgZ2lydGgsIGJ1dCB0aGUgaGVpZ2h0IGRvZXMgbm90IGdpdmUgYW55CmluZm9ybWF0aW9uIG9uIHRyZWUgdm9sdW1lLiBUaGlzIGlzIGxpa2VseSB3cm9uZywgc2luY2Ugd2Uga25vdyB0aGF0IHRoZSBoZWlnaHQgb2YgYSB0cmVlIHNob3VsZApoYXZlIGF0IGxlYXN0IHNvbWUgaW5mbHVlbmNlIG9uIGl0cyB2b2x1bWUuIFRoZSBwcm9ibGVtIGhlcmUgaXMgdGhhdCBiZWNhdXNlIG9mIHRoZSAyIHZlcnkgZGlmZmVyZW50CnVuaXQgbWVhc3VyZXMgKCptaWxlcyogYW5kICppbmNoZXMqKSwgdGhlIGluZmx1ZW5jZSBvZiB0aGUgZ2lydGggaXMgaW5mbGF0ZWQganVzdCBiZWNhdXNlIHRoZSBvcmRlcgpvZiB0aGUgc2NhbGUgaXMgbXVjaCBsYXJnZXIuCgpUaGlzIGlzIGFsc28gcmVmbGVjdGVkIGluIHRoZSB2YXJpYW5jZXMgb2YgdGhlc2UgdmFyaWFibGVzOgoKYGBge3J9CnJvdW5kKGRpYWcoY292KHRyZWVfbXgpKSwgOCkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgdmFyaWFuY2Ugb2YgdGhlICpnaXJ0aCogdmFyaWFibGUgaXMgc2V2ZXJhbCBvcmRlcnMgb2YgbWFnbml0dWRlIGxhcmdlciB0aGFuIHRoYXQgb2YKdGhlIGhlaWdodCAoYWdhaW4sIGJlY2F1c2Ugb2YgdGhlIGRpZmZlcmVudCB1bml0cykgYW5kIHRoaXMgaXMgcmVmbGVjdGVkIGluIHRoZSBQQ0EuCgpXZSBjb3VsZCB1c2UgdGhlIHNhbWUgdW5pdHMsIG9yIHdlIGNvdWxkIHN0YW5kYXJkaXplIHRoZSB2YXJpYWJsZXMgYnkgZGl2aWRpbmcgYnkgdGhlaXIgc3RhbmRhcmQKZGV2aWF0aW9ucy4gQm90aCB3aWxsIGxlYWQgdG8gYSBtb3JlIGJhbGFuY2VkIHBpY3R1cmUgb2YgdGhlIHZhcmlhYmlsaXR5LiBPZiBjb3Vyc2UgaW4gdGhpcyBjYXNlIG9uZQpjYW4gYXJndWUgdGhhdCB0aGUgdmFyaWFibGVzIHNob3VsZCBoYXZlIHRoZSBzYW1lIHVuaXQgYnV0IG5vdCBiZSBzdGFuZGFyZGl6ZWQsIHdoaWNoIG1heSBiZSBhIHZhbGlkCmFyZ3VtZW50LCB3ZXJlIGl0IG5vdCB0aGF0IHdlIGFyZSBtZWFzdXJpbmcgdHdvIGRpZmZlcmVudCB0aGluZ3MgKHRoZSBoZWlnaHQgYW5kIHRoZSBkaWFtZXRlcikuIFNvCmV2ZW4gaWYgd2UgdXNlZCB0aGUgc2FtZSB1bml0cywgaXQgaXMgcmVjb21tZW5kZWQgdG8gYWxzbyBzdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzLgoKSW1hZ2luZSBpZiB3ZSB3b3VsZCBiZSBtZWFzdXJpbmcgdGhlIG1hc3MgKCRrZyQpIG9mIHRoZSB0cmVlIGFuZCB0aGUgZ2lydGggKCRtJCkgb2YgdGhlIHRyZWUsIHRoZQpzY2FsZSBvbiB3aGljaCBib3RoIHNob3VsZCBiZSBtZWFzdXJlZCBpcyBubyBsb25nZXIgY2xlYXIsIHNpbmNlIG5pZXRoZXIga2lsb2dyYW1zIGNhbiBiZSBjb252ZXJ0ZWQKdG8gbWV0ZXJzIG5vciBtZXRlcnMgY29udmVydGVkIHRvIGtpbG9ncmFtcy4gSW4gdGhpcyBjYXNlIHdlIGhhdmUgYSBjbGVhciBhcmd1bWVudCB0byB3b3JrIG9uIHRoZQpzdGFuZGFyZGl6ZWQgdmFyaWFibGVzLgoKIyMgUmVkbyBQQ0Egb24gc3RhbmRhcmRpemVkIHZhcmlhYmxlcwoKV2Ugd2lsbCBsZWF2ZSB0aGUgKmhlaWdodCogb24gdGhlICptaWxlcyogc2NhbGUsIGJ1dCBub3cgd2Ugd2lsbCBzdGFuZGFyZGl6ZSB0aGUgdmFyaWFibGVzIGJlZm9yZQpwZXJmb3JtaW5nIHRoZSBQQ0EuIEkuZS4gaW4gYWRkaXRpb24gdG8gKmNlbnRlcmluZyogdGhlIG1hdHJpeCAoc3VidHJhY3RpbmcgdGhlIGNvbHVtbiBtZWFucyksIHdlCmFsc28gZGl2aWRlIGl0IGJ5IGl0cyBjb2x1bW4gc3RhbmRhcmQgZGV2aWF0aW9ucy4gTm90ZSB0aGF0IHRoZXNlIG9wZXJhdGlvbnMgY2FuIGJlIGRvbmUgaW4gb25lIGdvCndpdGggdGhlIGBwcmNvbXBgIGZ1bmN0aW9uIGJ5IHNwZWNpZnlpbmcgYGNlbnRlciA9IFRSVUVgIGFuZCBgc2NhbGUuID0gVFJVRWAgKG5vdGUgdGhlIGAuYCEpLgoKYGBge3J9CiMjIENvbXB1dGUgUENBIG9uIGNlbnRlcmVkIGFuZCBzY2FsZWQgbWF0cml4CnRyZWVfcGNhX3NjYWxlZCA8LSBwcmNvbXAodHJlZV9teCwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSkKc3VtbWFyeSh0cmVlX3BjYV9zY2FsZWQpCnRyZWVfcGNhX3NjYWxlZCRyb3RhdGlvbgp0cmVlX3BjYV9zY2FsZWQkc2RldgpgYGAKCldlIHdpbGwgYWdhaW4gcGxvdCB0aGUgb3JpZ2luYWwgZGF0YSwgYnV0IHRoaXMgdGltZSB1c2luZyB0aGUgKipzY2FsZWQgYW5kIGNlbnRlcmVkKiogdmFsdWVzLCBhbmQKb3ZlcmxheSB0aGUgcGxvdCB3aXRoIHRoZSBQQ3MuCgpgYGB7cn0KcGNfc2NhbGVkX2xvYWRpbmdzIDwtIHQodHJlZV9wY2Ffc2NhbGVkJHJvdGF0aW9uKSAqIHRyZWVfcGNhX3NjYWxlZCRzZGV2CgojIyBSZXVzZSBwbG90IGZyb20gYmVmb3JlIGFuZCBhZGQgUENzCnRyZWVzX3NjYWxlZF9wbG90ICsKICBnZW9tX3NlZ21lbnQoCiAgICBkYXRhID0gZGF0YS5mcmFtZShwY19zY2FsZWRfbG9hZGluZ3MpLAogICAgYWVzKHggPSAwLCB4ZW5kID0gZ2lydGgsIHkgPSAwLCB5ZW5kID0gaGVpZ2h0X21pbGVzKSwKICAgIGFycm93ID0gYXJyb3cobGVuZ3RoPXVuaXQoMC4yLCJjbSIpKQogICkgKwogIGdlb21fdGV4dCgKICAgIGRhdGEgPSBkYXRhLmZyYW1lKHBjX3NjYWxlZF9sb2FkaW5ncyksCiAgICBhZXMoeCA9IGdpcnRoLCB5ID0gaGVpZ2h0X21pbGVzLCBsYWJlbCA9IHJvd25hbWVzKHBjX3NjYWxlZF9sb2FkaW5ncykpLAogICAgbnVkZ2VfeCA9IDAuMSwgbnVkZ2VfeSA9IDAuMgogICkgKwogIGNvb3JkX2VxdWFsKHhsaW0gPSBjKC0yLjMsIDIuMyksIHlsaW0gPSBjKC0yLjEsIDIuMSkpICsKICBnZ3RpdGxlKCJUcmVlcyBkYXRhIG92ZXJsYXllZCB3aXRoIFBDcyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJCYXNlZCBvbiBQQ0Egb24gc3RhbmRhcmRpemVkIGRhdGEiKQpgYGAKClRoaXMgaXMgbW9yZSBpbiBsaW5lIHdpdGggb3VyIGV4cGVjdGF0aW9ucy4gUEMxIHBvaW50IGluIHRoZSBkaXJlY3Rpb24gb2YgZ3JlYXRlc3QgdmFyaWFiaWxpdHksIHdpdGgKUEMyIG9ydGhvZ29uYWwgYW5kIHBvaW50aW5nIGluIHRoZSBkaXJlY3Rpb24gb2Ygc2Vjb25kIGdyZWF0ZXN0IHZhcmlhYmlsdHkuCgpBbHNvIG5vdGUgZnJvbSB0aGUgbGVuZ3RocyBvZiB0aGUgUEMgdmVjdG9ycyB0aGF0IHRoZSBjb250cmlidXRpb25zIG9mIGhlaWdodCBhbmQgZ2lydGggYXJlIGVxdWFsIHRvCmJvdGggUENzLgoKVGhlIGJpcGxvdDoKCmBgYHtyIHRyZWUtc2NhbGVkLXBjYS1iaXBsb3R9CmdnYmlwbG90KHRyZWVfcGNhX3NjYWxlZCwgZ3JvdXBzID0gdHJlZXMkdm9sX2ZhYywgYWxwaGEgPSAwKSArCiAgIyMgQWRkIHBvaW50cyBsYXllciB0byBjb2xvciBhbmQgc2l6ZSBwb2ludHMKICBnZW9tX3BvaW50KGFlcyhjb2wgPSB0cmVlcyR2b2xfZmFjLCBzaXplID0gdHJlZXMkVm9sdW1lKSwgYWxwaGEgPSAwLjYpICsKICBsYWJzKHNpemUgPSAiVm9sdW1lIiwgY29sID0gIiIpICsKICB0aGVtZShhc3BlY3QucmF0aW8gPSAwLjUpICsKICBnZ3RpdGxlKCJCaXBsb3QgZm9yIHRoZSBQQ0Egb24gc3RhbmRhcmRpemVkIGRhdGEiKQpgYGAKCldlIG5vdyBnZXQgYSBtdWNoIG1vcmUgcmVhbGlzdGljIHJlc3VsdCwgd2hlcmUgdGhlICpoZWlnaHQqIGFuZCAqZ2lydGgqIHZhcmlhYmxlcyBoYXZlIG1vcmUgZXF1YWwKY29udHJpYnV0aW9ucyB0byB0aGUgUENzLgoKUEMxIGNhbiBiZSBpbnRlcnByZXRlZCBhcyBzZXBhcmF0aW5nIHNtYWxsIGFuZCBsYXJnZSB2b2x1bWUgdHJlZXMuIEEgcG90ZW50aWFsIGV4cGxhbmF0aW9uIG9mIFBDMgp3b3VsZCBiZSB0aGUgc2VwYXJhdGlvbiBiZXR3ZWVuIHRyZWVzIHRoYXQgaGF2ZSBhIHNpbWlsYXIgdm9sdW1lIGJ1dCBkaWZmZXIgaW4gdGhlaXIgaGVpZ2h0LXRvLWdpcnRoCnJhdGlvcywgaS5lLiBzaG9ydCB3aWRlIHRyZWVzIGFuZCBsb25nIHRoaW4gdHJlZXMuCgpOb3RlIHRoYXQgd2Ugd291bGQgZ2V0IHRoZSBleGFjdCBzYW1lIHJlc3VsdCAoYXBhcnQgbWF5YmUgZnJvbSB0aGUgc2lnbnMpIGlmIHdlIHVzZWQgKmhlaWdodCogb24gdGhlCm9yaWdpbmFsICpmZWV0KiBzY2FsZS4gU2luY2UgdGhlIGNvbnZlcnNpb24gaXMganVzdCBhIG11bHRpcGxpY2F0aW9uIGJ5IGEgY29uc3RhbnQsIHNjYWxpbmcgdGhlCmNvbHVtbiBieSBpdHMgc3RhbmRhcmQgZGV2aWF0aW9uIHdpbGwgZ2l2ZSB0aGUgc2FtZSByZXN1bHQuIChZb3UgY2FuIHZlcmlmeSB0aGlzIGZvciB5b3Vyc2VsZiBieQpyZWRvaW5nIHRoZSBQQ0EgdXNpbmcgdGhlIG9yaWdpbmFsIGhlaWdodCBjb2x1bW4gZnJvbSBgdHJlZXNgLCB3aXRob3V0IGNvbnZlcnRpbmcgaXQgdG8gbWlsZXMuKQoKIyBFeGVyY2lzZXMKCiMjIEhlYXZ5IG1ldGFscyBuZWFyIHRoZSBTY2hlbGRlCgojIyMgRGF0YSBwcmVwIHsudW5udW1iZXJlZH0KCkF0IHRoZSBkZXBhcnRtZW50IG9mIGFuYWx5dGljYWwgYW5kIHBoeXNpY2FsIGNoZW1pc3RyeSwgcmVzZWFyY2hlcnMgd2FudGVkIHRvIGludmVzdGlnYXRlIHRoZQpwb2xsdXRpb24gb2YgZ3Jhc3NsYW5kcyBpbiB0aGUgdmljaW5pdHkgb2YgdGhlIHJpdmVyIFNjaGVsZGUuIENvbmNlbnRyYXRpb25zIG9mIDggaGVhdnkgbWV0YWxzIHdlcmUKbWVhc3VyZWQgb24gMTkgZGlmZmVyZW50IGxvY2F0aW9ucywgZWFjaCB0aW1lIGF0IGEgZGVwdGggb2YgNSBjbSBhbmQgYXQgYSBkZXB0aCBvZiAyMCBjbTsgdGhlIGRhdGEKc2V0IGlzIGNhbGxlZCBoZWF2eW1ldGFscy4gVmljaW5pdHkgdG8gdGhlIHJpdmVyIHdhcyAwIChmYXIpIG9yIDEgKGNsb3NlKS4KCkxvYWQgaW4gdGhlIGRhdGEgYXMgZm9sbG93czoKCmBgYHtyIGxvYWQtaGVhdnltZXRhbHMtZGF0YSwgY29sbGFwc2U9RkFMU0V9CmRhdGEoImhlYXZ5bWV0YWxzIikKCiMjIFJlY29kZSB0aGUgInJpdmVyIiB2YXJpYWJsZQpoZWF2eW1ldGFscyRyaXZlciA8LSBpZmVsc2UoaGVhdnltZXRhbHMkcml2ZXIsICJjbG9zZSIsICJmYXIiKQoKaGVhdnltZXRhbHMKZGltKGhlYXZ5bWV0YWxzKQpgYGAKCk5vdGUgdGhhdCB0aGVyZSBhcmUgMiBjb2x1bW5zIHBlciBoZWF2eSBtZXRhbCwgb25lIGZvciB0aGUgbWVhc3VyZW1lbnQgYXQgNSBjbSBkZXB0aCBhbmQgb25lIGF0IDIwCmNtIGRlcHRoLgoKIyMjIFRhc2tzIHsudW5udW1iZXJlZH0KCiMjIyMjIDEuIENvbmR1Y3QgYSBQQ0EgdXNpbmcgKnN0YW5kYXJkaXplZCB2YXJpYWJsZXMqLiBIb3cgbWFueSBQQ3Mgd291bGQgeW91IHJldGFpbj8gTW90aXZhdGUgdGhlIGFuc3dlci9pbnRlcnByZXQuIHsudW5udW1iZXJlZH0KClRoaW5rIGFib3V0IHdoaWNoIGNvbHVtbnMgeW91IG5lZWQgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YSEKCjxkZXRhaWxzPgoKPHN1bW1hcnk+IFNvbHV0aW9uIDwvc3VtbWFyeT4KCkZpcnN0IGRvIHRoZSBQQ0EsIGV4Y2x1ZGluZyB0aGUgYGxvY2F0aW9uYCBhbmQgYHJpdmVyYCBjb2x1bW5zLgoKYGBge3IgaGVhdnltZXRhbHMtcGNhfQojIyBSZW1vdmUgJ2xvY2F0aW9uJyBhbmQgJ3JpdmVyJyBjb2x1bW5zIHdoZW4gY3JlYXRpbmcgbWF0cml4CmhlYXZ5bWV0YWxzX214IDwtIGRwbHlyOjpzZWxlY3QoaGVhdnltZXRhbHMsIC1sb2NhdGlvbiwgLXJpdmVyKSAlPiUKICBhcy5tYXRyaXgoKQoKIyMgUnVuIFBDQSBvbiBjZW50ZXJlZCBhbmQgc2NhbGVkIGRhdGEKaGVhdnltZXRhbHNfcGNhIDwtIHByY29tcChoZWF2eW1ldGFsc19teCwgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSkKc3VtbWFyeShoZWF2eW1ldGFsc19wY2EpCmBgYAoKVG8gY2hvb3NlIHRoZSBudW1iZXIgb2YgUENzIHRvIHJldGFpbiwgd2UgbG9vayBhdCB0aGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSB0aGF0IGVhY2ggUEMgZXhwbGFpbnMuClRoaXMgY2FuIGJlIHZpc3VhbGl6ZWQgdXNpbmcgd2hhdCBpcyBrbm93biBhcyBhICpzY3JlZSBwbG90Ki4KCmBgYHtyIGhlYXZ5bWV0YWxzLXBjYS1zY3JlZXBsb3R9CiMjIENhbGN1bGF0ZSB0b3RhbCB2YXJpYW5jZSBieSBzdW1taW5nIHRoZSBQQyB2YXJpYW5jZXMgKHNkZXYncyBzcXVhcmVkKQp0b3RfdmFyIDwtIHN1bShoZWF2eW1ldGFsc19wY2Ekc2Rldl4yKQoKIyMgQ3JlYXRlIGRhdGEuZnJhbWUgb2YgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggUEMKaGVhdnltZXRhbHNfcHJvcF92YXIgPC0gZGF0YS5mcmFtZSgKICBQQyA9IDE6bmNvbChoZWF2eW1ldGFsc19wY2EkeCksCiAgdmFyID0gaGVhdnltZXRhbHNfcGNhJHNkZXZeMgopICU+JQogICMjIFVzaW5nIGBtdXRhdGVgIHRvIGNhbGN1bGF0ZSBwcm9wLiB2YXIgYW5kIGN1bS4gcHJvcC4gdmFyCiAgbXV0YXRlKAogICAgcHJvcF92YXIgPSB2YXIgLyB0b3RfdmFyLAogICAgY3VtX3Byb3BfdmFyID0gY3Vtc3VtKHZhciAvIHRvdF92YXIpCiAgKQoKaGVhZChoZWF2eW1ldGFsc19wcm9wX3ZhcikKCiMjIFBsb3QgdGhlIHByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggUEMKZ2dwbG90KGhlYXZ5bWV0YWxzX3Byb3BfdmFyLCBhZXMoUEMsIHByb3BfdmFyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDIuNSwgY29sID0gImZpcmVicmljayIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTpuY29sKGhlYXZ5bWV0YWxzX3BjYSR4KSkgKwogIGxhYnMoeSA9ICJQcm9wb3J0aW9uIG9mIHZhcmlhbmNlIikgKwogIGdndGl0bGUoIlByb3BvcnRpb24gb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IGVhY2ggUEMiLAogICAgICAgICAgc3VidGl0bGUgPSAiSGVhdnkgbWV0YWxzIGRhdGEiKQoKIyMgUGxvdCB0aGUgY3VtdWxhdGl2ZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIFBDCmdncGxvdChoZWF2eW1ldGFsc19wcm9wX3ZhciwgYWVzKFBDLCBjdW1fcHJvcF92YXIpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMi41LCBjb2wgPSAiZmlyZWJyaWNrIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxOm5jb2woaGVhdnltZXRhbHNfcGNhJHgpKSArCiAgbGFicyh5ID0gIlByb3BvcnRpb24gb2YgdmFyaWFuY2UiKSArCiAgZ2d0aXRsZSgiQ3VtdWxhdGl2ZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIFBDIiwKICAgICAgICAgIHN1YnRpdGxlID0gIkhlYXZ5IG1ldGFscyBkYXRhIikKYGBgCgpXZSBkZWNpZGUgdG8ga2VlcCB0aGUgZmlyc3QgMiBQQ3MgKGluZGljYXRlZCBieSB0aGUgcmVkIHZlcnRpY2FsIGxpbmUpLCBhcyB0aGlzIGNvaW5jaWRlcyB3aXRoIHRoZQoiZWxib3ciIGluIHRoZSBzY3JlZSBwbG90LiBUaGlzIGxlYXZlcyB1cyB3aXRoIDg4JSBvZiB0aGUgdG90YWwgdmFyaWFuY2UgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YSwKd2hpY2ggaXMgbm90IGJhZCBhdCBhbGwgZ2l2ZW4gdGhhdCB3ZSB3ZW50IGZyb20gMTYgdG8gb25seSAyIGRpbWVuc2lvbnMhIE90aGVyIGNvbW1vbiBjdXRvZmZzIGFyZSB0bwprZWVwIGUuZy4gOTAlLCA5NSUgb3IgOTklIG9mIHRoZSBvcmlnaW5hbCB2YXJpYW5jZS4gV2hpY2ggd291bGQgZ2l2ZSB1cyAzLCA0IG9yIDcgUENzIHJlc3BlY3RpdmVseS4KSG93ZXZlciwgZm9yIHRoZSBwdXJwb3NlIG9mIHRoaXMgZXhlcmNpc2UsIHRoZSBmaXJzdCAyIHdpbGwgYmUgZW5vdWdoLgoKPC9kZXRhaWxzPgoKIyMjIyMgMi4gTWFrZSBhIGJpcGxvdCB1c2luZyB0aGUgcmV0YWluZWQgUENzIGFuZCBpbnRlcnByZXQuIElzIHRoZXJlIGEgcmVsYXRpb25oc2lwIGJldHdlZW4gdmljaW5pdHkgdG8gdGhlIHJpdmVyIGFuZCBwb2x1dGlvbiB3aXRoIGNlcnRhaW4gbWV0YWxzPyB7LnVubnVtYmVyZWR9CgoqKkhpbnQqKjogVHJ5IHRvIGNvbG9yIG9yIGxhYmVsIHRoZSBkYXRhIHBvaW50cyBieSB0aGVpciB2aWNpbml0eSB0byB0aGUgcml2ZXIgKHVzaW5nIHRoZSBgcml2ZXJgCnZhcmlhYmxlKSB0byBhaWQgd2l0aCBpbnRlcnByZXRhdGlvbi4KCjxkZXRhaWxzPgoKPHN1bW1hcnk+IFNvbHV0aW9uIDwvc3VtbWFyeT4KCk1ha2luZyB0aGUgYmlwbG90IGZvciB0aGUgZmlyc3QgMiBQQ3MuCgpgYGB7ciBoZWF2eW1ldGFscy1wY2EtYmlwbG90fQpnZ2JpcGxvdChoZWF2eW1ldGFsc19wY2EsIGdyb3VwcyA9IGhlYXZ5bWV0YWxzJHJpdmVyKSArCiAgbGFicyhjb2xvciA9ICJWaWNpbml0eSB0byByaXZlciIpICsKICBnZ3RpdGxlKCJCaXBsb3QgZm9yIHRoZSBoZWF2eSBtZXRhbHMgUENBIikKYGBgCgpUaGUgZGlzdGluY3Rpb24gdGhhdCBpcyBpbW1lZGlhdGVseSBjbGVhciBpcyB0aGF0IHRoZSBsZXZlbHMgb2YgTWFuZ2FuZXNlIChNbikgc2VlbSB0byBiZSBoaWdoZXIgaW4KdGhlIGFyZWFzIGZhciBmcm9tIHRoZSByaXZlciAoYXQgYm90aCBkZXB0aHMpLCBjb21wYXJlZCB0byBhbGwgb3RoZXIgbWV0YWxzLgoKV2UgY2FuIGFsc28gc2VlIHRoaXMgZnJvbSB0aGUgbG9hZGluZ3MsIHdoZXJlIE1uMjAgaXMgdGhlIG9ubHkgbWVhc3VyZW1lbnQgdGhhdCBpcyBuZWdhdGl2ZWx5CmNvcnJlbGF0ZWQgd2l0aCBQQzEsIHdoaWxlIE1uNSBpcyBiYXJlbHkgY29ycmVsYXRlZCB3aXRoIFBDMS4KCkEgcG90ZW50aWFsIGh5cG90aGVzaXMgd291bGQgYmUgdGhhdCBhcyB3ZSBtb3ZlIGZhcnRoZXIgYXdheSBmcm9tIHRoZSByaXZlciwgdGhlIGNvbmNlbnRyYXRpb24gb2YgTW4KaW5jcmVhc2VzIHdpdGggZGVwdGguCgpgYGB7cn0KaGVhdnltZXRhbHNfcGNhJHJvdGF0aW9uWywgMToyXQpgYGAKCjwvZGV0YWlscz4KCiMjIyMjIDMuIENhbGN1bGF0ZSB0aGUgbG9hZGluZ3MgYW5kIHNjb3JlcyBvZiB0aGUgUENBIHVzaW5nIHRoZSBTVkQgKGZ1bmN0aW9uIGBzdmRgKS4gVmVyaWZ5IHRoYXQgdGhlIGxvYWRpbmdzIGFuZCBzY29yZXMgb2J0YWluZWQgdXNpbmcgdGhlIFNWRCBhcHByb2FjaCBhcmUgZXF1YWwgdG8gdGhvc2Ugb2J0YWluZWQgdXNpbmcgdGhlIGBwcmNvbXBgIGZ1bmN0aW9uLiB7LnVubnVtYmVyZWR9Cgo8ZGV0YWlscz4KCjxzdW1tYXJ5PiBTb2x1dGlvbiA8L3N1bW1hcnk+CgpGaXJzdCBwZXJmb3JtIHRoZSBTVkQuIFJlbWVtYmVyIHRvICoqY2VudGVyIGFuZCBzY2FsZSoqIHRoZSBkYXRhIG1hdHJpeCEKCmBgYHtyIGhlYXZ5bWV0YWxzLVNWRH0KaGVhdnltZXRhbHNfc2NhbGVkIDwtIHNjYWxlKGhlYXZ5bWV0YWxzX214KQpoZWF2eW1ldGFsc19zdmQgPC0gc3ZkKGhlYXZ5bWV0YWxzX3NjYWxlZCkKYGBgCgpOb3cgY29tcGFyZSB0aGUgUEMgbG9hZGluZ3Mgd2l0aCB0aGUgcmlnaHQgc2luZ3VsYXIgdmVjdG9ycyAkXG1hdGhiZntWfSQgYW5kIHRoZSBzY29yZXMgd2l0aCB0aGUKcHJvamVjdGlvbnMgJFxtYXRoYmZ7Wl9rfSA9IFxtYXRoYmZ7WFZfa30kLgoKYGBge3J9CiMjIFJlbW92ZSBkaW1uYW1lcyBmb3IgY29tcGFyaXNvbgphbGwuZXF1YWwodW5uYW1lKGhlYXZ5bWV0YWxzX3BjYSRyb3RhdGlvbiksIGhlYXZ5bWV0YWxzX3N2ZCR2KQoKIyMgQ2FsY3VsYXRlIHByb2plY3Rpb25zCmhlYXZ5bWV0YWxzX1prIDwtIGhlYXZ5bWV0YWxzX3NjYWxlZCAlKiUgaGVhdnltZXRhbHNfc3ZkJHYKaGVhdnltZXRhbHNfc2NvcmVzIDwtIHVubmFtZShoZWF2eW1ldGFsc19wY2EkeCkKCmFsbC5lcXVhbChoZWF2eW1ldGFsc19aaywgaGVhdnltZXRhbHNfc2NvcmVzKQpgYGAKClRoaXMgYWdhaW4gc2hvd3MgdGhhdCBQQ0EgaXMgbm90aGluZyBtb3JlIHRoYW4gYW4gU1ZEIG9uIHRoZSAoY2VudGVyZWQgYW5kIHNjYWxlZCkgZGF0YSBtYXRyaXghCgo8L2RldGFpbHM+CgojIyBFbXBsb3ltZW50IGJ5IGluZHVzdHJ5IGluIEV1cm9wZWFuIGNvdW50cmllcwoKVXNpbmcgdGhlIHNhbWUgZGF0YSBhcyBpbiBbTGFiCjFdKC4vTGFiMS1JbnRyby1TVkQuaHRtbCMzMl9FeGVyY2lzZTpfZW1wbG95bWVudF9ieV9pbmR1c3RyeV9pbl9FdXJvcGVhbl9jb3VudHJpZXMpLgoKIyMjIERhdGEgcHJlcCB7LnVubnVtYmVyZWR9CgpUaGUgYCJpbmR1c3RyaWVzImAgZGF0YXNldCBjb250YWlucyBkYXRhIG9uIHRoZSBkaXN0cmlidXRpb24gb2YgZW1wbG95bWVudCBiZXR3ZWVuIDkgaW5kdXN0cmlhbApzZWN0b3JzLCBpbiAyNiBFdXJvcGVhbiBjb3VudHJpZXMuIFRoZSBkYXRhc2V0IHN0ZW1zIGZyb20gdGhlIENvbGQtV2FyIGVyYTsgdGhlIGRhdGEgYXJlIGV4cHJlc3NlZAphcyBwZXJjZW50YWdlcy4gTG9hZCB0aGUgZGF0YSBhbmQgZXhwbG9yZSBpdHMgY29udGVudHMuCgpgYGB7ciByZWFkLWluZHVzdHJpZXMtZGF0YX0KIyMgTG9hZCAnaW5kdXN0cmllcycgZGF0YSBmcm9tIHRoZSBIRERBRGF0YSBwYWNrYWdlCmRhdGEoImluZHVzdHJpZXMiKQoKIyBFeHBsb3JlIGNvbnRlbnRzCmluZHVzdHJpZXMKc3VtbWFyeShpbmR1c3RyaWVzKQpgYGAKCiMjIyBUYXNrcyB7LnVubnVtYmVyZWR9CgojIyMjIyAxLiBQZXJmb3JtIGEgUENBLiBIb3cgbWFueSBQQ3Mgd291bGQgeW91IHJldGFpbj8gRXhwbGFpbi4gey51bm51bWJlcmVkfQoKPGRldGFpbHM+Cgo8c3VtbWFyeT4gU29sdXRpb24gPC9zdW1tYXJ5PgoKUGVyZm9ybSB0aGUgUENBIG9uIHRoZSBjZW50ZXJlZCBhbmQgc2NhbGVkIGRhdGEgbWF0cml4LCBhZnRlciByZW1vdmluZyB0aGUgYGNvdW50cnlgIGNvbHVtbi4KCmBgYHtyIGluZHVzdHJpZXMtcGNhfQppbmR1c3RyaWVzX3BjYSA8LSBwcmNvbXAoaW5kdXN0cmllc1ssIC0xXSwgc2NhbGUuID0gVFJVRSkKc3VtbWFyeShpbmR1c3RyaWVzX3BjYSkKYGBgCgpgYGB7ciBpbmR1c3RyaWVzLXBjYS1zY3JlZXBsb3R9CiMjIENhbGN1bGF0ZSB0b3RhbCB2YXJpYW5jZSBieSBzdW1taW5nIHRoZSBQQyB2YXJpYW5jZXMgKHNkZXYncyBzcXVhcmVkKQp0b3RfdmFyIDwtIHN1bShpbmR1c3RyaWVzX3BjYSRzZGV2XjIpCgojIyBDcmVhdGUgZGF0YS5mcmFtZSBvZiB0aGUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBQQwppbmR1c3RyaWVzX3Byb3BfdmFyIDwtIGRhdGEuZnJhbWUoCiAgUEMgPSAxOm5jb2woaW5kdXN0cmllc19wY2EkeCksCiAgdmFyID0gaW5kdXN0cmllc19wY2Ekc2Rldl4yCikgJT4lCiAgIyMgVXNpbmcgYG11dGF0ZWAgdG8gY2FsY3VsYXRlIHByb3AuIHZhciBhbmQgY3VtLiBwcm9wLiB2YXIKICBtdXRhdGUoCiAgICBwcm9wX3ZhciA9IHZhciAvIHRvdF92YXIsCiAgICBjdW1fcHJvcF92YXIgPSBjdW1zdW0odmFyIC8gdG90X3ZhcikKICApCgppbmR1c3RyaWVzX3Byb3BfdmFyCgojIyBQbG90IHRoZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIFBDCmdncGxvdChpbmR1c3RyaWVzX3Byb3BfdmFyLCBhZXMoUEMsIHByb3BfdmFyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDUuNSwgY29sID0gImZpcmVicmljayIpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gMTpuY29sKGluZHVzdHJpZXNfcGNhJHgpKSArCiAgbGFicyh5ID0gIlByb3BvcnRpb24gb2YgdmFyaWFuY2UiKSArCiAgZ2d0aXRsZSgiUHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBQQyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJJbmR1c3RyaWVzIGRhdGEiKQoKIyMgUGxvdCB0aGUgY3VtdWxhdGl2ZSBwcm9wb3J0aW9uIG9mIHZhcmlhbmNlIGV4cGxhaW5lZCBieSBlYWNoIFBDCmdncGxvdChpbmR1c3RyaWVzX3Byb3BfdmFyLCBhZXMoUEMsIGN1bV9wcm9wX3ZhcikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSA1LjUsIGNvbCA9ICJmaXJlYnJpY2siKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6bmNvbChpbmR1c3RyaWVzX3BjYSR4KSkgKwogIGxhYnMoeSA9ICJQcm9wb3J0aW9uIG9mIHZhcmlhbmNlIikgKwogIGdndGl0bGUoIkN1bXVsYXRpdmUgcHJvcG9ydGlvbiBvZiB2YXJpYW5jZSBleHBsYWluZWQgYnkgZWFjaCBQQyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJJbmR1c3RyaWVzIGRhdGEiKQpgYGAKCkluIHRoaXMgY2FzZSwgcmV0YWluaW5nIHRoZSBmaXJzdCA1LTcgUENzIHdvdWxkIGJlIG1vcmUgYXBwcm9wcmlhdGUuCgo8L2RldGFpbHM+CgojIyMjIyAyLiBXaGF0IGNvdWxkIHlvdSBzYXkgYWJvdXQgZS5nLiBEZW5tYXJrIGJhc2VkIG9uIHRoZSBiaXBsb3Q/IHsudW5udW1iZXJlZH0KCjxkZXRhaWxzPgoKPHN1bW1hcnk+IFNvbHV0aW9uIDwvc3VtbWFyeT4KCkNvbnN0cnVjdCB0aGUgYmlwbG90LiBXZSBkb24ndCByZWFsbHkgaGF2ZSBhIGdyb3VwaW5nIHZhcmlhYmxlIHRvIGNvbG9yIHRoZSBwb2ludHMsIGJ1dCB3ZSBjYW4gYWRkCmxhYmVscyB3aXRoIHRoZSBjb3VudHJ5IG5hbWVzLgoKYGBge3IgaW5kdXN0cmllcy1wY2EtYmlwbG90fQppbmR1c3RyaWVzX2JpcGxvdCA8LSBnZ2JpcGxvdChpbmR1c3RyaWVzX3BjYSwKICBsYWJlbHMgPSBpbmR1c3RyaWVzJGNvdW50cnksIGxhYmVscy5zaXplID0gMgopICsKICBnZ3RpdGxlKCJCaXBsb3QgZm9yIHRoZSBpbmR1c3RyaWVzIFBDQSIpICsKICB4bGltKGMoLTMuNCwgMy40KSkgKwogIHlsaW0oYygtMi4yLCAyLjIpKQppbmR1c3RyaWVzX2JpcGxvdApgYGAKClRoZSBiaXBsb3Qgc2hvd3MgaG93IHRoZSB3b3JrIGZvcmNlcyBvZiB0aGUgY291bnRyaWVzIGFyZSBkaXN0cmlidXRlZCBhbW9uZyB0aGUgZGlmZmVyZW50CmluZHVzdHJpZXMuCgpUaGUgZW1wbG95bWVudCBpbiBEZW5tYXJrIHNlZW1zIHRvIGJlIG1haW5seSBjb25jZW50cmF0ZWQgaW4gdGhlIGZpbmFuY2UsIHNlcnZpY2VzIGFuZCBzb2NpYWwKc2VjdG9ycy4KCjwvZGV0YWlscz4KCiMjIyMjIDMuIFRyeSB0byBpbnRlcnByZXQgdGhlIGZpcnN0IDIgUENzLiB7LnVubnVtYmVyZWR9Cgo8ZGV0YWlscz4KCjxzdW1tYXJ5PiBTb2x1dGlvbiA8L3N1bW1hcnk+CgpXZSBjYW4gdXNlIHRoZSBiaXBsb3QgYW5kIGxvYWRpbmdzIHRvIGludGVycHJldCB0aGUgUENzLiBUaGUgYmlwbG90IGlzIGdpdmVuIGFib3ZlLCB3aGlsZSB0aGUKbG9hZGluZ3MgY2FuIGJlIGFjY2Vzc2VkIGZyb20gdGhlIGAkcm90YXRpb25gIHNsb3Q6CgpgYGB7cn0KaW5kdXN0cmllc19wY2Ekcm90YXRpb25bLCAxOjJdCmBgYAoKVGhlIGZpcnN0IFBDIHNlZW1zIHRvIGJlIGxhcmdlbHkgZHJpdmVuIGJ5IHRoZSAqYWdyaWN1bHR1cmUqIGluZHVzdHJ5LCBpLmUuIGl0IGlzIHNlcGFyYXRpbmcKY291bnRyaWVzIG1vc3RseSBiYXNlZCBvbiB0aGVpciBlbXBsb3ltZW50IGluIGFncmljdWx0dXJlICgpIFRodXMsIGNvdW50cmllcyBzaXR1YXRlZCBvbiB0aGUKcG9zaXRpdmUgc2lkZSBvZiBQQzEgd2lsbCBsaWtlbHkgaGF2ZSBhICpoaWdoZXItdGhhbi1hdmVyYWdlKiBlbXBsb3ltZW50IGluIGFncmljdWx0dXJlLiBPbiB0aGUKb3RoZXIgaGFuZCwgY291bnRyaWVzIG9uIHRoZSBuZWdhdGl2ZSBzaWRlIG9mIFBDMSBhcmUgbGVzcyBhZ3JpY3VsdHVyZS1iYXNlZCBlY29ub21pZXMgYW5kIGhhdmUKaGlnaGVyIGVtcGxveW1lbnRzIGluIGUuZy4gdGhlIHNvY2lhbCBzZWN0b3IuCgpJbmRlZWQsIGlmIHdlIHJhbmsgdGhlIGNvdW50cmllcyBieSB0aGVpciBhZ3JpY3VsdHVyZSBlbXBsb3ltZW50LCB3ZSBsYXJnZWx5IHJlY292ZXIgdGhlIG9yZGVyIG9mCnRoZSBjb3VudHJpZXMgYWxvbmcgUEMxLCB3aXRoIFR1cmtleSBjbGVhcmx5IGJlaW5nIHRoZSBtb3N0IGFncmljdWx0dXJlLWZvY3VzZWQgKHJlbWVtYmVyIHRoYXQgdGhpcwpkYXRhIGlzIGZyb20gdGhlIENvbGQgV2FyIGVyYSkhCgpgYGB7cn0KIyMgU2hvdyByYW5raW5nIG9mIGFncmljdWx0dXJlIGluZHVzdHJ5CmluZHVzdHJpZXMgJT4lCiAgZHBseXI6OnNlbGVjdChjb3VudHJ5LCBhZ3JpY3VsdHVyZSwgc29jaWFsLnNlY3RvciwgbWluaW5nLCBmaW5hbmNlKSAlPiUKICBhcnJhbmdlKGRlc2MoYWdyaWN1bHR1cmUpKQpgYGAKCldlIGNvdWxkIHNheSB0aGF0IHRoZSBmaXJzdCBQQyBzZXBhcmF0ZXMgYWdyaWN1bHR1cmUtYmFzZWQgZWNvbm9taWVzIGZyb20gbm9uLWFncmljdWx0dXJlLWJhc2VkLiBUaGUKZmFjdCB0aGF0IG1vc3Qgb3RoZXIgaW5kdXN0cmllcyBhcmUgbmVnYXRpdmVseSBjb3JyZWxhdGVkIHdpdGggdGhlIGZpcnN0IFBDLCBzZWVtcyB0byBpbmRpY2F0ZSB0aGF0CmNvdW50cmllcyBlaXRoZXIgaGF2ZSBhIGxhcmdlIGFncmljdWx0dXJlIGluZHVzdHJ5IG9yIGRpc3RyaWJ1dGUgdGhlaXIgd29yayBmb3JjZSBtb3JlIGVxdWFsbHkgYW1vbmcKdGhlIG90aGVyIGluZHVzdHJpZXMuCgpUaGUgMiBtYWluIGV4Y2VwdGlvbnMgYXJlIHRoZSBtaW5pbmcgYW5kIGZpbmFuY2UgaW5kdXN0cmllcywgd2hpY2ggYXJlIChhbG1vc3QpIHBlcnBlbmRpY3VsYXIgdG8KUEMxLgoKVGhlIHNlY29uZCBQQyBpcyBtb3N0bHkgZHJpdmVuIGJ5IHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gbW9yZSBzZXJ2aWNlcy1iYXNlZCAob24gdGhlIG5lZ2F0aXZlIHNpZGUpCmFuZCBtb3JlIGluZHVzdHJ5LWJhc2VkIChwb3NpdGl2ZSBQQykgZWNvbm9taWVzLiBXaXRoIHRoZSBtYWluIGRyaXZlcnMgYmVpbmcgdGhlICptaW5pbmcqIGFuZAoqZmluYW5jZSogc2VjdG9ycy4KCktlZXAgaW4gbWluZCBob3dldmVyIHRoYXQgdGhlc2UgMiBQQ3MgIm9ubHkiIGV4cGxhaW4gMzguNyUgYW5kIDIzLjclIG9mIHRoZSB0b3RhbCB2YXJpYW5jZSwKcmVzcGVjdGl2ZWx5LiBTbyB0aGVyZSBhcmUgbGlrZWx5IHN0aWxsIG1hbnkgcGF0dGVybnMgd2UgYXJlIG1pc3NpbmcuCgo8L2RldGFpbHM+CgoKIyMjIyAqKkV4dHJhKio6IHRoZSBhdmVyYWdlIGNvdW50cnkgey51bm51bWJlcmVkfQoKV2hlcmUgd291bGQgYSBjb3VudHJ5IHdpdGggYXZlcmFnZSBlbXBsb3ltZW50IGFjcm9zcyBhbGwgaW5kdXN0cmllcyBsaWUgb24gdGhlIGJpcGxvdD8KCjxkZXRhaWxzPgoKPHN1bW1hcnk+IFNvbHV0aW9uIDwvc3VtbWFyeT4KCkZpcnN0IGNhbGN1bGF0ZSB0aGUgImF2ZXJhZ2UiIGNvdW50cnkgYnkgY29tcHV0aW5nIHRoZSBtZWFucyBvZiBlYWNoIHZhcmlhYmxlCmluIHRoZSBgaW5kdXN0cmllc2AgZGF0YS4gQnV0IHJlbWVtYmVyLCB3ZSAqKmNlbnRlcmVkIGFuZCBzY2FsZWQqKiB0aGUgZGF0YQpiZWZvcmUgY2FsY3VsYXRpbmcgdGhlIFBDQSAodGhyb3VnaCB0aGUgYGNlbnRlciA9IFRSVUVgIGFuZCBgc2NhbGUuID0gVFJVRWAKYXJndW1lbnRzIGluIGBwcmNvbXBgKS4gU28gd2UgaGF2ZSB0byBkbyB0aGUgc2FtZSBwcm9jZWR1cmUgd2l0aCBvdXIgbmV3IGNvdW50cnkuCk9mIGNvdXJzZSwgc3VidHJhY3RpbmcgdGhlIGF2ZXJhZ2VzIGZyb20gdGhlIGF2ZXJhZ2UgY291bnRyeSByZXN1bHRzIGluIGFsbCAwJ3MuClNvIHdlIGNhbiByZXByZXNlbnQgb3VyIGF2ZXJhZ2UgY291bnRyeSBieSBhIHZlY3RvciBvZiAwJ3MgZm9yIGVhY2ggZmVhdHVyZS4KCmBgYHtyfQphdmdfY291bnRyeSA8LSByZXAoMCwgbmNvbChpbmR1c3RyaWVzKSAtIDEpICMgLTEgZm9yIHRoZSAnY291bnRyeScgY29sdW1uCmBgYAoKTmV4dCwgd2UgcHJvamVjdCBvdXIgbmV3IGNvdW50cnkgb24gdG8gdGhlIFBDQSBzcGFjZSwgdXNpbmcKCiQkIFogPSBYVl97a30kJAoKd2hlcmUgJFYkIGFyZSB0aGUgUEMgKmxvYWRpbmdzKiAob3IgcmlnaHQgc2luZ3VsYXIgdmVjdG9ycyBpbiBTVkQgdGVybXMpIGFuZCAkayQKYXJlIHRoZSBjaG9zZW4gbnVtYmVyIG9mIGRpbWVuc2lvbnMgKDIgaW4gdGhpcyBjYXNlKS4KCk9mIGNvdXJzZSwgc2luY2UgJFgkIGhlcmUgY29uc2lzdHMgZW50aXJlbHkgb2YgemVyb3MsIHRoZSByZXN1bHRpbmcgcHJvamVjdGlvbgp3aWxsIGFsc28gYmUgMCBldmVyeXdoZXJlLCBhbmQgeW91IGNhbiBzZWUgd2h5IHRoZSBhdmVyYWdlIGNvdW50cnkgd2lsbCBsaWUgaW4KdGhlIGNlbnRlciBvZiB0aGUgYmlwbG90IQoKYGBge3J9CihhdmdfY291bnRyeV9wYyA8LSBhdmdfY291bnRyeSAlKiUgaW5kdXN0cmllc19wY2Ekcm90YXRpb24pCiMjIFJlZm9ybWF0IGEgYml0IHRvIG1ha2UgaXQgY29uc2lzdGVudCB3aXRoIHRoZSBkYXRhIGluIHRoZSBiaXBsb3QKYXZnX2NvdW50cnlfYmlwbG90IDwtIGRhdGEuZnJhbWUoCiAgeHZhciA9IGF2Z19jb3VudHJ5X3BjWywgIlBDMSJdLAogIHl2YXIgPSBhdmdfY291bnRyeV9wY1ssICJQQzIiXSwKICBsYWJlbHMgPSAiQVZFUkFHRSIsIHJvdy5uYW1lcyA9IE5VTEwKKQoKIyMgQWRkIHRoZSBhdmVyYWdlIGNvdW50cnkgdG8gdGhlIGJpcGxvdAppbmR1c3RyaWVzX2JpcGxvdCArCiAgZ2VvbV9wb2ludCgKICAgIGRhdGEgPSBhdmdfY291bnRyeV9iaXBsb3QsIHNpemUgPSAzLAogICAgc2hhcGUgPSAyMywgZmlsbCA9ICJkb2RnZXJibHVlIgogICkgKwogIGdlb21fbGFiZWxfcmVwZWwoCiAgICBkYXRhID0gYXZnX2NvdW50cnlfYmlwbG90LCBhZXMobGFiZWwgPSBsYWJlbHMpLAogICAgbnVkZ2VfeCA9IDEsIG51ZGdlX3kgPSAwLjUsIHNpemUgPSA0LAogICAgY29sb3IgPSAiZG9kZ2VyYmx1ZSIKICApICsgbGFicyhzdWJ0aXRsZSA9ICJBdmVyYWdlIGNvdW50cnkgaGlnaGxpZ2h0ZWQiKQpgYGAKCjwvZGV0YWlscz4KCgojIEZ1cnRoZXIgcmVhZGluZwoKSGVyZSBhcmUgc29tZSBmdXJ0aGVyIHJlc291cmNlcyB0aGF0IGNhbiBoZWxwIHdpdGggdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBQQ0EgYW5kIGl0cyBsaW5rIHdpdGgKdGhlIFNWRDoKCi0gICA8aHR0cHM6Ly9zZXRvc2EuaW8vZXYvcHJpbmNpcGFsLWNvbXBvbmVudC1hbmFseXNpcy8+Ci0gICA8aHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9hLzEzNDI4My8yNjQ3Njg+Ci0gICA8aHR0cHM6Ly90d2l0dGVyLmNvbS9hbGxpc29uX2hvcnN0L3N0YXR1cy8xMjg4OTA0NDU5NDkwMjEzODg4P3M9MjA+Cgo8YmxvY2txdW90ZSBjbGFzcz0idHdpdHRlci10d2VldCI+PHAgbGFuZz0iZW4iIGRpcj0ibHRyIj4uLi50aGF0IGxvb2tzIGxpa2UgdGhpcy4gSSBnZXQgdG8gc2VlIHRoZW0gcHJldGVuZCB0byBiZSB3aGFsZSBzaGFya3MsIHdlIHRhbGsgYSBiaXQgYWJvdXQgaG93IHRvIGdldCBhcyBtYW55IGtyaWxsIGFzIHBvc3NpYmxlIGluIHRoZSBmZXdlc3QgcGFzc2VzIHlvdSYjMzk7cmUgZ29ubmEgdGlsdCB5b3VyIGZhY2UsIHRoZW4gd2UgZ2V0IGludG8gUENBLiA8YSBocmVmPSJodHRwczovL3QuY28vOFAwTlprN2VsTyI+cGljLnR3aXR0ZXIuY29tLzhQME5aazdlbE88L2E+PC9wPiZtZGFzaDsgQWxsaXNvbiBIb3JzdCAoQGFsbGlzb25faG9yc3QpIDxhIGhyZWY9Imh0dHBzOi8vdHdpdHRlci5jb20vYWxsaXNvbl9ob3JzdC9zdGF0dXMvMTI4ODkwNDQ2NDUyNzU3Mjk5Mj9yZWZfc3JjPXR3c3JjJTVFdGZ3Ij5KdWx5IDMwLCAyMDIwPC9hPjwvYmxvY2txdW90ZT4gPHNjcmlwdCBhc3luYyBzcmM9Imh0dHBzOi8vcGxhdGZvcm0udHdpdHRlci5jb20vd2lkZ2V0cy5qcyIgY2hhcnNldD0idXRmLTgiPjwvc2NyaXB0PgoKCmBgYHtyLCBjaGlsZD0iX3Nlc3Npb24taW5mby5SbWQifQpgYGAK