I am using mtcars data set to visualize this.

mtcars <- tibble::rownames_to_column(mtcars, "car")
summary(mtcars)
     car                 mpg             cyl             disp      
 Length:32          Min.   :10.40   Min.   :4.000   Min.   : 71.1  
 Class :character   1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8  
 Mode  :character   Median :19.20   Median :6.000   Median :196.3  
                    Mean   :20.09   Mean   :6.188   Mean   :230.7  
                    3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0  
                    Max.   :33.90   Max.   :8.000   Max.   :472.0  
       hp             drat             wt             qsec      
 Min.   : 52.0   Min.   :2.760   Min.   :1.513   Min.   :14.50  
 1st Qu.: 96.5   1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89  
 Median :123.0   Median :3.695   Median :3.325   Median :17.71  
 Mean   :146.7   Mean   :3.597   Mean   :3.217   Mean   :17.85  
 3rd Qu.:180.0   3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90  
 Max.   :335.0   Max.   :4.930   Max.   :5.424   Max.   :22.90  
       vs               am              gear            carb      
 Min.   :0.0000   Min.   :0.0000   Min.   :3.000   Min.   :1.000  
 1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
 Median :0.0000   Median :0.0000   Median :4.000   Median :2.000  
 Mean   :0.4375   Mean   :0.4062   Mean   :3.688   Mean   :2.812  
 3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
 Max.   :1.0000   Max.   :1.0000   Max.   :5.000   Max.   :8.000  

lares

library(lares)

Ranked cross correlation:

corr_cross(
  df = mtcars, 
  max_pvalue = 0.05,
  type = 1, top = 20, grid = F
)

Local cross correlation:

corr_cross(
  df = mtcars, 
  max_pvalue = 0.05,
  type = 2, local = 3
)

Correlation with a specific variable:

corr_var(df = mtcars, var = cyl, ranks = F)

corr_var(df = mtcars, var = qsec, ranks = F)

ggcorrplot

library(ggcorrplot)

Stores a matrix containing the p-values of correlations:

p_mat <- ggcorrplot::cor_pmat(mtcars[,sapply(mtcars, is.numeric)])

cor_pmat function is also available in rstatix package.

Stores a correlation matrix:

corr_mat <- cor(mtcars[,sapply(mtcars, is.numeric)])

A simple correlogram:

ggcorrplot(corr_mat)

Only lower part:

ggcorrplot(corr_mat, type = "lower")

Order by strength:

ggcorrplot(corr_mat, type = "lower", hc.order = TRUE, p.mat = p_mat)

Furnished further:

ggcorrplot(corr_mat,
           hc.order = TRUE,
           method = "square",
           type = "lower",
           title = "Correlogram of mtcars dataset", 
           legend.title = "Correlation", 
           outline.color = "white",
           ggtheme = ggplot2::theme_minimal,
           colors = c("#91412d", "white", "#45839a"),
           lab = TRUE)

Cross out insignificant correlation values:

ggcorrplot(corr_mat,
           hc.order = TRUE,
           method = "square",
           type = "lower",
           p.mat = p_mat)

Using circle type:

ggcorrplot(corr_mat,
           hc.order = TRUE,
           method = "circle",
           type = "lower",
           outline.color = "white",
           ggtheme = ggplot2::theme_minimal,
           colors = c("#91412d", "white", "#45839a"),
           lab = TRUE
           )

corrplot

Learn more at: https://cran.r-project.org/web/packages/corrplot/vignettes/corrplot-intro.html

library(corrplot)

Stores a correlation matrix:

corr_mat <- cor(mtcars[,sapply(mtcars, is.numeric)])
corrplot(corr_mat)

Mixed correlogram:

corrplot.mixed(
  corr_mat,
  upper = "square",
  lower = "number",
  addgrid.col = "black", # The color of the grid
  tl.col = "black"  # The color of text label
  )

Ordered correlogram:

corr_mat_ord <- corrMatOrder(corr_mat, order = 'FPC')
corrplot.mixed(
  corr_mat[corr_mat_ord,corr_mat_ord],
  upper = "square",
  lower = "number",
  addgrid.col = "black", # The color of the grid
  tl.col = "black"  # The color of text label
  )

ggstatsplot

library(ggstatsplot)
ggcorrmat(
  data = mtcars,
  colors = c("#B2182B", "white", "#4D4D4D"),
  title = "Correlalogram for mtcars", 
  matrix.type = "lower", 
  type = "parametric", pch = ""
)

This function has an argument sig.level that does not work as it should be. So I have added pch = “” so that no cell is crossed out.

pairs

pairs(iris[,1:4],
      pch=23, #Shape of the points
      col=as.numeric(iris$Species)+1,   #Colour of the outline of the points
      bg=scales::alpha(as.numeric(iris$Species)+1, 0.3),  #Fill colour of the points with reduced colour intensity
      cex=1.5,  #Size of the points
      upper.panel=NULL,  #Do not display the the upper panel
      labels=gsub("[[:punct:]]"," ",colnames(iris[,1:4])) #Substitution the full stop in the feature names with a space
      )  

