1 Introduction

In high dimensional data the PCs may have unclear interpretation because they depend on all \(p\) original variables.

1.1 Example: Toxicogenomics in early drug development

1.1.1 Background

  • Effect of compound on gene expression.

  • Insight in action and toxicity of drug in early phase

  • Determine activity with bio-assay: e.g. binding afinity of compound to cellwall recepter (target, IC50).

  • Early phase: 20 to 50 compounds

  • Based on in vitro results one aims to get insight in how to build better compound (higher on-target activity less toxicity.

  • Small variantions in moelculair structure lead to variations in BA and gene expression.

  • Aim: Build model to predict bio-activity based on gene expression in liver cell line.

1.1.2 Data

  • 30 chemical compounds have been screened for toxicity

  • Bioassay data on toxicity screening

  • Gene expressions in a liver cell line are profiled for each compound (4000 genes)

toxData <- read_csv(
  "https://raw.githubusercontent.com/statOmics/HDA2020/data/toxDataCentered.csv",
  col_types = cols()
)
svdX <- svd(toxData[,-1])

Data is already centered:

toxData %>%
  colMeans %>%
  range
#> [1] -2.405483e-17  5.921189e-17
 toxData %>%
  names %>%
  head
#> [1] "BA" "X1" "X2" "X3" "X4" "X5"
  • First column contains data on Bioassay.
  • The higher the score on Bioassay the more toxic the compound
  • Other columns contain data on gene expression X1, … , X4000

1.1.3 Data exploration

toxData %>%
  ggplot(aes(x="",y=BA)) +
  geom_boxplot(outlier.shape=NA) +
  geom_point(position="jitter")

svdX <- toxData[,-1] %>%
  svd

k <- 2
Vk <- svdX$v[,1:k]
Uk <- svdX$u[,1:k]
Dk <- diag(svdX$d[1:k])
Zk <- Uk%*%Dk
colnames(Zk) <- paste0("Z",1:k)
colnames(Vk) <- paste0("V",1:k)

pca <- Zk %>%
  as.data.frame %>%
  mutate(BA = toxData %>% pull(BA)) %>%
  ggplot(aes(x= Z1, y = Z2, color = BA)) +
  geom_point(size = 3) +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  geom_point(size = 3, pch = 21, color = "black")
pca

  • Scores on the first two principal components (or MDS plot). - Each point corresponds to a compound.
  • Color code refers to the toxicity score (higher score more toxic).
  • Clear separation between compounds according to toxicity.

  • Next logic step in a PCA is to interpret the principal components.
  • We thus have to assess the loadings.
  • We can add a vector for each gene to get a biplot, but this would require plotting 4000 vectors, which would render the plot unreadable.

Alternative graph to look at the many loadings of the first two PCs.

grid.arrange(
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V1)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,1]), col = "red") ,
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V2)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,2]), col = "red"),
  ncol=2)

  • It is almost impossible to interpret the PCs because there are 4000 genes contributing to each PC.

  • In an attempt to find the most important genes (in the sense that they drive the interpretation of the PCs), the plots show horizontal reference lines: the average of the loadings, and the average ± twice the standard deviation of the loadings. In between the lines we expects about 95% of the loadings (if they were normally distributed).

  • The points outside the band come from the genes that have rather large loadings (in absolute value) and hence are important for the interpretation of the PCs.

  • Note, that particularly for the first PC, only a few genes show a markedly large loadings that are negative. This means that an upregulation of these genes will lead to low scores on PC1.

  • These genes will very likely play an important role in the toxicity mechanism.

  • Indeed, low scores on PC1 are in the direction of more toxicity.


1.2 Sparse matrix decomposition

Basic idea of sparse matrix decomposition of an \(n \times p\) matrix \(\mathbf{X}\)}

Find a rank \(k\) approximation of \(\mathbf{X}\) of the form \[ \mathbf{X}_k = \sum_{j=1}^k \delta_j \mathbf{u}_j\mathbf{v}_j^t \] such that many of the elements in the \(\mathbf{v}_j\) and or \(\mathbf{u}_j\) are exactly zero.

We focus on the Penalised Matrix Decomposition (PMD) method of Witten et al. (2009, Biostatistics, 10, 3, 515-534).

When many of the loadings in \(\mathbf{v}_j\) are zero, the interpretation of the PC only depends on the features that correspond to the remaining non-zero loadings.


2 Penalised Matrix Decomposition

2.1 Rank 1 approximation

The rank-1 PMD approximation of the \(n \times p\) matrix \(\mathbf{X}\) is of the form \[ \mathbf{X}_1 = \delta_1\mathbf{u}_1\mathbf{v}_1^t, \]

and it is the solution to the following minimisation problem: \[ \min_{\delta,u,v} \Vert \mathbf{X} - \delta\mathbf{u}\mathbf{v}^t\Vert_F^2 \text{ subject to } \delta \geq 0, \Vert \mathbf{u}\Vert_2^2=\Vert \mathbf{v}\Vert_2^2=1, \Vert\mathbf{u}\Vert_1\leq c_1, \Vert \mathbf{v}\Vert_1 \leq c_2 \]

for some user-defined constants \(c_1,c_2>0\).

The constraints \(\Vert\mathbf{u}\Vert_1\leq c_1\) and \(\Vert \mathbf{v}\Vert_1 \leq c_2\) are known as \(L_1\) or lasso constraints.

Note, \[ \Vert\mathbf{u}\Vert_1\ = \sum_{i=1}^n \vert u_i \vert \text{ and }\Vert\mathbf{v}\Vert_1\ = \sum_{i=1}^p \vert v_i\vert. \]

If \(c_1=+\infty\), then the constraint on \(\mathbf{u}\) is removed (similar for \(c_2\)).


Note that \[ \min_{\delta,u,v} \Vert \mathbf{X} - \delta\mathbf{u}\mathbf{v}^t\Vert_F^2 = \min_{A:\text{rank}(A)=1} \Vert \mathbf{X} - A\Vert_F^2 \] with \(A=X_1\) (truncated SVD of \(\mathbf{X}\), truncated after 1 term). However, the PMD requires a constrained minimisation. The unconstrained solution arises with \(c_1=c_2=+\infty\).

Note that \(\Vert \mathbf{u}\Vert_2^2=\Vert \mathbf{v}\Vert_2^2=1\) may also be written as \(\mathbf{u}^t\mathbf{u}=\mathbf{v}^t\mathbf{v}=1\) (i.e. the vectors are normalised, just like for the singular vectors of the SVD).

  • We also applied the \(L_1\) constraint to the least-squares estimator of the regression parameters in a linear regression model.

  • Effect was that many of the parameter estimates are set exactly to zero.

  • Here, the same effect will take place. Many of the elements in \(\mathbf{u}\) and \(\mathbf{v}\) will be set exactly to zero.

  • In particular, the smaller \(c_2\), the more elements of \(\mathbf{v}\) will be set to zero (shrinkage).

  • Similarly, the smaller \(c_1\), the more elements of \(\mathbf{u}\) will be set to zero.

  • The choice of \(c_1\) and \(c_2\) depends on the objective of the data analysis and will be discussed later.

The solution is known as a rank-1 PMD approximation of \(\mathbf{X}\) because \(\delta_1\mathbf{u}_1\mathbf{v}_1^t\) is a matrix of rank 1.


2.2 Rank-2 approximation

The rank-2 PMD approximation is found as follows:

  1. find the rank-1 PMD as before: \(\mathbf{X}_1=\delta_1\mathbf{u}_1\mathbf{v}_1^t\);

  2. compute \[ \tilde{\mathbf{X}}_2 = \mathbf{X}-\mathbf{X}_1, \]

and compute the rank-1 PMD of \(\tilde{\mathbf{X}}_2\), which gives \(\delta_2, \mathbf{u}_2, \mathbf{v}_2\);

  1. the rank-2 PMD of \(\mathbf{X}\) is then given by \[ \mathbf{X}_2 = \delta_1\mathbf{u}_1\mathbf{v}_1^t+\delta_2\mathbf{u}_2\mathbf{v}_2^t. \]

Note that the \(\mathbf{u}\)’s and \(\mathbf{v}\)’s are not necessarily orthogonal (only if \(c_1, c_2=+\infty\)).


2.3 Rank-k approximation