GGally

Suitable for small number of variables only.

library(GGally)

ggcorr

ggcorr(mtcars, label = T, hjust = 1, layout.exp = 2, size = 3, label_size = 2)

ggpairs

ggpairs(mtcars[,2:6], 
        title="Correlogram of mtcars dataset",
        lower = list(continuous = "points", combo = "dot_no_facet")) 

ggpairs(mtcars[,2:6], 
        title="Correlogram of mtcars dataset",
        upper = list(continuous = wrap("cor", size=2)))+
  theme(axis.text = element_text(colour = "black", size = 4),
        strip.text = element_text(size = 5))

Custom functions to be used in diagonal, upper and lower portion of the correlogram.

# Function for diagonal
my_bin <- function(data, mapping, binwidth, ...) {
  ggplot(data = data, mapping = mapping) +
    geom_histogram(aes(y=..density..), 
                   binwidth = binwidth, fill = "#298dff", alpha = 0.2) +
    geom_density(color = "red") + theme_classic()
}

# Function for upper portion
upperfun <- function(data, mapping, method, symbol, ...){
  x <- eval_data_col(data, mapping$x)
  y <- eval_data_col(data, mapping$y)
  corr <- cor(x, y, method=method, use='complete.obs')
  ggally_text(
    label = paste(symbol, as.character(round(corr, 2))), 
    mapping = aes(), xP = 0.5, yP = 0.5, color = 'black',
    ...
  ) + theme_classic()
}

# Function for lower portion
lowerfun <- function(data, mapping) {
  ggplot(data = data, mapping = mapping)+ 
    geom_point(alpha = 1, color = "#298dff", pch = 20) + 
    geom_smooth(method = "lm", formula = y ~ x, 
                color = "red", size = 0.5) +
    theme_classic()
}
ggpairs(
  mtcars[, 2:6],
  
  diag = list(continuous =  wrap(my_bin, binwidth = 10)),
  upper = list(continuous = wrap(upperfun, method = 'pearson', symbol = expression('r ='))),
  lower = list(continuous = wrap(lowerfun))
  ) +
  theme(plot.caption = element_text(hjust = 0.5, face = "bold"))+
  labs(caption="Correlogram of mtcars dataset")

PerformanceAnalytics

library(PerformanceAnalytics)
chart.Correlation(mtcars[,sapply(mtcars, is.numeric)], 
                  histogram=TRUE, pch=19)

rstatix

library(rstatix)

Correlation Matrix:

rstatix::cor_mat(mtcars[,sapply(mtcars, is.numeric)])
# A tibble: 11 × 12
   rowname   mpg   cyl  disp    hp   drat    wt   qsec    vs     am  gear   carb
 * <chr>   <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>  <dbl> <dbl>  <dbl> <dbl>  <dbl>
 1 mpg      1    -0.85 -0.85 -0.78  0.68  -0.87  0.42   0.66  0.6    0.48 -0.55 
 2 cyl     -0.85  1     0.9   0.83 -0.7    0.78 -0.59  -0.81 -0.52  -0.49  0.53 
 3 disp    -0.85  0.9   1     0.79 -0.71   0.89 -0.43  -0.71 -0.59  -0.56  0.39 
 4 hp      -0.78  0.83  0.79  1    -0.45   0.66 -0.71  -0.72 -0.24  -0.13  0.75 
 5 drat     0.68 -0.7  -0.71 -0.45  1     -0.71  0.091  0.44  0.71   0.7  -0.091
 6 wt      -0.87  0.78  0.89  0.66 -0.71   1    -0.17  -0.55 -0.69  -0.58  0.43 
 7 qsec     0.42 -0.59 -0.43 -0.71  0.091 -0.17  1      0.74 -0.23  -0.21 -0.66 
 8 vs       0.66 -0.81 -0.71 -0.72  0.44  -0.55  0.74   1     0.17   0.21 -0.57 
 9 am       0.6  -0.52 -0.59 -0.24  0.71  -0.69 -0.23   0.17  1      0.79  0.058
10 gear     0.48 -0.49 -0.56 -0.13  0.7   -0.58 -0.21   0.21  0.79   1     0.27 
11 carb    -0.55  0.53  0.39  0.75 -0.091  0.43 -0.66  -0.57  0.058  0.27  1    

P-value matrix of correlations:

rstatix::cor_pmat(mtcars[,sapply(mtcars, is.numeric)])
# A tibble: 11 × 12
   rowname      mpg      cyl     disp            hp       drat        wt    qsec
   <chr>      <dbl>    <dbl>    <dbl>         <dbl>      <dbl>     <dbl>   <dbl>
 1 mpg     0        6.11e-10 9.38e-10 0.000000179   0.0000178  1.29e- 10 1.71e-2
 2 cyl     6.11e-10 0        1.80e-12 0.00000000348 0.00000824 1.22e-  7 3.66e-4
 3 disp    9.38e-10 1.80e-12 0        0.0000000714  0.00000528 1.22e- 11 1.31e-2
 4 hp      1.79e- 7 3.48e- 9 7.14e- 8 0             0.00999    4.15e-  5 5.77e-6
 5 drat    1.78e- 5 8.24e- 6 5.28e- 6 0.00999       0          4.78e-  6 6.2 e-1
 6 wt      1.29e-10 1.22e- 7 1.22e-11 0.0000415     0.00000478 2.27e-236 3.39e-1
 7 qsec    1.71e- 2 3.66e- 4 1.31e- 2 0.00000577    0.62       3.39e-  1 0      
 8 vs      3.42e- 5 1.84e- 8 5.24e- 6 0.00000294    0.0117     9.8 e-  4 1.03e-6
 9 am      2.85e- 4 2.15e- 3 3.66e- 4 0.18          0.00000473 1.13e-  5 2.06e-1
10 gear    5.4 e- 3 4.17e- 3 9.64e- 4 0.493         0.00000836 4.59e-  4 2.43e-1
11 carb    1.08e- 3 1.94e- 3 2.53e- 2 0.000000783   0.621      1.46e-  2 4.54e-5
# ℹ 4 more variables: vs <dbl>, am <dbl>, gear <dbl>, carb <dbl>

ggplot2

corr_mat <- rstatix::cor_mat(mtcars[,sapply(mtcars, is.numeric)])
corr_mat_df <- corr_mat %>% 
  tidyr::pivot_longer(cols = -rowname, names_to = 'variables', values_to = 'r')
head(corr_mat_df)
# A tibble: 6 × 3
  rowname variables     r
  <chr>   <chr>     <dbl>
1 mpg     mpg        1   
2 mpg     cyl       -0.85
3 mpg     disp      -0.85
4 mpg     hp        -0.78
5 mpg     drat       0.68
6 mpg     wt        -0.87
corr_mat_df %>% 
  ggplot(aes(rowname, variables, fill = r)) + 
  geom_tile() +
  labs(x = "variables", y = "variables") +
  scale_fill_gradient(low = "blue", high = "red") +
  geom_text(aes(label = r), size = 3)

heatmap

corr_mat <- cor(mtcars[,sapply(mtcars, is.numeric)])
col <- colorRampPalette(c("blue", "white", "red"))(20)
heatmap(corr_mat, col = col, symm = TRUE)

levelplot

lattice::levelplot(cor(mtcars[c('cyl','disp','hp','drat','wt','qsec','vs')]), 
                   pretty = TRUE)

sjPlot

library(sjPlot)
tab_corr(iris[,sapply(iris, is.numeric)], 
         show.p = T, 
         p.numeric = T,
         title = "Table 1: Correlation Matrix", 
         var.labels = c("Sepal Length", "Sepal Width", "Petal Length", "Petal Width"),
         triangle = "lower"
         )
Table 1: Correlation Matrix
  Sepal Length Sepal Width Petal Length Petal Width
Sepal Length        
Sepal Width -0.118
(.152)
     
Petal Length 0.872
(<.001)
-0.428
(<.001)
   
Petal Width 0.818
(<.001)
-0.366
(<.001)
0.963
(<.001)
 
Computed correlation used pearson-method with listwise-deletion.

correlation

library(correlation)
correlation(iris[,sapply(iris, is.numeric)]) |> kableExtra::kable()
Parameter1 Parameter2 r CI CI_low CI_high t df_error p Method n_Obs
Sepal.Length Sepal.Width -0.1175698 0.95 -0.2726932 0.0435116 -1.440287 148 0.1518983 Pearson correlation 150
Sepal.Length Petal.Length 0.8717538 0.95 0.8270363 0.9055080 21.646019 148 0.0000000 Pearson correlation 150
Sepal.Length Petal.Width 0.8179411 0.95 0.7568971 0.8648361 17.296454 148 0.0000000 Pearson correlation 150
Sepal.Width Petal.Length -0.4284401 0.95 -0.5508771 -0.2879499 -5.768449 148 0.0000001 Pearson correlation 150
Sepal.Width Petal.Width -0.3661259 0.95 -0.4972130 -0.2186966 -4.786461 148 0.0000081 Pearson correlation 150
Petal.Length Petal.Width 0.9628654 0.95 0.9490525 0.9729853 43.387237 148 0.0000000 Pearson correlation 150
correlation(iris[,sapply(iris, is.numeric)]) |> 
  summary(redundant = TRUE, digits = 3) |>
  visualisation_recipe() |>
  plot()

correlation(iris[,sapply(iris, is.numeric)]) |> 
  as.matrix() |>
  cor_sort() |>
  visualisation_recipe() |>
  plot()