The rank-\(k\) PMD approximation is found as follows:

  1. find the rank-1 PMD as before: \(\tilde{\mathbf{X}}_1=\delta_1\mathbf{u}_1\mathbf{v}_1^t\);

  2. set \(j=1\);

  3. compute \[ \tilde{\mathbf{X}}_{j+1} = \mathbf{X}-\tilde{\mathbf{X}}_1-\cdots - \tilde{\mathbf{X}}_j, \] and compute the rank-1 PMD of \(\tilde{\mathbf{X}}_{j+1}\), which gives \(\delta_{j+1}, \mathbf{u}_{j+1}, \mathbf{v}_{j+1}\);

  4. if \(j<k\), increase \(j\) with one and go back to step 3; otherwise go to step 5;

  5. the rank-\(k\) PMD of \(\mathbf{X}\) is then given by \[ \mathbf{X}_k = \sum_{j=1}^k \delta_j\mathbf{u}_j\mathbf{v}_j^t. \]

Without the \(L_1\) constraints, this algorithm gives the ordinary SVD.

As before the \(\delta_j\)s, \(\mathbf{u}_j\)s and \(\mathbf{v}_j\)s are stacked into matrices \(\mathbf{D}_k\), \(\mathbf{U}_k\) and \(\mathbf{V}_k\): \(\mathbf{X}_k = \mathbf{U}_k\mathbf{D}_k\mathbf{V}_k^t\).

Note that the \(\mathbf{u}\) vectors are generally not orthogonal (i.e. \(\mathbf{u}_i^t\mathbf{u}_j\neq 0\) for \(i\neq j\)) (the same holds for the \(\mathbf{v}\) vectors).


2.4 Sparse PCA via PMD

Just like the SVD can be used to find the PCA solution, the PMD can be used to find a sparse PCA solution (SPC).

For a PCA, it is only important to have sparse loadings (in the \(\mathbf{v}\)s); the scores (involving the \(\mathbf{u}\)s) need not be sparse.

The rank-1 solutions thus satisfy \[ \min_{\delta,u,v} \Vert \mathbf{X} - \delta\mathbf{u}\mathbf{v}^t\Vert_F^2 \text{ subject to } \delta \geq 0, \Vert \mathbf{u}\Vert_2^2=\Vert \mathbf{v}\Vert_2^2=1, \Vert \mathbf{v}\Vert_1 \leq c \] (i.e. no sparsity constraint on \(\mathbf{u}\)).

Hence the PCA scores \[ \mathbf{Z}_k = \mathbf{U}_k\mathbf{D}_k \] relate to \(\mathbf{X}_k\) through the sparse loadings in \(\mathbf{V}_k\).


It can be shown that the SPC solution (rank-1) also maximises \[ \mathbf{v}^t\mathbf{X}^t\mathbf{X}\mathbf{v} \text{ subject to } \Vert \mathbf{u}\Vert_2^2=\Vert \mathbf{v}\Vert_2^2=1, \Vert \mathbf{v}\Vert_1 \leq c \] (i.e. it maximises the variance of the PCs, subject to sparsity constraint on loadings).

From these two slides we may conclude that we can use the PMD as the basis of a sparse PCA, and that the interpretation is as before in terms of maximising variance. Only now we hope that only a few features will give a non-zero loading in the \(\mathbf{v}\)-vectors. Biplots may again be constructed (with fewer vectors to be plotted).


3 Toxicogenomics analysis with a sparse PCA

3.1 SPCA

library(PMA)

spcX <- SPC(
  toxData[,-1] %>%
    as.matrix,
  K=30,
  sumabsv = 5)
#> 12345678910111213141516171819
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
par(mfrow=c(1,2))
barplot(spcX$d^2,ylab="eigenvalue",cex.lab=1.5)
barplot(spcX$prop.var.explained,
     ylab="cummulative variance",cex.lab=1.5)

  • The plot on the left shows the eigenvalues (squared singular values) from the PMD.

    • This suggests that the first one or two are the most important, but all eigenvalues remain substantially larger than zero.
  • The figure on the right shows the cumulative percentage of variance retained in the PCs.

    • These percentages are now no longer calculated as before as ratio’s of sums of eigenvalues.
    • A more complicated calculation is required now, because the PCs are no longer uncorrelated with one another.
    • Thus part of the information (variance) of one PC is shared with part of the information of another PCs (i.e. non-zero correlation or covariance).

3.2 Loading

Loadings of the first two PCs, with constraint \(\Vert\mathbf{v}\Vert_1 < 5\).

k <- 2
Vk <- spcX$v[,1:k]
colnames(Vk) <- paste0("V",1:k)

grid.arrange(
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V1)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,1]), col = "red") ,
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V2)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,2]), col = "red"),
  ncol=2)

These plots show the loadings of the first two sparse PCs. Many of the loadings are now exactly equal to zero. Only few genes show large loadings.

3.3 Change constraint

We now repeat the analysis, but with the constraint \(\Vert \mathbf{v} \Vert_1 < 1\). This gives a much sparser solution with only two genes with non-zero loadings in each dimension.

spcX1 <- SPC(
  toxData[,-1] %>%
    as.matrix,
  K=30,
  sumabsv = 1)
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
#> 12
k <- 2
Vk <- spcX1$v[,1:k]
colnames(Vk) <- paste0("V", 1:k)

grid.arrange(
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V1)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,1]), col = "red") ,
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V2)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,2]), col = "red"),
  ncol=2)


We now repeat the analysis, but with the constraint \(\Vert \mathbf{v} \Vert_1 < 10\). This gives a much less sparse solution with several genes with non-zero loadings.

spcX10 <- SPC(
  toxData[,-1] %>%
    as.matrix,
  K=30,
  sumabsv = 10)
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
#> 1234567891011121314151617181920
k <- 2
Vk <- spcX10$v[,1:k]
colnames(Vk) <- paste0("V",1:k)

grid.arrange(
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V1)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,1]), col = "red") ,
  Vk %>%
    as.data.frame %>%
    mutate(geneID = 1:nrow(Vk)) %>%
    ggplot(aes(x = geneID, y = V2)) +
    geom_point(pch=21) +
    geom_hline(yintercept = c(-2,0,2)*sd(Vk[,2]), col = "red"),
  ncol=2)


3.4 Variance explained

Variance explained for \(c=1,5,10\) and \(+\infty\).

data.frame(
  PCs = 1:30,
  "c1" = spcX1$prop.var.explained,
  "c5" = spcX$prop.var.explained,
  "c10" = spcX10$prop.var.explained,
  "conventional" = cumsum(svdX$d^2)/sum(svdX$d^2)
  ) %>%
  gather("method","varExpl",-1) %>%
  ggplot(aes(x = PCs, y = varExpl, color = method)) +
  geom_line() +
  geom_point() +
  ylab("Variance explained (%)")

This graph illustrates that the percentages of variance explained by the PCs are smaller for sparser solutions. Thus in choosing an appropriate penalty parameter \(c_2\) one should not only look at the sparseness of the solution, but also at the percentage of variance contained in the first few PCs. If this percentage is very small, then the graphs of the scores (or biplots) are no longer very informative (i.e. they will not tell the full story). Thus a compromise must be looked for.

library(latex2exp)

Uk5 <- spcX$u[,1:k]
Dk5 <- diag(spcX$d[1:k])
Zk5 <- Uk5%*%Dk5
colnames(Zk5) <- paste0("Z",1:k)

spc5 <- Zk5 %>%
  as.data.frame %>%
  mutate(BA = toxData %>% pull(BA)) %>%
  ggplot(aes(x= Z1, y = Z2, color = BA)) +
  geom_point(size = 3) +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  geom_point(size = 3, pch = 21, color = "black") +
  ggtitle(TeX("$c_2 = 5$"))

Uk1 <- spcX1$u[,1:k]
Dk1 <- diag(spcX1$d[1:k])
Zk1 <- Uk1%*%Dk1
colnames(Zk1) <- paste0("Z",1:k)

spc1 <- Zk1 %>%
  as.data.frame %>%
  mutate(BA = toxData %>% pull(BA)) %>%
  ggplot(aes(x= Z1, y = Z2, color = BA)) +
  geom_point(size = 3) +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  geom_point(size = 3, pch = 21, color = "black") +
  ggtitle(TeX("$c_2 = 1$"))

Uk10 <- spcX10$u[,1:k]
Dk10 <- diag(spcX10$d[1:k])
Zk10 <- Uk10%*%Dk10
colnames(Zk10) <- paste0("Z",1:k)

spc10 <- Zk10 %>%
  as.data.frame %>%
  mutate(BA = toxData %>% pull(BA)) %>%
  ggplot(aes(x= Z1, y = Z2, color = BA)) +
  geom_point(size = 3) +
  scale_colour_gradient2(low = "blue",mid="white",high="red") +
  geom_point(size = 3, pch = 21, color = "black") +
  ggtitle(TeX("$c_2 = 10$"))

grid.arrange(spc1, spc5, spc10, pca + ggtitle(TeX("$c_2 = \\infty")), ncol = 2)