correlation(iris[,sapply(iris, is.numeric)]) |> 
  visualisation_recipe() |>
  plot()

LS0tDQp0aXRsZTogIkNvcnJlbG9ncmFtIg0KYXV0aG9yOiAiTUQgQUhTQU5VTCBJU0xBTSINCm91dHB1dDogDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiBmYWxzZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KICAgIHRvY19kZXB0aDogMg0KICAgIHRoZW1lOiBjZXJ1bGVhbg0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQotLS0NCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGNvbW1lbnQgPSAiIiwgcHJvbXB0ID0gRiwgbWVzc2FnZT1GLCB3YXJuaW5nID0gRg0KKQ0KYGBgDQoNCmBgYHtyIGtsaXBweSwgZWNobz1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KIyByZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigicmxlc3VyL2tsaXBweSIpDQprbGlwcHk6OmtsaXBweSh0b29sdGlwX21lc3NhZ2UgPSAnQ2xpY2sgdG8gY29weScsIHRvb2x0aXBfc3VjY2VzcyA9ICdEb25lJywNCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gYygndG9wJywgJ3JpZ2h0JykpDQpgYGANCg0KIyMgey50YWJzZXQgLnRhYnNldC1mYWRlIC50YWJzZXQtcGlsbHN9DQoNCkkgYW0gdXNpbmcgYG10Y2Fyc2AgZGF0YSBzZXQgdG8gdmlzdWFsaXplIHRoaXMuIA0KYGBge3J9DQptdGNhcnMgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4obXRjYXJzLCAiY2FyIikNCnN1bW1hcnkobXRjYXJzKQ0KYGBgDQoNCi0tLQ0KDQojIyMgbGFyZXMNCg0KDQpgYGB7cn0NCmxpYnJhcnkobGFyZXMpDQpgYGANCg0KUmFua2VkIGNyb3NzIGNvcnJlbGF0aW9uOg0KYGBge3J9DQpjb3JyX2Nyb3NzKA0KICBkZiA9IG10Y2FycywgDQogIG1heF9wdmFsdWUgPSAwLjA1LA0KICB0eXBlID0gMSwgdG9wID0gMjAsIGdyaWQgPSBGDQopDQpgYGANCg0KTG9jYWwgY3Jvc3MgY29ycmVsYXRpb246DQpgYGB7cn0NCmNvcnJfY3Jvc3MoDQogIGRmID0gbXRjYXJzLCANCiAgbWF4X3B2YWx1ZSA9IDAuMDUsDQogIHR5cGUgPSAyLCBsb2NhbCA9IDMNCikNCmBgYA0KDQpDb3JyZWxhdGlvbiB3aXRoIGEgc3BlY2lmaWMgdmFyaWFibGU6DQpgYGB7cn0NCmNvcnJfdmFyKGRmID0gbXRjYXJzLCB2YXIgPSBjeWwsIHJhbmtzID0gRikNCmNvcnJfdmFyKGRmID0gbXRjYXJzLCB2YXIgPSBxc2VjLCByYW5rcyA9IEYpDQpgYGANCg0KIyMjIGdnY29ycnBsb3QNCg0KYGBge3J9DQpsaWJyYXJ5KGdnY29ycnBsb3QpDQpgYGANCg0KU3RvcmVzIGEgbWF0cml4IGNvbnRhaW5pbmcgdGhlIHAtdmFsdWVzIG9mIGNvcnJlbGF0aW9uczoNCmBgYHtyfQ0KcF9tYXQgPC0gZ2djb3JycGxvdDo6Y29yX3BtYXQobXRjYXJzWyxzYXBwbHkobXRjYXJzLCBpcy5udW1lcmljKV0pDQpgYGANCmBjb3JfcG1hdGAgZnVuY3Rpb24gaXMgYWxzbyBhdmFpbGFibGUgaW4gYHJzdGF0aXhgIHBhY2thZ2UuIA0KDQpTdG9yZXMgYSBjb3JyZWxhdGlvbiBtYXRyaXg6DQpgYGB7cn0NCmNvcnJfbWF0IDwtIGNvcihtdGNhcnNbLHNhcHBseShtdGNhcnMsIGlzLm51bWVyaWMpXSkNCmBgYA0KDQpBIHNpbXBsZSBjb3JyZWxvZ3JhbToNCmBgYHtyfQ0KZ2djb3JycGxvdChjb3JyX21hdCkNCmBgYA0KDQpPbmx5IGxvd2VyIHBhcnQ6DQpgYGB7cn0NCmdnY29ycnBsb3QoY29ycl9tYXQsIHR5cGUgPSAibG93ZXIiKQ0KYGBgDQoNCk9yZGVyIGJ5IHN0cmVuZ3RoOg0KYGBge3J9DQpnZ2NvcnJwbG90KGNvcnJfbWF0LCB0eXBlID0gImxvd2VyIiwgaGMub3JkZXIgPSBUUlVFLCBwLm1hdCA9IHBfbWF0KQ0KYGBgDQoNCkZ1cm5pc2hlZCBmdXJ0aGVyOg0KYGBge3J9DQpnZ2NvcnJwbG90KGNvcnJfbWF0LA0KICAgICAgICAgICBoYy5vcmRlciA9IFRSVUUsDQogICAgICAgICAgIG1ldGhvZCA9ICJzcXVhcmUiLA0KICAgICAgICAgICB0eXBlID0gImxvd2VyIiwNCiAgICAgICAgICAgdGl0bGUgPSAiQ29ycmVsb2dyYW0gb2YgbXRjYXJzIGRhdGFzZXQiLCANCiAgICAgICAgICAgbGVnZW5kLnRpdGxlID0gIkNvcnJlbGF0aW9uIiwgDQogICAgICAgICAgIG91dGxpbmUuY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICBnZ3RoZW1lID0gZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzkxNDEyZCIsICJ3aGl0ZSIsICIjNDU4MzlhIiksDQogICAgICAgICAgIGxhYiA9IFRSVUUpDQpgYGANCg0KQ3Jvc3Mgb3V0IGluc2lnbmlmaWNhbnQgY29ycmVsYXRpb24gdmFsdWVzOg0KYGBge3J9DQpnZ2NvcnJwbG90KGNvcnJfbWF0LA0KICAgICAgICAgICBoYy5vcmRlciA9IFRSVUUsDQogICAgICAgICAgIG1ldGhvZCA9ICJzcXVhcmUiLA0KICAgICAgICAgICB0eXBlID0gImxvd2VyIiwNCiAgICAgICAgICAgcC5tYXQgPSBwX21hdCkNCmBgYA0KDQpVc2luZyBjaXJjbGUgdHlwZToNCmBgYHtyfQ0KZ2djb3JycGxvdChjb3JyX21hdCwNCiAgICAgICAgICAgaGMub3JkZXIgPSBUUlVFLA0KICAgICAgICAgICBtZXRob2QgPSAiY2lyY2xlIiwNCiAgICAgICAgICAgdHlwZSA9ICJsb3dlciIsDQogICAgICAgICAgIG91dGxpbmUuY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICBnZ3RoZW1lID0gZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCwNCiAgICAgICAgICAgY29sb3JzID0gYygiIzkxNDEyZCIsICJ3aGl0ZSIsICIjNDU4MzlhIiksDQogICAgICAgICAgIGxhYiA9IFRSVUUNCiAgICAgICAgICAgKQ0KYGBgDQoNCiMjIyBjb3JycGxvdA0KDQpMZWFybiBtb3JlIGF0OiBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY29ycnBsb3QvdmlnbmV0dGVzL2NvcnJwbG90LWludHJvLmh0bWwNCg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJwbG90KQ0KYGBgDQoNClN0b3JlcyBhIGNvcnJlbGF0aW9uIG1hdHJpeDoNCmBgYHtyfQ0KY29ycl9tYXQgPC0gY29yKG10Y2Fyc1ssc2FwcGx5KG10Y2FycywgaXMubnVtZXJpYyldKQ0KYGBgDQoNCmBgYHtyfQ0KY29ycnBsb3QoY29ycl9tYXQpDQpgYGANCg0KTWl4ZWQgY29ycmVsb2dyYW06DQpgYGB7cn0NCmNvcnJwbG90Lm1peGVkKA0KICBjb3JyX21hdCwNCiAgdXBwZXIgPSAic3F1YXJlIiwNCiAgbG93ZXIgPSAibnVtYmVyIiwNCiAgYWRkZ3JpZC5jb2wgPSAiYmxhY2siLCAjIFRoZSBjb2xvciBvZiB0aGUgZ3JpZA0KICB0bC5jb2wgPSAiYmxhY2siICAjIFRoZSBjb2xvciBvZiB0ZXh0IGxhYmVsDQogICkNCmBgYA0KDQpPcmRlcmVkIGNvcnJlbG9ncmFtOg0KYGBge3J9DQpjb3JyX21hdF9vcmQgPC0gY29yck1hdE9yZGVyKGNvcnJfbWF0LCBvcmRlciA9ICdGUEMnKQ0KY29ycnBsb3QubWl4ZWQoDQogIGNvcnJfbWF0W2NvcnJfbWF0X29yZCxjb3JyX21hdF9vcmRdLA0KICB1cHBlciA9ICJzcXVhcmUiLA0KICBsb3dlciA9ICJudW1iZXIiLA0KICBhZGRncmlkLmNvbCA9ICJibGFjayIsICMgVGhlIGNvbG9yIG9mIHRoZSBncmlkDQogIHRsLmNvbCA9ICJibGFjayIgICMgVGhlIGNvbG9yIG9mIHRleHQgbGFiZWwNCiAgKQ0KYGBgDQoNCiMjIyBnZ3N0YXRzcGxvdA0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dzdGF0c3Bsb3QpDQpgYGANCg0KDQpgYGB7cn0NCmdnY29ycm1hdCgNCiAgZGF0YSA9IG10Y2FycywNCiAgY29sb3JzID0gYygiI0IyMTgyQiIsICJ3aGl0ZSIsICIjNEQ0RDREIiksDQogIHRpdGxlID0gIkNvcnJlbGFsb2dyYW0gZm9yIG10Y2FycyIsIA0KICBtYXRyaXgudHlwZSA9ICJsb3dlciIsIA0KICB0eXBlID0gInBhcmFtZXRyaWMiLCBwY2ggPSAiIg0KKQ0KYGBgDQoNClRoaXMgZnVuY3Rpb24gaGFzIGFuIGFyZ3VtZW50IGBzaWcubGV2ZWxgIHRoYXQgZG9lcyBub3Qgd29yayBhcyBpdCBzaG91bGQgYmUuIFNvIEkgaGF2ZSBhZGRlZCBwY2ggPSAiIiBzbyB0aGF0IG5vIGNlbGwgaXMgY3Jvc3NlZCBvdXQuDQoNCiMjIyBwYWlycw0KDQpgYGB7cn0NCnBhaXJzKGlyaXNbLDE6NF0sDQogICAgICBwY2g9MjMsICNTaGFwZSBvZiB0aGUgcG9pbnRzDQogICAgICBjb2w9YXMubnVtZXJpYyhpcmlzJFNwZWNpZXMpKzEsICAgI0NvbG91ciBvZiB0aGUgb3V0bGluZSBvZiB0aGUgcG9pbnRzDQogICAgICBiZz1zY2FsZXM6OmFscGhhKGFzLm51bWVyaWMoaXJpcyRTcGVjaWVzKSsxLCAwLjMpLCAgI0ZpbGwgY29sb3VyIG9mIHRoZSBwb2ludHMgd2l0aCByZWR1Y2VkIGNvbG91ciBpbnRlbnNpdHkNCiAgICAgIGNleD0xLjUsICAjU2l6ZSBvZiB0aGUgcG9pbnRzDQogICAgICB1cHBlci5wYW5lbD1OVUxMLCAgI0RvIG5vdCBkaXNwbGF5IHRoZSB0aGUgdXBwZXIgcGFuZWwNCiAgICAgIGxhYmVscz1nc3ViKCJbWzpwdW5jdDpdXSIsIiAiLGNvbG5hbWVzKGlyaXNbLDE6NF0pKSAjU3Vic3RpdHV0aW9uIHRoZSBmdWxsIHN0b3AgaW4gdGhlIGZlYXR1cmUgbmFtZXMgd2l0aCBhIHNwYWNlDQogICAgICApICANCmBgYA0KDQoNCiMjIyBHR2FsbHkNCg0KU3VpdGFibGUgZm9yIHNtYWxsIG51bWJlciBvZiB2YXJpYWJsZXMgb25seS4NCmBgYHtyfQ0KbGlicmFyeShHR2FsbHkpDQpgYGANCg0KIyMjIyBnZ2NvcnINCg0KYGBge3J9DQpnZ2NvcnIobXRjYXJzLCBsYWJlbCA9IFQsIGhqdXN0ID0gMSwgbGF5b3V0LmV4cCA9IDIsIHNpemUgPSAzLCBsYWJlbF9zaXplID0gMikNCmBgYA0KDQojIyMjIGdncGFpcnMNCg0KYGBge3J9DQpnZ3BhaXJzKG10Y2Fyc1ssMjo2XSwgDQogICAgICAgIHRpdGxlPSJDb3JyZWxvZ3JhbSBvZiBtdGNhcnMgZGF0YXNldCIsDQogICAgICAgIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gInBvaW50cyIsIGNvbWJvID0gImRvdF9ub19mYWNldCIpKSANCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwYWlycyhtdGNhcnNbLDI6Nl0sIA0KICAgICAgICB0aXRsZT0iQ29ycmVsb2dyYW0gb2YgbXRjYXJzIGRhdGFzZXQiLA0KICAgICAgICB1cHBlciA9IGxpc3QoY29udGludW91cyA9IHdyYXAoImNvciIsIHNpemU9MikpKSsNCiAgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIHNpemUgPSA0KSwNCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNSkpDQpgYGANCg0KQ3VzdG9tIGZ1bmN0aW9ucyB0byBiZSB1c2VkIGluIGRpYWdvbmFsLCB1cHBlciBhbmQgbG93ZXIgcG9ydGlvbiBvZiB0aGUgY29ycmVsb2dyYW0uDQpgYGB7cn0NCiMgRnVuY3Rpb24gZm9yIGRpYWdvbmFsDQpteV9iaW4gPC0gZnVuY3Rpb24oZGF0YSwgbWFwcGluZywgYmlud2lkdGgsIC4uLikgew0KICBnZ3Bsb3QoZGF0YSA9IGRhdGEsIG1hcHBpbmcgPSBtYXBwaW5nKSArDQogICAgZ2VvbV9oaXN0b2dyYW0oYWVzKHk9Li5kZW5zaXR5Li4pLCANCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IGJpbndpZHRoLCBmaWxsID0gIiMyOThkZmYiLCBhbHBoYSA9IDAuMikgKw0KICAgIGdlb21fZGVuc2l0eShjb2xvciA9ICJyZWQiKSArIHRoZW1lX2NsYXNzaWMoKQ0KfQ0KDQojIEZ1bmN0aW9uIGZvciB1cHBlciBwb3J0aW9uDQp1cHBlcmZ1biA8LSBmdW5jdGlvbihkYXRhLCBtYXBwaW5nLCBtZXRob2QsIHN5bWJvbCwgLi4uKXsNCiAgeCA8LSBldmFsX2RhdGFfY29sKGRhdGEsIG1hcHBpbmckeCkNCiAgeSA8LSBldmFsX2RhdGFfY29sKGRhdGEsIG1hcHBpbmckeSkNCiAgY29yciA8LSBjb3IoeCwgeSwgbWV0aG9kPW1ldGhvZCwgdXNlPSdjb21wbGV0ZS5vYnMnKQ0KICBnZ2FsbHlfdGV4dCgNCiAgICBsYWJlbCA9IHBhc3RlKHN5bWJvbCwgYXMuY2hhcmFjdGVyKHJvdW5kKGNvcnIsIDIpKSksIA0KICAgIG1hcHBpbmcgPSBhZXMoKSwgeFAgPSAwLjUsIHlQID0gMC41LCBjb2xvciA9ICdibGFjaycsDQogICAgLi4uDQogICkgKyB0aGVtZV9jbGFzc2ljKCkNCn0NCg0KIyBGdW5jdGlvbiBmb3IgbG93ZXIgcG9ydGlvbg0KbG93ZXJmdW4gPC0gZnVuY3Rpb24oZGF0YSwgbWFwcGluZykgew0KICBnZ3Bsb3QoZGF0YSA9IGRhdGEsIG1hcHBpbmcgPSBtYXBwaW5nKSsgDQogICAgZ2VvbV9wb2ludChhbHBoYSA9IDEsIGNvbG9yID0gIiMyOThkZmYiLCBwY2ggPSAyMCkgKyANCiAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IHgsIA0KICAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIHNpemUgPSAwLjUpICsNCiAgICB0aGVtZV9jbGFzc2ljKCkNCn0NCmBgYA0KDQoNCmBgYHtyfQ0KZ2dwYWlycygNCiAgbXRjYXJzWywgMjo2XSwNCiAgDQogIGRpYWcgPSBsaXN0KGNvbnRpbnVvdXMgPSAgd3JhcChteV9iaW4sIGJpbndpZHRoID0gMTApKSwNCiAgdXBwZXIgPSBsaXN0KGNvbnRpbnVvdXMgPSB3cmFwKHVwcGVyZnVuLCBtZXRob2QgPSAncGVhcnNvbicsIHN5bWJvbCA9IGV4cHJlc3Npb24oJ3IgPScpKSksDQogIGxvd2VyID0gbGlzdChjb250aW51b3VzID0gd3JhcChsb3dlcmZ1bikpDQogICkgKw0KICB0aGVtZShwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIGZhY2UgPSAiYm9sZCIpKSsNCiAgbGFicyhjYXB0aW9uPSJDb3JyZWxvZ3JhbSBvZiBtdGNhcnMgZGF0YXNldCIpDQpgYGANCg0KDQoNCiMjIyBQZXJmb3JtYW5jZUFuYWx5dGljcw0KDQpgYGB7cn0NCmxpYnJhcnkoUGVyZm9ybWFuY2VBbmFseXRpY3MpDQpgYGANCg0KYGBge3J9DQpjaGFydC5Db3JyZWxhdGlvbihtdGNhcnNbLHNhcHBseShtdGNhcnMsIGlzLm51bWVyaWMpXSwgDQogICAgICAgICAgICAgICAgICBoaXN0b2dyYW09VFJVRSwgcGNoPTE5KQ0KYGBgDQoNCg0KIyMjIHJzdGF0aXgNCg0KYGBge3J9DQpsaWJyYXJ5KHJzdGF0aXgpDQpgYGANCg0KQ29ycmVsYXRpb24gTWF0cml4Og0KYGBge3J9DQpyc3RhdGl4Ojpjb3JfbWF0KG10Y2Fyc1ssc2FwcGx5KG10Y2FycywgaXMubnVtZXJpYyldKQ0KYGBgDQoNClAtdmFsdWUgbWF0cml4IG9mIGNvcnJlbGF0aW9uczoNCmBgYHtyfQ0KcnN0YXRpeDo6Y29yX3BtYXQobXRjYXJzWyxzYXBwbHkobXRjYXJzLCBpcy5udW1lcmljKV0pDQpgYGANCg0KDQojIyMgZ2dwbG90Mg0KDQpgYGB7cn0NCmNvcnJfbWF0IDwtIHJzdGF0aXg6OmNvcl9tYXQobXRjYXJzWyxzYXBwbHkobXRjYXJzLCBpcy5udW1lcmljKV0pDQpjb3JyX21hdF9kZiA8LSBjb3JyX21hdCAlPiUgDQogIHRpZHlyOjpwaXZvdF9sb25nZXIoY29scyA9IC1yb3duYW1lLCBuYW1lc190byA9ICd2YXJpYWJsZXMnLCB2YWx1ZXNfdG8gPSAncicpDQpoZWFkKGNvcnJfbWF0X2RmKQ0KYGBgDQoNCmBgYHtyfQ0KY29ycl9tYXRfZGYgJT4lIA0KICBnZ3Bsb3QoYWVzKHJvd25hbWUsIHZhcmlhYmxlcywgZmlsbCA9IHIpKSArIA0KICBnZW9tX3RpbGUoKSArDQogIGxhYnMoeCA9ICJ2YXJpYWJsZXMiLCB5ID0gInZhcmlhYmxlcyIpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiYmx1ZSIsIGhpZ2ggPSAicmVkIikgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gciksIHNpemUgPSAzKQ0KYGBgDQoNCiMjIyBoZWF0bWFwDQoNCmBgYHtyfQ0KY29ycl9tYXQgPC0gY29yKG10Y2Fyc1ssc2FwcGx5KG10Y2FycywgaXMubnVtZXJpYyldKQ0KYGBgDQoNCmBgYHtyfQ0KY29sIDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkoMjApDQpoZWF0bWFwKGNvcnJfbWF0LCBjb2wgPSBjb2wsIHN5bW0gPSBUUlVFKQ0KYGBgDQoNCg0KIyMjIGxldmVscGxvdA0KDQpgYGB7cn0NCmxhdHRpY2U6OmxldmVscGxvdChjb3IobXRjYXJzW2MoJ2N5bCcsJ2Rpc3AnLCdocCcsJ2RyYXQnLCd3dCcsJ3FzZWMnLCd2cycpXSksIA0KICAgICAgICAgICAgICAgICAgIHByZXR0eSA9IFRSVUUpDQpgYGANCg0KIyMjIHNqUGxvdA0KDQpgYGB7cn0NCmxpYnJhcnkoc2pQbG90KQ0KdGFiX2NvcnIoaXJpc1ssc2FwcGx5KGlyaXMsIGlzLm51bWVyaWMpXSwgDQogICAgICAgICBzaG93LnAgPSBULCANCiAgICAgICAgIHAubnVtZXJpYyA9IFQsDQogICAgICAgICB0aXRsZSA9ICJUYWJsZSAxOiBDb3JyZWxhdGlvbiBNYXRyaXgiLCANCiAgICAgICAgIHZhci5sYWJlbHMgPSBjKCJTZXBhbCBMZW5ndGgiLCAiU2VwYWwgV2lkdGgiLCAiUGV0YWwgTGVuZ3RoIiwgIlBldGFsIFdpZHRoIiksDQogICAgICAgICB0cmlhbmdsZSA9ICJsb3dlciINCiAgICAgICAgICkNCmBgYA0KDQojIyMgY29ycmVsYXRpb24NCg0KYGBge3J9DQpsaWJyYXJ5KGNvcnJlbGF0aW9uKQ0KY29ycmVsYXRpb24oaXJpc1ssc2FwcGx5KGlyaXMsIGlzLm51bWVyaWMpXSkgfD4ga2FibGVFeHRyYTo6a2FibGUoKQ0KDQpjb3JyZWxhdGlvbihpcmlzWyxzYXBwbHkoaXJpcywgaXMubnVtZXJpYyldKSB8PiANCiAgc3VtbWFyeShyZWR1bmRhbnQgPSBUUlVFLCBkaWdpdHMgPSAzKSB8Pg0KICB2aXN1YWxpc2F0aW9uX3JlY2lwZSgpIHw+DQogIHBsb3QoKQ0KDQpjb3JyZWxhdGlvbihpcmlzWyxzYXBwbHkoaXJpcywgaXMubnVtZXJpYyldKSB8PiANCiAgYXMubWF0cml4KCkgfD4NCiAgY29yX3NvcnQoKSB8Pg0KICB2aXN1YWxpc2F0aW9uX3JlY2lwZSgpIHw+DQogIHBsb3QoKQ0KDQpjb3JyZWxhdGlvbihpcmlzWyxzYXBwbHkoaXJpcywgaXMubnVtZXJpYyldKSB8PiANCiAgdmlzdWFsaXNhdGlvbl9yZWNpcGUoKSB8Pg0KICBwbG90KCkNCmBgYA0KDQoNCg==