Finally we show the plots of the scores on the first two sparse PCs for \(c_2=1\) (top-left), \(c_2=5\) (top-right) and \(c_2=10\) (bottom-left) and \(c_2=\infty\) (bottom-right, convention SVD).

Note, that the most sparse solution (top-left) shows a strong positive correlation between the first two PCs (as a consequence of the loss of orthogonality of the singular vectors).

All graphs succeed quite well in separating the toxic from the non-toxic compounds based on their gene expression.

  • Hence, we may expect that the two genes identified from the most sparse solution play an important role in the development of toxic effects.

  • Note, however, that our data analysis did not provide a scientific proof for this conclusion: (sparse) PCA is only an exploratory or descriptive data analysis method that helps the data-analyst to gain insight into the data.

  • The fact that we found a few genes that appear to be associated with toxicity is of course a nice result.

  • It is important to understand that the toxicity outcome was not used in the (sparse) PCA. We only used the toxicity to color the points in the graphs. PCA is an unsupervised method.

Acknowledgement

  • Olivier Thas for sharing his materials of Analysis of High Dimensional Data 2019-2020, which I used as the starting point for this chapter.

Session info

Session info
#> [1] "2024-10-07 12:48:12 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
#>  bit           4.5.0   2024-09-20 [1] CRAN (R 4.4.1)
#>  bit64         4.5.2   2024-09-22 [1] CRAN (R 4.4.1)
#>  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)
#>  crayon        1.5.3   2024-06-20 [1] CRAN (R 4.4.0)
#>  curl          5.2.3   2024-09-20 [1] CRAN (R 4.4.1)
#>  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)
#>  ggplot2     * 3.5.1   2024-04-23 [1] CRAN (R 4.4.0)
#>  glue          1.8.0   2024-09-30 [1] CRAN (R 4.4.1)
#>  gridExtra   * 2.3     2017-09-09 [1] CRAN (R 4.4.0)
#>  gtable        0.3.5   2024-04-22 [1] CRAN (R 4.4.0)
#>  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)
#>  latex2exp   * 0.9.6   2022-11-28 [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)
#>  PMA         * 1.2-4   2024-09-03 [1] CRAN (R 4.4.1)
#>  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)
#>  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)
#>  vroom         1.6.5   2023-12-05 [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
#> 
#> ──────────────────────────────────────────────────────────────────────────────
LS0tCnRpdGxlOiAiNC4gU3BhcnNlIFNpbmd1bGFyIFZhbHVlIERlY29tcG9zaXRpb24iCmF1dGhvcjogIkxpZXZlbiBDbGVtZW50IgpkYXRlOiAic3RhdE9taWNzLCBHaGVudCBVbml2ZXJzaXR5IChodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8pIgpvdXRwdXQ6CiAgYm9va2Rvd246OnBkZl9kb2N1bWVudDI6CiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4CmFsd2F5c19hbGxvd19odG1sOiB0cnVlCi0tLQoKYGBge3IsIGNoaWxkPSJfc2V0dXAuUm1kIn0KYGBgCgpgYGB7ciBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ3JpZEV4dHJhKQpgYGAKCiMgSW50cm9kdWN0aW9uCgpJbiBoaWdoIGRpbWVuc2lvbmFsIGRhdGEgdGhlIFBDcyBtYXkgaGF2ZSB1bmNsZWFyIGludGVycHJldGF0aW9uIGJlY2F1c2UgdGhleSBkZXBlbmQgb24gYWxsICRwJCBvcmlnaW5hbCB2YXJpYWJsZXMuCgojIyBFeGFtcGxlOiBUb3hpY29nZW5vbWljcyBpbiBlYXJseSBkcnVnIGRldmVsb3BtZW50CgojIyMgQmFja2dyb3VuZAoKLSBFZmZlY3Qgb2YgY29tcG91bmQgb24gZ2VuZSBleHByZXNzaW9uLgoKLSBJbnNpZ2h0IGluIGFjdGlvbiBhbmQgdG94aWNpdHkgb2YgZHJ1ZyBpbiBlYXJseSBwaGFzZQotIERldGVybWluZSBhY3Rpdml0eSB3aXRoIGJpby1hc3NheTogZS5nLiBiaW5kaW5nIGFmaW5pdHkgb2YgY29tcG91bmQgdG8gY2VsbHdhbGwgcmVjZXB0ZXIgKHRhcmdldCwgSUM1MCkuCi0gRWFybHkgcGhhc2U6ICAyMCB0byA1MCBjb21wb3VuZHMKLSBCYXNlZCBvbiBpbiB2aXRybyByZXN1bHRzIG9uZSBhaW1zIHRvIGdldCBpbnNpZ2h0IGluIGhvdyB0byBidWlsZCBiZXR0ZXIgY29tcG91bmQgKGhpZ2hlciBvbi10YXJnZXQgYWN0aXZpdHkgbGVzcyB0b3hpY2l0eS4KLSBTbWFsbCB2YXJpYW50aW9ucyBpbiBtb2VsY3VsYWlyIHN0cnVjdHVyZSBsZWFkIHRvIHZhcmlhdGlvbnMgaW4gQkEgYW5kIGdlbmUgZXhwcmVzc2lvbi4KLSBBaW06IEJ1aWxkIG1vZGVsIHRvIHByZWRpY3QgYmlvLWFjdGl2aXR5IGJhc2VkIG9uIGdlbmUgZXhwcmVzc2lvbiBpbiBsaXZlciBjZWxsIGxpbmUuCgojIyMgRGF0YQoKLSAzMCBjaGVtaWNhbCBjb21wb3VuZHMgaGF2ZSBiZWVuIHNjcmVlbmVkIGZvciB0b3hpY2l0eQoKLSBCaW9hc3NheSBkYXRhIG9uIHRveGljaXR5IHNjcmVlbmluZwoKLSBHZW5lIGV4cHJlc3Npb25zIGluIGEgbGl2ZXIgY2VsbCBsaW5lIGFyZSBwcm9maWxlZCBmb3IgZWFjaCBjb21wb3VuZCAoNDAwMCBnZW5lcykKCgpgYGB7cn0KdG94RGF0YSA8LSByZWFkX2NzdigKICAiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3N0YXRPbWljcy9IREEyMDIwL2RhdGEvdG94RGF0YUNlbnRlcmVkLmNzdiIsCiAgY29sX3R5cGVzID0gY29scygpCikKc3ZkWCA8LSBzdmQodG94RGF0YVssLTFdKQpgYGAKCkRhdGEgaXMgYWxyZWFkeSBjZW50ZXJlZDoKCmBgYHtyfQp0b3hEYXRhICU+JQogIGNvbE1lYW5zICU+JQogIHJhbmdlCmBgYAoKYGBge3J9CiB0b3hEYXRhICU+JQogIG5hbWVzICU+JQogIGhlYWQKYGBgCgotIEZpcnN0IGNvbHVtbiBjb250YWlucyBkYXRhIG9uIEJpb2Fzc2F5LgotIFRoZSBoaWdoZXIgdGhlIHNjb3JlIG9uIEJpb2Fzc2F5IHRoZSBtb3JlIHRveGljIHRoZSBjb21wb3VuZAotIE90aGVyIGNvbHVtbnMgY29udGFpbiBkYXRhIG9uIGdlbmUgZXhwcmVzc2lvbiBYMSwgLi4uICwgWDQwMDAKCiMjIyBEYXRhIGV4cGxvcmF0aW9uCgpgYGB7cn0KdG94RGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHg9IiIseT1CQSkpICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZT1OQSkgKwogIGdlb21fcG9pbnQocG9zaXRpb249ImppdHRlciIpCmBgYAoKYGBge3J9CnN2ZFggPC0gdG94RGF0YVssLTFdICU+JQogIHN2ZAoKayA8LSAyClZrIDwtIHN2ZFgkdlssMTprXQpVayA8LSBzdmRYJHVbLDE6a10KRGsgPC0gZGlhZyhzdmRYJGRbMTprXSkKWmsgPC0gVWslKiVEawpjb2xuYW1lcyhaaykgPC0gcGFzdGUwKCJaIiwxOmspCmNvbG5hbWVzKFZrKSA8LSBwYXN0ZTAoIlYiLDE6aykKCnBjYSA8LSBaayAlPiUKICBhcy5kYXRhLmZyYW1lICU+JQogIG11dGF0ZShCQSA9IHRveERhdGEgJT4lIHB1bGwoQkEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9IFoxLCB5ID0gWjIsIGNvbG9yID0gQkEpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gImJsdWUiLG1pZD0id2hpdGUiLGhpZ2g9InJlZCIpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBwY2ggPSAyMSwgY29sb3IgPSAiYmxhY2siKQpwY2EKYGBgCgotIFNjb3JlcyBvbiB0aGUgZmlyc3QgdHdvIHByaW5jaXBhbCBjb21wb25lbnRzIChvciBNRFMgcGxvdCkuIC0gRWFjaCBwb2ludCBjb3JyZXNwb25kcyB0byBhIGNvbXBvdW5kLgotIENvbG9yIGNvZGUgcmVmZXJzIHRvIHRoZSB0b3hpY2l0eSBzY29yZSAoaGlnaGVyIHNjb3JlIG1vcmUgdG94aWMpLgotIENsZWFyIHNlcGFyYXRpb24gYmV0d2VlbiBjb21wb3VuZHMgYWNjb3JkaW5nIHRvIHRveGljaXR5LgoKLS0tCgotIE5leHQgbG9naWMgc3RlcCBpbiBhIFBDQSBpcyB0byBpbnRlcnByZXQgdGhlIHByaW5jaXBhbCBjb21wb25lbnRzLgotIFdlIHRodXMgaGF2ZSB0byBhc3Nlc3MgdGhlIGxvYWRpbmdzLgotIFdlIGNhbiBhZGQgYSB2ZWN0b3IgZm9yIGVhY2ggZ2VuZSB0byBnZXQgYSBiaXBsb3QsIGJ1dCB0aGlzIHdvdWxkIHJlcXVpcmUgcGxvdHRpbmcgNDAwMCB2ZWN0b3JzLCB3aGljaCB3b3VsZCByZW5kZXIgdGhlIHBsb3QgdW5yZWFkYWJsZS4KCkFsdGVybmF0aXZlIGdyYXBoIHRvIGxvb2sgYXQgdGhlIG1hbnkgbG9hZGluZ3Mgb2YgdGhlIGZpcnN0IHR3byBQQ3MuCgpgYGB7cn0KZ3JpZC5hcnJhbmdlKAogIFZrICU+JQogICAgYXMuZGF0YS5mcmFtZSAlPiUKICAgIG11dGF0ZShnZW5lSUQgPSAxOm5yb3coVmspKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IGdlbmVJRCwgeSA9IFYxKSkgKwogICAgZ2VvbV9wb2ludChwY2g9MjEpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoLTIsMCwyKSpzZChWa1ssMV0pLCBjb2wgPSAicmVkIikgLAogIFZrICU+JQogICAgYXMuZGF0YS5mcmFtZSAlPiUKICAgIG11dGF0ZShnZW5lSUQgPSAxOm5yb3coVmspKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IGdlbmVJRCwgeSA9IFYyKSkgKwogICAgZ2VvbV9wb2ludChwY2g9MjEpICsKICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoLTIsMCwyKSpzZChWa1ssMl0pLCBjb2wgPSAicmVkIiksCiAgbmNvbD0yKQpgYGAKCi0gSXQgaXMgYWxtb3N0IGltcG9zc2libGUgdG8gaW50ZXJwcmV0IHRoZSBQQ3MgYmVjYXVzZSB0aGVyZSBhcmUgNDAwMCBnZW5lcyBjb250cmlidXRpbmcgdG8gZWFjaCBQQy4KCi0gSW4gYW4gYXR0ZW1wdCB0byBmaW5kIHRoZSBtb3N0IGltcG9ydGFudCBnZW5lcyAoaW4gdGhlIHNlbnNlIHRoYXQgdGhleSBkcml2ZSB0aGUgaW50ZXJwcmV0YXRpb24gb2YgdGhlIFBDcyksIHRoZSBwbG90cyBzaG93IGhvcml6b250YWwgcmVmZXJlbmNlIGxpbmVzOiB0aGUgYXZlcmFnZSBvZiB0aGUgbG9hZGluZ3MsIGFuZCB0aGUgYXZlcmFnZSDCsSB0d2ljZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBsb2FkaW5ncy4gSW4gYmV0d2VlbiB0aGUgbGluZXMgd2UgZXhwZWN0cyBhYm91dCA5NSUgb2YgdGhlIGxvYWRpbmdzIChpZiB0aGV5IHdlcmUgbm9ybWFsbHkgZGlzdHJpYnV0ZWQpLgoKLSBUaGUgcG9pbnRzIG91dHNpZGUgdGhlIGJhbmQgY29tZSBmcm9tIHRoZSBnZW5lcyB0aGF0IGhhdmUgcmF0aGVyIGxhcmdlIGxvYWRpbmdzIChpbiBhYnNvbHV0ZSB2YWx1ZSkgYW5kIGhlbmNlIGFyZSBpbXBvcnRhbnQgZm9yIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgUENzLgoKLSBOb3RlLCB0aGF0IHBhcnRpY3VsYXJseSBmb3IgdGhlIGZpcnN0IFBDLCBvbmx5IGEgZmV3IGdlbmVzIHNob3cgYSBtYXJrZWRseSBsYXJnZSBsb2FkaW5ncyB0aGF0IGFyZSBuZWdhdGl2ZS4gVGhpcyBtZWFucyB0aGF0IGFuIHVwcmVndWxhdGlvbiBvZiB0aGVzZSBnZW5lcyB3aWxsIGxlYWQgdG8gbG93IHNjb3JlcyBvbiBQQzEuCi0gVGhlc2UgZ2VuZXMgd2lsbCB2ZXJ5IGxpa2VseSBwbGF5IGFuIGltcG9ydGFudCByb2xlIGluIHRoZSB0b3hpY2l0eSBtZWNoYW5pc20uCi0gSW5kZWVkLCBsb3cgc2NvcmVzIG9uIFBDMSBhcmUgaW4gdGhlIGRpcmVjdGlvbiBvZiBtb3JlIHRveGljaXR5LgoKLS0tCgojIyBTcGFyc2UgbWF0cml4IGRlY29tcG9zaXRpb24KCkJhc2ljIGlkZWEgb2Ygc3BhcnNlIG1hdHJpeCBkZWNvbXBvc2l0aW9uIG9mIGFuICRuIFx0aW1lcyBwJCBtYXRyaXggJFxtYXRoYmZ7WH0kfQoKRmluZCBhIHJhbmsgJGskIGFwcHJveGltYXRpb24gb2YgJFxtYXRoYmZ7WH0kIG9mIHRoZSBmb3JtClxbClxtYXRoYmZ7WH1fayA9IFxzdW1fe2o9MX1eayBcZGVsdGFfaiBcbWF0aGJme3V9X2pcbWF0aGJme3Z9X2pedApcXQpzdWNoIHRoYXQgbWFueSBvZiB0aGUgZWxlbWVudHMgaW4gdGhlICRcbWF0aGJme3Z9X2okIGFuZCBvciAkXG1hdGhiZnt1fV9qJCBhcmUgZXhhY3RseSB6ZXJvLgoKV2UgZm9jdXMgb24gdGhlICoqUGVuYWxpc2VkIE1hdHJpeCBEZWNvbXBvc2l0aW9uKiogKFBNRCkgbWV0aG9kIG9mIFdpdHRlbiAqZXQgYWwuKiAoMjAwOSwgQmlvc3RhdGlzdGljcywgMTAsIDMsIDUxNS01MzQpLgoKV2hlbiBtYW55IG9mIHRoZSBsb2FkaW5ncyBpbiAkXG1hdGhiZnt2fV9qJCBhcmUgemVybywgdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSBQQyBvbmx5IGRlcGVuZHMgb24gdGhlIGZlYXR1cmVzIHRoYXQgY29ycmVzcG9uZCB0byB0aGUgcmVtYWluaW5nIG5vbi16ZXJvIGxvYWRpbmdzLgoKLS0tCgojIFBlbmFsaXNlZCBNYXRyaXggRGVjb21wb3NpdGlvbgoKIyMgUmFuayAxIGFwcHJveGltYXRpb24KClRoZSByYW5rLTEgUE1EIGFwcHJveGltYXRpb24gb2YgdGhlICRuIFx0aW1lcyBwJCBtYXRyaXggJFxtYXRoYmZ7WH0kIGlzIG9mIHRoZSBmb3JtClxbClxtYXRoYmZ7WH1fMSA9IFxkZWx0YV8xXG1hdGhiZnt1fV8xXG1hdGhiZnt2fV8xXnQsClxdCgphbmQgaXQgaXMgdGhlIHNvbHV0aW9uIHRvIHRoZSBmb2xsb3dpbmcgbWluaW1pc2F0aW9uIHByb2JsZW06ClxbClxtaW5fe1xkZWx0YSx1LHZ9IFxWZXJ0IFxtYXRoYmZ7WH0gLSBcZGVsdGFcbWF0aGJme3V9XG1hdGhiZnt2fV50XFZlcnRfRl4yIFx0ZXh0eyBzdWJqZWN0IHRvIH0KXGRlbHRhIFxnZXEgMCwgXFZlcnQgXG1hdGhiZnt1fVxWZXJ0XzJeMj1cVmVydCBcbWF0aGJme3Z9XFZlcnRfMl4yPTEsIFxWZXJ0XG1hdGhiZnt1fVxWZXJ0XzFcbGVxIGNfMSwgXFZlcnQgXG1hdGhiZnt2fVxWZXJ0XzEgXGxlcSBjXzIKXF0KCmZvciBzb21lIHVzZXItZGVmaW5lZCBjb25zdGFudHMgJGNfMSxjXzI+MCQuCgpUaGUgY29uc3RyYWludHMgJFxWZXJ0XG1hdGhiZnt1fVxWZXJ0XzFcbGVxIGNfMSQgYW5kICRcVmVydCBcbWF0aGJme3Z9XFZlcnRfMSBcbGVxIGNfMiQgYXJlIGtub3duIGFzICRMXzEkIG9yIGxhc3NvIGNvbnN0cmFpbnRzLgoKCk5vdGUsClxbClxWZXJ0XG1hdGhiZnt1fVxWZXJ0XzFcID0gXHN1bV97aT0xfV5uIFx2ZXJ0IHVfaSBcdmVydCBcdGV4dHsgYW5kIH1cVmVydFxtYXRoYmZ7dn1cVmVydF8xXCA9IFxzdW1fe2k9MX1ecCBcdmVydCB2X2lcdmVydC4KXF0KCklmICRjXzE9K1xpbmZ0eSQsIHRoZW4gdGhlIGNvbnN0cmFpbnQgb24gJFxtYXRoYmZ7dX0kIGlzIHJlbW92ZWQgKHNpbWlsYXIgZm9yICRjXzIkKS4KCi0tLQoKTm90ZSB0aGF0ClxbCiAgXG1pbl97XGRlbHRhLHUsdn0gXFZlcnQgXG1hdGhiZntYfSAtIFxkZWx0YVxtYXRoYmZ7dX1cbWF0aGJme3Z9XnRcVmVydF9GXjIgPSBcbWluX3tBOlx0ZXh0e3Jhbmt9KEEpPTF9ICBcVmVydCBcbWF0aGJme1h9IC0gQVxWZXJ0X0ZeMgpcXQp3aXRoICRBPVhfMSQgKHRydW5jYXRlZCBTVkQgb2YgJFxtYXRoYmZ7WH0kLCB0cnVuY2F0ZWQgYWZ0ZXIgMSB0ZXJtKS4gSG93ZXZlciwgdGhlIFBNRCByZXF1aXJlcyBhIGNvbnN0cmFpbmVkIG1pbmltaXNhdGlvbi4gVGhlIHVuY29uc3RyYWluZWQgc29sdXRpb24gYXJpc2VzIHdpdGggJGNfMT1jXzI9K1xpbmZ0eSQuCgpOb3RlIHRoYXQgJFxWZXJ0IFxtYXRoYmZ7dX1cVmVydF8yXjI9XFZlcnQgXG1hdGhiZnt2fVxWZXJ0XzJeMj0xJCBtYXkgYWxzbyBiZSB3cml0dGVuIGFzICRcbWF0aGJme3V9XnRcbWF0aGJme3V9PVxtYXRoYmZ7dn1edFxtYXRoYmZ7dn09MSQgKGkuZS4gdGhlIHZlY3RvcnMgYXJlIG5vcm1hbGlzZWQsIGp1c3QgbGlrZSBmb3IgdGhlIHNpbmd1bGFyIHZlY3RvcnMgb2YgdGhlIFNWRCkuCgotIFdlIGFsc28gYXBwbGllZCB0aGUgJExfMSQgY29uc3RyYWludCB0byB0aGUgbGVhc3Qtc3F1YXJlcyBlc3RpbWF0b3Igb2YgdGhlIHJlZ3Jlc3Npb24gcGFyYW1ldGVycyBpbiBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsLgoKLSBFZmZlY3Qgd2FzIHRoYXQgbWFueSBvZiB0aGUgcGFyYW1ldGVyIGVzdGltYXRlcyBhcmUgc2V0IGV4YWN0bHkgdG8gemVyby4KCi0gSGVyZSwgdGhlIHNhbWUgZWZmZWN0IHdpbGwgdGFrZSBwbGFjZS4gTWFueSBvZiB0aGUgZWxlbWVudHMgaW4gJFxtYXRoYmZ7dX0kIGFuZCAkXG1hdGhiZnt2fSQgd2lsbCBiZSBzZXQgZXhhY3RseSB0byB6ZXJvLgoKLSBJbiBwYXJ0aWN1bGFyLCB0aGUgc21hbGxlciAkY18yJCwgdGhlIG1vcmUgZWxlbWVudHMgb2YgJFxtYXRoYmZ7dn0kIHdpbGwgYmUgc2V0IHRvIHplcm8gKHNocmlua2FnZSkuCgotIFNpbWlsYXJseSwgdGhlIHNtYWxsZXIgJGNfMSQsIHRoZSBtb3JlIGVsZW1lbnRzIG9mICRcbWF0aGJme3V9JCB3aWxsIGJlIHNldCB0byB6ZXJvLgoKLSBUaGUgY2hvaWNlIG9mICRjXzEkIGFuZCAkY18yJCBkZXBlbmRzIG9uIHRoZSBvYmplY3RpdmUgb2YgdGhlIGRhdGEgYW5hbHlzaXMgYW5kIHdpbGwgYmUgZGlzY3Vzc2VkIGxhdGVyLgoKVGhlIHNvbHV0aW9uIGlzIGtub3duIGFzIGEgcmFuay0xIFBNRCBhcHByb3hpbWF0aW9uIG9mICRcbWF0aGJme1h9JCBiZWNhdXNlICRcZGVsdGFfMVxtYXRoYmZ7dX1fMVxtYXRoYmZ7dn1fMV50JCBpcyBhIG1hdHJpeCBvZiByYW5rIDEuCgotLS0KCiMjIFJhbmstMiBhcHByb3hpbWF0aW9uCgpUaGUgcmFuay0yIFBNRCBhcHByb3hpbWF0aW9uIGlzIGZvdW5kIGFzIGZvbGxvd3M6CgoxLiBmaW5kIHRoZSByYW5rLTEgUE1EIGFzIGJlZm9yZTogJFxtYXRoYmZ7WH1fMT1cZGVsdGFfMVxtYXRoYmZ7dX1fMVxtYXRoYmZ7dn1fMV50JDsKCjIuIGNvbXB1dGUKXFsKXHRpbGRle1xtYXRoYmZ7WH19XzIgPSBcbWF0aGJme1h9LVxtYXRoYmZ7WH1fMSwKXF0KCmFuZCBjb21wdXRlIHRoZSByYW5rLTEgUE1EIG9mICRcdGlsZGV7XG1hdGhiZntYfX1fMiQsIHdoaWNoIGdpdmVzICRcZGVsdGFfMiwgXG1hdGhiZnt1fV8yLCBcbWF0aGJme3Z9XzIkOwoKMy4gdGhlIHJhbmstMiBQTUQgb2YgJFxtYXRoYmZ7WH0kIGlzIHRoZW4gZ2l2ZW4gYnkKXFsKXG1hdGhiZntYfV8yID0gXGRlbHRhXzFcbWF0aGJme3V9XzFcbWF0aGJme3Z9XzFedCtcZGVsdGFfMlxtYXRoYmZ7dX1fMlxtYXRoYmZ7dn1fMl50LgpcXQoKTm90ZSB0aGF0IHRoZSAkXG1hdGhiZnt1fSQncyBhbmQgJFxtYXRoYmZ7dn0kJ3MgYXJlIG5vdCBuZWNlc3NhcmlseSBvcnRob2dvbmFsIChvbmx5IGlmICRjXzEsIGNfMj0rXGluZnR5JCkuCgotLS0KCiMjIFJhbmstayBhcHByb3hpbWF0aW9uCgpUaGUgcmFuay0kayQgUE1EIGFwcHJveGltYXRpb24gaXMgZm91bmQgYXMgZm9sbG93czoKCjEuIGZpbmQgdGhlIHJhbmstMSBQTUQgYXMgYmVmb3JlOiAkXHRpbGRle1xtYXRoYmZ7WH19XzE9XGRlbHRhXzFcbWF0aGJme3V9XzFcbWF0aGJme3Z9XzFedCQ7CgoyLiBzZXQgJGo9MSQ7CgozLiBjb21wdXRlIFxbClx0aWxkZXtcbWF0aGJme1h9fV97aisxfSA9IFxtYXRoYmZ7WH0tXHRpbGRle1xtYXRoYmZ7WH19XzEtXGNkb3RzIC0gXHRpbGRle1xtYXRoYmZ7WH19X2osClxdIGFuZCBjb21wdXRlIHRoZSByYW5rLTEgUE1EIG9mICRcdGlsZGV7XG1hdGhiZntYfX1fe2orMX0kLCB3aGljaCBnaXZlcyAkXGRlbHRhX3tqKzF9LCBcbWF0aGJme3V9X3tqKzF9LCBcbWF0aGJme3Z9X3tqKzF9JDsKCjQuIGlmICRqPGskLCBpbmNyZWFzZSAkaiQgd2l0aCBvbmUgYW5kIGdvIGJhY2sgdG8gc3RlcCAzOyBvdGhlcndpc2UgZ28gdG8gc3RlcCA1OwoKNS4gdGhlIHJhbmstJGskIFBNRCBvZiAkXG1hdGhiZntYfSQgaXMgdGhlbiBnaXZlbiBieQogICBcWwogICAgIFxtYXRoYmZ7WH1fayA9IFxzdW1fe2o9MX1eayBcZGVsdGFfalxtYXRoYmZ7dX1falxtYXRoYmZ7dn1fal50LgogICBcXQoKV2l0aG91dCB0aGUgJExfMSQgY29uc3RyYWludHMsIHRoaXMgYWxnb3JpdGhtIGdpdmVzIHRoZSBvcmRpbmFyeSBTVkQuCgpBcyBiZWZvcmUgdGhlICRcZGVsdGFfaiRzLCAkXG1hdGhiZnt1fV9qJHMgYW5kICRcbWF0aGJme3Z9X2okcyBhcmUgc3RhY2tlZCBpbnRvIG1hdHJpY2VzICRcbWF0aGJme0R9X2skLCAkXG1hdGhiZntVfV9rJCBhbmQgJFxtYXRoYmZ7Vn1fayQ6ICRcbWF0aGJme1h9X2sgPSBcbWF0aGJme1V9X2tcbWF0aGJme0R9X2tcbWF0aGJme1Z9X2tedCQuCgoKTm90ZSB0aGF0IHRoZSAkXG1hdGhiZnt1fSQgdmVjdG9ycyBhcmUgZ2VuZXJhbGx5IG5vdCBvcnRob2dvbmFsIChpLmUuICRcbWF0aGJme3V9X2ledFxtYXRoYmZ7dX1falxuZXEgMCQgZm9yICRpXG5lcSBqJCkgKHRoZSBzYW1lIGhvbGRzIGZvciB0aGUgJFxtYXRoYmZ7dn0kIHZlY3RvcnMpLgoKLS0tCgojIyBTcGFyc2UgUENBIHZpYSBQTUQKCkp1c3QgbGlrZSB0aGUgU1ZEIGNhbiBiZSB1c2VkIHRvIGZpbmQgdGhlIFBDQSBzb2x1dGlvbiwgdGhlIFBNRCBjYW4gYmUgdXNlZCB0byBmaW5kIGEgc3BhcnNlIFBDQSBzb2x1dGlvbiAoU1BDKS4KCkZvciBhIFBDQSwgaXQgaXMgb25seSBpbXBvcnRhbnQgdG8gaGF2ZSBzcGFyc2UgbG9hZGluZ3MgKGluIHRoZSAkXG1hdGhiZnt2fSRzKTsgdGhlIHNjb3JlcyAoaW52b2x2aW5nIHRoZSAkXG1hdGhiZnt1fSRzKSBuZWVkIG5vdCBiZSBzcGFyc2UuCgpUaGUgcmFuay0xIHNvbHV0aW9ucyB0aHVzIHNhdGlzZnkKXFsKICBcbWluX3tcZGVsdGEsdSx2fSBcVmVydCBcbWF0aGJme1h9IC0gXGRlbHRhXG1hdGhiZnt1fVxtYXRoYmZ7dn1edFxWZXJ0X0ZeMiBcdGV4dHsgc3ViamVjdCB0byB9CiAgXGRlbHRhIFxnZXEgMCwgXFZlcnQgXG1hdGhiZnt1fVxWZXJ0XzJeMj1cVmVydCBcbWF0aGJme3Z9XFZlcnRfMl4yPTEsIFxWZXJ0IFxtYXRoYmZ7dn1cVmVydF8xIFxsZXEgYwpcXQooaS5lLiBubyBzcGFyc2l0eSBjb25zdHJhaW50IG9uICRcbWF0aGJme3V9JCkuCgpIZW5jZSB0aGUgUENBIHNjb3JlcwpcWwogXG1hdGhiZntafV9rID0gXG1hdGhiZntVfV9rXG1hdGhiZntEfV9rClxdCnJlbGF0ZSB0byAkXG1hdGhiZntYfV9rJCB0aHJvdWdoIHRoZSBzcGFyc2UgbG9hZGluZ3MgaW4gJFxtYXRoYmZ7Vn1fayQuCgoKLS0tCgpJdCBjYW4gYmUgc2hvd24gdGhhdCB0aGUgU1BDIHNvbHV0aW9uIChyYW5rLTEpIGFsc28gbWF4aW1pc2VzClxbCiAgXG1hdGhiZnt2fV50XG1hdGhiZntYfV50XG1hdGhiZntYfVxtYXRoYmZ7dn0gXHRleHR7IHN1YmplY3QgdG8gfQogIFxWZXJ0IFxtYXRoYmZ7dX1cVmVydF8yXjI9XFZlcnQgXG1hdGhiZnt2fVxWZXJ0XzJeMj0xLCBcVmVydCBcbWF0aGJme3Z9XFZlcnRfMSBcbGVxIGMKXF0KKGkuZS4gaXQgbWF4aW1pc2VzIHRoZSB2YXJpYW5jZSBvZiB0aGUgUENzLCBzdWJqZWN0IHRvIHNwYXJzaXR5IGNvbnN0cmFpbnQgb24gbG9hZGluZ3MpLgoKCkZyb20gdGhlc2UgdHdvIHNsaWRlcyB3ZSBtYXkgY29uY2x1ZGUgdGhhdCB3ZSBjYW4gdXNlIHRoZSBQTUQgYXMgdGhlIGJhc2lzIG9mIGEgc3BhcnNlIFBDQSwgYW5kIHRoYXQgdGhlIGludGVycHJldGF0aW9uIGlzIGFzIGJlZm9yZSBpbiB0ZXJtcyBvZiBtYXhpbWlzaW5nIHZhcmlhbmNlLiBPbmx5IG5vdyB3ZSBob3BlIHRoYXQgb25seSBhIGZldyBmZWF0dXJlcyB3aWxsIGdpdmUgYSBub24temVybyBsb2FkaW5nIGluIHRoZSAkXG1hdGhiZnt2fSQtdmVjdG9ycy4gQmlwbG90cyBtYXkgYWdhaW4gYmUgY29uc3RydWN0ZWQgKHdpdGggZmV3ZXIgdmVjdG9ycyB0byBiZSBwbG90dGVkKS4KCi0tLQoKIyBUb3hpY29nZW5vbWljcyBhbmFseXNpcyB3aXRoIGEgc3BhcnNlIFBDQQoKIyMgU1BDQQoKYGBge3J9CmxpYnJhcnkoUE1BKQoKc3BjWCA8LSBTUEMoCiAgdG94RGF0YVssLTFdICU+JQogICAgYXMubWF0cml4LAogIEs9MzAsCiAgc3VtYWJzdiA9IDUpCgpwYXIobWZyb3c9YygxLDIpKQpiYXJwbG90KHNwY1gkZF4yLHlsYWI9ImVpZ2VudmFsdWUiLGNleC5sYWI9MS41KQpiYXJwbG90KHNwY1gkcHJvcC52YXIuZXhwbGFpbmVkLAogICAgIHlsYWI9ImN1bW11bGF0aXZlIHZhcmlhbmNlIixjZXgubGFiPTEuNSkKCmBgYAoKCi0gVGhlIHBsb3Qgb24gdGhlIGxlZnQgc2hvd3MgdGhlIGVpZ2VudmFsdWVzIChzcXVhcmVkIHNpbmd1bGFyIHZhbHVlcykgZnJvbSB0aGUgUE1ELgoKICAtIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgZmlyc3Qgb25lIG9yIHR3byBhcmUgdGhlIG1vc3QgaW1wb3J0YW50LCBidXQgYWxsIGVpZ2VudmFsdWVzIHJlbWFpbiBzdWJzdGFudGlhbGx5IGxhcmdlciB0aGFuIHplcm8uCgotIFRoZSBmaWd1cmUgb24gdGhlIHJpZ2h0IHNob3dzIHRoZSBjdW11bGF0aXZlIHBlcmNlbnRhZ2Ugb2YgdmFyaWFuY2UgcmV0YWluZWQgaW4gdGhlIFBDcy4KCiAgICAtIFRoZXNlIHBlcmNlbnRhZ2VzIGFyZSBub3cgbm8gbG9uZ2VyIGNhbGN1bGF0ZWQgYXMgYmVmb3JlIGFzIHJhdGlvJ3Mgb2Ygc3VtcyBvZiBlaWdlbnZhbHVlcy4KICAgIC0gQSBtb3JlIGNvbXBsaWNhdGVkIGNhbGN1bGF0aW9uIGlzIHJlcXVpcmVkIG5vdywgYmVjYXVzZSB0aGUgUENzIGFyZSBubyBsb25nZXIgdW5jb3JyZWxhdGVkIHdpdGggb25lIGFub3RoZXIuCiAgICAtIFRodXMgcGFydCBvZiB0aGUgaW5mb3JtYXRpb24gKHZhcmlhbmNlKSBvZiBvbmUgUEMgaXMgc2hhcmVkIHdpdGggcGFydCBvZiB0aGUgaW5mb3JtYXRpb24gb2YgYW5vdGhlciBQQ3MgKGkuZS4gbm9uLXplcm8gY29ycmVsYXRpb24gb3IgY292YXJpYW5jZSkuCgojIyBMb2FkaW5nCgpMb2FkaW5ncyBvZiB0aGUgZmlyc3QgdHdvIFBDcywgd2l0aCBjb25zdHJhaW50ICRcVmVydFxtYXRoYmZ7dn1cVmVydF8xIDwgNSQuCgpgYGB7cn0KayA8LSAyClZrIDwtIHNwY1gkdlssMTprXQpjb2xuYW1lcyhWaykgPC0gcGFzdGUwKCJWIiwxOmspCgpncmlkLmFycmFuZ2UoCiAgVmsgJT4lCiAgICBhcy5kYXRhLmZyYW1lICU+JQogICAgbXV0YXRlKGdlbmVJRCA9IDE6bnJvdyhWaykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZUlELCB5ID0gVjEpKSArCiAgICBnZW9tX3BvaW50KHBjaD0yMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMiwwLDIpKnNkKFZrWywxXSksIGNvbCA9ICJyZWQiKSAsCiAgVmsgJT4lCiAgICBhcy5kYXRhLmZyYW1lICU+JQogICAgbXV0YXRlKGdlbmVJRCA9IDE6bnJvdyhWaykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZUlELCB5ID0gVjIpKSArCiAgICBnZW9tX3BvaW50KHBjaD0yMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMiwwLDIpKnNkKFZrWywyXSksIGNvbCA9ICJyZWQiKSwKICBuY29sPTIpCmBgYAoKVGhlc2UgcGxvdHMgc2hvdyB0aGUgbG9hZGluZ3Mgb2YgdGhlIGZpcnN0IHR3byBzcGFyc2UgUENzLiBNYW55IG9mIHRoZSBsb2FkaW5ncyBhcmUgbm93IGV4YWN0bHkgZXF1YWwgdG8gemVyby4gT25seSBmZXcgZ2VuZXMgc2hvdyBsYXJnZSBsb2FkaW5ncy4KCiMjIENoYW5nZSBjb25zdHJhaW50CgpXZSBub3cgcmVwZWF0IHRoZSBhbmFseXNpcywgYnV0IHdpdGggdGhlIGNvbnN0cmFpbnQgJFxWZXJ0IFxtYXRoYmZ7dn0gXFZlcnRfMSA8IDEkLiBUaGlzIGdpdmVzIGEgbXVjaCBzcGFyc2VyIHNvbHV0aW9uIHdpdGggb25seSB0d28gZ2VuZXMgd2l0aCBub24temVybyBsb2FkaW5ncyBpbiBlYWNoIGRpbWVuc2lvbi4KCmBgYHtyfQoKc3BjWDEgPC0gU1BDKAogIHRveERhdGFbLC0xXSAlPiUKICAgIGFzLm1hdHJpeCwKICBLPTMwLAogIHN1bWFic3YgPSAxKQoKayA8LSAyClZrIDwtIHNwY1gxJHZbLDE6a10KY29sbmFtZXMoVmspIDwtIHBhc3RlMCgiViIsIDE6aykKCmdyaWQuYXJyYW5nZSgKICBWayAlPiUKICAgIGFzLmRhdGEuZnJhbWUgJT4lCiAgICBtdXRhdGUoZ2VuZUlEID0gMTpucm93KFZrKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBnZW5lSUQsIHkgPSBWMSkpICsKICAgIGdlb21fcG9pbnQocGNoPTIxKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKC0yLDAsMikqc2QoVmtbLDFdKSwgY29sID0gInJlZCIpICwKICBWayAlPiUKICAgIGFzLmRhdGEuZnJhbWUgJT4lCiAgICBtdXRhdGUoZ2VuZUlEID0gMTpucm93KFZrKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBnZW5lSUQsIHkgPSBWMikpICsKICAgIGdlb21fcG9pbnQocGNoPTIxKSArCiAgICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSBjKC0yLDAsMikqc2QoVmtbLDJdKSwgY29sID0gInJlZCIpLAogIG5jb2w9MikKYGBgCgotLS0KCldlIG5vdyByZXBlYXQgdGhlIGFuYWx5c2lzLCBidXQgd2l0aCB0aGUgY29uc3RyYWludCAkXFZlcnQgXG1hdGhiZnt2fSBcVmVydF8xIDwgMTAkLiBUaGlzIGdpdmVzIGEgbXVjaCBsZXNzIHNwYXJzZSBzb2x1dGlvbiB3aXRoIHNldmVyYWwgZ2VuZXMgd2l0aCBub24temVybyBsb2FkaW5ncy4KCmBgYHtyfQpzcGNYMTAgPC0gU1BDKAogIHRveERhdGFbLC0xXSAlPiUKICAgIGFzLm1hdHJpeCwKICBLPTMwLAogIHN1bWFic3YgPSAxMCkKCmsgPC0gMgpWayA8LSBzcGNYMTAkdlssMTprXQpjb2xuYW1lcyhWaykgPC0gcGFzdGUwKCJWIiwxOmspCgpncmlkLmFycmFuZ2UoCiAgVmsgJT4lCiAgICBhcy5kYXRhLmZyYW1lICU+JQogICAgbXV0YXRlKGdlbmVJRCA9IDE6bnJvdyhWaykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZUlELCB5ID0gVjEpKSArCiAgICBnZW9tX3BvaW50KHBjaD0yMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMiwwLDIpKnNkKFZrWywxXSksIGNvbCA9ICJyZWQiKSAsCiAgVmsgJT4lCiAgICBhcy5kYXRhLmZyYW1lICU+JQogICAgbXV0YXRlKGdlbmVJRCA9IDE6bnJvdyhWaykpICU+JQogICAgZ2dwbG90KGFlcyh4ID0gZ2VuZUlELCB5ID0gVjIpKSArCiAgICBnZW9tX3BvaW50KHBjaD0yMSkgKwogICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gYygtMiwwLDIpKnNkKFZrWywyXSksIGNvbCA9ICJyZWQiKSwKICBuY29sPTIpCmBgYAoKLS0tCgojIyBWYXJpYW5jZSBleHBsYWluZWQKClZhcmlhbmNlIGV4cGxhaW5lZCBmb3IgJGM9MSw1LDEwJCBhbmQgJCtcaW5mdHkkLgoKYGBge3J9CmRhdGEuZnJhbWUoCiAgUENzID0gMTozMCwKICAiYzEiID0gc3BjWDEkcHJvcC52YXIuZXhwbGFpbmVkLAogICJjNSIgPSBzcGNYJHByb3AudmFyLmV4cGxhaW5lZCwKICAiYzEwIiA9IHNwY1gxMCRwcm9wLnZhci5leHBsYWluZWQsCiAgImNvbnZlbnRpb25hbCIgPSBjdW1zdW0oc3ZkWCRkXjIpL3N1bShzdmRYJGReMikKICApICU+JQogIGdhdGhlcigibWV0aG9kIiwidmFyRXhwbCIsLTEpICU+JQogIGdncGxvdChhZXMoeCA9IFBDcywgeSA9IHZhckV4cGwsIGNvbG9yID0gbWV0aG9kKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHlsYWIoIlZhcmlhbmNlIGV4cGxhaW5lZCAoJSkiKQpgYGAKClRoaXMgZ3JhcGggaWxsdXN0cmF0ZXMgdGhhdCB0aGUgcGVyY2VudGFnZXMgb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBQQ3MgYXJlIHNtYWxsZXIgZm9yIHNwYXJzZXIgc29sdXRpb25zLiBUaHVzIGluIGNob29zaW5nIGFuIGFwcHJvcHJpYXRlIHBlbmFsdHkgcGFyYW1ldGVyICRjXzIkIG9uZSBzaG91bGQgbm90IG9ubHkgbG9vayBhdCB0aGUgc3BhcnNlbmVzcyBvZiB0aGUgc29sdXRpb24sIGJ1dCAgYWxzbyBhdCB0aGUgcGVyY2VudGFnZSBvZiB2YXJpYW5jZSBjb250YWluZWQgaW4gdGhlIGZpcnN0IGZldyBQQ3MuIElmIHRoaXMgcGVyY2VudGFnZSBpcyB2ZXJ5IHNtYWxsLCB0aGVuIHRoZSBncmFwaHMgb2YgdGhlIHNjb3JlcyAob3IgYmlwbG90cykgYXJlIG5vIGxvbmdlciB2ZXJ5IGluZm9ybWF0aXZlIChpLmUuIHRoZXkgd2lsbCBub3QgdGVsbCB0aGUgZnVsbCBzdG9yeSkuIFRodXMgYSBjb21wcm9taXNlIG11c3QgYmUgbG9va2VkIGZvci4KCmBgYHtyfQpsaWJyYXJ5KGxhdGV4MmV4cCkKClVrNSA8LSBzcGNYJHVbLDE6a10KRGs1IDwtIGRpYWcoc3BjWCRkWzE6a10pClprNSA8LSBVazUlKiVEazUKY29sbmFtZXMoWms1KSA8LSBwYXN0ZTAoIloiLDE6aykKCnNwYzUgPC0gWms1ICU+JQogIGFzLmRhdGEuZnJhbWUgJT4lCiAgbXV0YXRlKEJBID0gdG94RGF0YSAlPiUgcHVsbChCQSkpICU+JQogIGdncGxvdChhZXMoeD0gWjEsIHkgPSBaMiwgY29sb3IgPSBCQSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsbWlkPSJ3aGl0ZSIsaGlnaD0icmVkIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHBjaCA9IDIxLCBjb2xvciA9ICJibGFjayIpICsKICBnZ3RpdGxlKFRlWCgiJGNfMiA9IDUkIikpCgpVazEgPC0gc3BjWDEkdVssMTprXQpEazEgPC0gZGlhZyhzcGNYMSRkWzE6a10pClprMSA8LSBVazElKiVEazEKY29sbmFtZXMoWmsxKSA8LSBwYXN0ZTAoIloiLDE6aykKCnNwYzEgPC0gWmsxICU+JQogIGFzLmRhdGEuZnJhbWUgJT4lCiAgbXV0YXRlKEJBID0gdG94RGF0YSAlPiUgcHVsbChCQSkpICU+JQogIGdncGxvdChhZXMoeD0gWjEsIHkgPSBaMiwgY29sb3IgPSBCQSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50Mihsb3cgPSAiYmx1ZSIsbWlkPSJ3aGl0ZSIsaGlnaD0icmVkIikgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDMsIHBjaCA9IDIxLCBjb2xvciA9ICJibGFjayIpICsKICBnZ3RpdGxlKFRlWCgiJGNfMiA9IDEkIikpCgpVazEwIDwtIHNwY1gxMCR1WywxOmtdCkRrMTAgPC0gZGlhZyhzcGNYMTAkZFsxOmtdKQpaazEwIDwtIFVrMTAlKiVEazEwCmNvbG5hbWVzKFprMTApIDwtIHBhc3RlMCgiWiIsMTprKQoKc3BjMTAgPC0gWmsxMCAlPiUKICBhcy5kYXRhLmZyYW1lICU+JQogIG11dGF0ZShCQSA9IHRveERhdGEgJT4lIHB1bGwoQkEpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9IFoxLCB5ID0gWjIsIGNvbG9yID0gQkEpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMykgKwogIHNjYWxlX2NvbG91cl9ncmFkaWVudDIobG93ID0gImJsdWUiLG1pZD0id2hpdGUiLGhpZ2g9InJlZCIpICsKICBnZW9tX3BvaW50KHNpemUgPSAzLCBwY2ggPSAyMSwgY29sb3IgPSAiYmxhY2siKSArCiAgZ2d0aXRsZShUZVgoIiRjXzIgPSAxMCQiKSkKCmdyaWQuYXJyYW5nZShzcGMxLCBzcGM1LCBzcGMxMCwgcGNhICsgZ2d0aXRsZShUZVgoIiRjXzIgPSBcXGluZnR5IikpLCBuY29sID0gMikKYGBgCgpGaW5hbGx5IHdlIHNob3cgdGhlIHBsb3RzIG9mIHRoZSBzY29yZXMgb24gdGhlIGZpcnN0IHR3byBzcGFyc2UgUENzIGZvciAkY18yPTEkICh0b3AtbGVmdCksICRjXzI9NSQgKHRvcC1yaWdodCkgYW5kICRjXzI9MTAkIChib3R0b20tbGVmdCkgYW5kICRjXzI9XGluZnR5JCAoYm90dG9tLXJpZ2h0LCBjb252ZW50aW9uIFNWRCkuCgpOb3RlLCB0aGF0IHRoZSBtb3N0IHNwYXJzZSBzb2x1dGlvbiAodG9wLWxlZnQpIHNob3dzIGEgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIGZpcnN0IHR3byBQQ3MgKGFzIGEgY29uc2VxdWVuY2Ugb2YgdGhlIGxvc3Mgb2Ygb3J0aG9nb25hbGl0eSBvZiB0aGUgc2luZ3VsYXIgdmVjdG9ycykuCgpBbGwgZ3JhcGhzIHN1Y2NlZWQgcXVpdGUgd2VsbCBpbiBzZXBhcmF0aW5nIHRoZSB0b3hpYyBmcm9tIHRoZSBub24tdG94aWMgY29tcG91bmRzIGJhc2VkIG9uIHRoZWlyIGdlbmUgZXhwcmVzc2lvbi4KCi0gSGVuY2UsIHdlIG1heSBleHBlY3QgdGhhdCB0aGUgdHdvIGdlbmVzIGlkZW50aWZpZWQgZnJvbSB0aGUgbW9zdCBzcGFyc2Ugc29sdXRpb24gcGxheSBhbiBpbXBvcnRhbnQgcm9sZSBpbiB0aGUgZGV2ZWxvcG1lbnQgb2YgdG94aWMgZWZmZWN0cy4KCi0gTm90ZSwgaG93ZXZlciwgdGhhdCBvdXIgZGF0YSBhbmFseXNpcyBkaWQgbm90IHByb3ZpZGUgYSBzY2llbnRpZmljIHByb29mIGZvciB0aGlzIGNvbmNsdXNpb246IChzcGFyc2UpIFBDQSBpcyBvbmx5IGFuIGV4cGxvcmF0b3J5IG9yIGRlc2NyaXB0aXZlIGRhdGEgYW5hbHlzaXMgbWV0aG9kIHRoYXQgaGVscHMgdGhlIGRhdGEtYW5hbHlzdCB0byBnYWluIGluc2lnaHQgaW50byB0aGUgZGF0YS4KCi0gVGhlIGZhY3QgdGhhdCB3ZSBmb3VuZCBhIGZldyBnZW5lcyB0aGF0IGFwcGVhciB0byBiZSBhc3NvY2lhdGVkIHdpdGggdG94aWNpdHkgaXMgb2YgY291cnNlIGEgbmljZSByZXN1bHQuCgotIEl0IGlzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIHRoYXQgdGhlIHRveGljaXR5IG91dGNvbWUgd2FzIG5vdCB1c2VkIGluIHRoZSAoc3BhcnNlKSBQQ0EuIFdlIG9ubHkgdXNlZCB0aGUgdG94aWNpdHkgdG8gY29sb3IgdGhlIHBvaW50cyBpbiB0aGUgZ3JhcGhzLiBQQ0EgaXMgYW4gdW5zdXBlcnZpc2VkIG1ldGhvZC4KCiMgQWNrbm93bGVkZ2VtZW50IHstfQoKLSBPbGl2aWVyIFRoYXMgZm9yIHNoYXJpbmcgaGlzIG1hdGVyaWFscyBvZiBBbmFseXNpcyBvZiBIaWdoIERpbWVuc2lvbmFsIERhdGEgMjAxOS0yMDIwLCB3aGljaCBJIHVzZWQgYXMgdGhlIHN0YXJ0aW5nIHBvaW50IGZvciB0aGlzIGNoYXB0ZXIuCgpgYGB7ciwgY2hpbGQ9Il9zZXNzaW9uLWluZm8uUm1kIn0KYGBgCg==