Stat: Alpha Diversity Visualisation(phyloseq_alpha_diversity.py v5.1.0)

## Import packages
library(phyloseq)
library(ggplot2)
library(vegan)
source(file.path(params$libdir, "graphical_methods.R"))
## Alternative to source all extra function from a github repo
## source("https://raw.githubusercontent.com/mahendra-mariadassou/phyloseq-extended/master/load-extra-functions.R")

## Setting variables
  ## The Phyloseq object (format rdata)
  # phyloseq <- ""

  ## The experiment variable that you want to analyse
  # varExp <- ""

  ## "The alpha diversity indices to compute. Multiple indice may be indicated by separating them by a comma.
  ## Available indices are : Observed, Chao1, Shannon, InvSimpson, Simpson, ACE, Fisher
  # measures <- ""

## Create input and parameters dataframe
  # params <- data.frame( "phyloseq" = phylose, "measures" = measures, "varExp" = varExp)

## Load data
load(params$phyloseq)

## Convert measures to list
measures <- as.list(strsplit(params$measures, ",")[[1]])

## Compute numeric values of alpha diversity indices
alpha.diversity <- estimate_richness(data, measures = measures)

## Export diversity indices to text file
write.table(alpha.diversity, params$fileAlpha, sep="\t", quote = FALSE, col.names = NA)

Richness plot

p <- plot_richness(data, x = params$varExp, color = params$varExp, measures = measures) + ggtitle(paste("Alpha diversity distribution by", params$varExp))+ theme(plot.title = element_text(hjust = 0.5))
plot(p)

Richness plot with boxplot

p <- p + geom_boxplot(alpha = 0.2) +
         geom_point()+ theme_grey() +
         theme(axis.text.x = element_text(angle=90, hjust=0.5)) +
         theme(plot.title = element_text(hjust = 0.5))
plot(p)

Alpha Diversity Indice Anova Analysis

anova_data <- cbind(sample_data(data), alpha.diversity)
anova_data$Depth <- sample_sums(data)

variables <- paste(sep=" + ", "Depth", params$varExp )

## Perform ANOVA on observed richness, which effects are significant
for (m in measures){
    f <- paste(m," ~ ", variables)
    cat(sep = "", "###############################################################\n#Perform ANOVA on ",m,", which effects are significant\nanova.",m," <-aov( ",f,", anova_data)\nsummary(anova.",m,")\n")
    anova_res <- aov( as.formula(f), anova_data)
    res <- summary(anova_res)
    print(res)
    cat("\n\n")
}
###############################################################
#Perform ANOVA on Observed, which effects are significant
anova.Observed <-aov( Observed  ~  Depth + EnvType, anova_data)
summary(anova.Observed)
            Df Sum Sq Mean Sq F value   Pr(>F)    
Depth        1  97970   97970 203.414  < 2e-16 ***
EnvType      7  18148    2593   5.383 9.78e-05 ***
Residuals   55  26490     482                     
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


###############################################################
#Perform ANOVA on Chao1, which effects are significant
anova.Chao1 <-aov( Chao1  ~  Depth + EnvType, anova_data)
summary(anova.Chao1)
            Df Sum Sq Mean Sq F value   Pr(>F)    
Depth        1  79015   79015 100.129 5.45e-14 ***
EnvType      7  13764    1966   2.492   0.0269 *  
Residuals   55  43403     789                     
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1


###############################################################
#Perform ANOVA on Shannon, which effects are significant
anova.Shannon <-aov( Shannon  ~  Depth + EnvType, anova_data)
summary(anova.Shannon)
            Df Sum Sq Mean Sq F value   Pr(>F)    
Depth        1  7.699   7.699  16.895 0.000133 ***
EnvType      7 11.333   1.619   3.553 0.003209 ** 
Residuals   55 25.064   0.456                     
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Rarefaction curves

##code of Mahendra Mariadassou, INRA

## Import additionnal packages
# library(parallel)

## Rarefaction curve, ggplot style (additionnal phyloseq-extend function, not yet released)
ggrare <- function(physeq, step = 10, label = NULL, color = NULL, plot = TRUE, parallel = FALSE, se = TRUE) {
    ## Args:
    ## - physeq: phyloseq class object, from which abundance data are extracted
    ## - step: Step size for sample size in rarefaction curves
    ## - label: Default `NULL`. Character string. The name of the variable
    ##          to map to text labels on the plot. Similar to color option
    ##          but for plotting text.
    ## - color: (Optional). Default ‘NULL’. Character string. The name of the
    ##          variable to map to colors in the plot. This can be a sample
    ##          variable (among the set returned by
    ##          ‘sample_variables(physeq)’ ) or taxonomic rank (among the set
    ##          returned by ‘rank_names(physeq)’).
    ##
    ##          Finally, The color scheme is chosen automatically by
    ##          ‘link{ggplot}’, but it can be modified afterward with an
    ##          additional layer using ‘scale_color_manual’.
    ## - color: Default `NULL`. Character string. The name of the variable
    ##          to map to text labels on the plot. Similar to color option
    ##          but for plotting text.
    ## - plot:  Logical, should the graphic be plotted.
    ## - parallel: should rarefaction be parallelized (using parallel framework)
    ## - se:    Default TRUE. Logical. Should standard errors be computed. 
    ## require vegan
    x <- as(otu_table(physeq), "matrix")
    if (taxa_are_rows(physeq)) { x <- t(x) }

    ## This script is adapted from vegan `rarecurve` function
    tot <- rowSums(x)
    S   <- rowSums(x > 0)
    nr  <- nrow(x)

    rarefun <- function(i) {
        # cat(paste("rarefying sample", rownames(x)[i]), sep = "\n")
        n <- seq(1, tot[i], by = step)
        if (n[length(n)] != tot[i]) {
            n <- c(n, tot[i])
        }
        y <- rarefy(x[i, ,drop = FALSE], n, se = se)
        if (nrow(y) != 1) {
        rownames(y) <- c(".S", ".se")
            return(data.frame(t(y), Size = n, Sample = rownames(x)[i]))
        } else {
            return(data.frame(.S = y[1, ], Size = n, Sample = rownames(x)[i]))
        }
    }
    if (parallel) {
        out <- mclapply(seq_len(nr), rarefun, mc.preschedule = FALSE)
    } else {
        out <- lapply(seq_len(nr), rarefun)
    }
    df <- do.call(rbind, out)
    
    ## Get sample data 
    if (!is.null(sample_data(physeq, FALSE))) {
        sdf <- as(sample_data(physeq), "data.frame")
        sdf$Sample <- rownames(sdf)
        data <- merge(df, sdf, by = "Sample")
        labels <- data.frame(x = tot, y = S, Sample = rownames(x))
        labels <- merge(labels, sdf, by = "Sample")
    }
    
    ## Add, any custom-supplied plot-mapped variables
    if( length(color) > 1 ){
        data$color <- color
        names(data)[names(data)=="color"] <- deparse(substitute(color))
        color <- deparse(substitute(color))
    }
    if( length(label) > 1 ){
        labels$label <- label
        names(labels)[names(labels)=="label"] <- deparse(substitute(label))
        label <- deparse(substitute(label))
    }
    
    p <- ggplot(data = data, aes_string(x = "Size", y = ".S", group = "Sample", color = color))
    p <- p + labs(x = "Sample Size", y = "ASV Richness")
    if (!is.null(label)) {
        p <- p + geom_text(data = labels, aes_string(x = "x", y = "y", label = label, color = color),
                           size = 4, hjust = 0)
    }
    p <- p + geom_line()
    if (se) { ## add standard error if available
        p <- p + geom_ribbon(aes_string(ymin = ".S - .se", ymax = ".S + .se", color = NULL, fill = color), alpha = 0.2)
    }
    if (plot) {
        plot(p)
    }
    invisible(p)
}

rare.level <- sample_sums(data)[[1]]
facet <- paste('facet_wrap(~',params$varExp,')')

p <- ggrare(data, step = 500, color = params$varExp, plot = FALSE) + 
        geom_vline(xintercept = rare.level, color = "gray60") + eval(parse(text = facet))
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 6
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 10
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 8
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 6
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 16
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 6
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 7
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 9
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 6
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 5
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 7
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 6
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 4
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 2
Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3

Warning in rarefy(x[i, , drop = FALSE], n, se = se): most observed count data
have counts 1, but smallest count is 3
Warning: `aes_string()` was deprecated in ggplot2 3.0.0.
ℹ Please use tidy evaluation idioms with `aes()`.
ℹ See also `vignette("ggplot2-in-packages")` for more information.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
generated.
plot(p)

Reproducibility token

sessioninfo::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.1.2 (2021-11-01)
 os       Ubuntu 24.04.2 LTS
 system   x86_64, linux-gnu
 ui       X11
 language fr_FR:en
 collate  en_US.utf8
 ctype    en_US.utf8
 tz       Europe/Paris
 date     2026-01-14
 pandoc   2.19.2 @ /home/maria/miniforge3/envs/frogs@5.1.0/bin/ (via rmarkdown)

─ Packages ───────────────────────────────────────────────────────────────────
 package          * version   date (UTC) lib source
 ade4               1.7-22    2023-02-06 [1] CRAN (R 4.1.3)
 ape                5.7-1     2023-03-13 [1] CRAN (R 4.1.3)
 Biobase            2.54.0    2021-10-26 [1] Bioconductor
 BiocGenerics       0.40.0    2021-10-26 [1] Bioconductor
 biomformat         1.22.0    2021-10-26 [1] Bioconductor
 Biostrings         2.62.0    2021-10-26 [1] Bioconductor
 bitops             1.0-7     2021-04-24 [1] CRAN (R 4.1.3)
 bslib              0.5.0     2023-06-09 [1] CRAN (R 4.1.3)
 cachem             1.0.8     2023-05-01 [1] CRAN (R 4.1.3)
 cli                3.6.1     2023-03-23 [1] CRAN (R 4.1.3)
 cluster            2.1.4     2022-08-22 [1] CRAN (R 4.1.3)
 codetools          0.2-19    2023-02-01 [1] CRAN (R 4.1.3)
 colorspace         2.1-0     2023-01-23 [1] CRAN (R 4.1.3)
 crayon             1.5.2     2022-09-29 [1] CRAN (R 4.1.3)
 data.table         1.14.8    2023-02-17 [1] CRAN (R 4.1.3)
 digest             0.6.31    2022-12-11 [1] CRAN (R 4.1.3)
 dplyr              1.1.2     2023-04-20 [1] CRAN (R 4.1.3)
 evaluate           0.21      2023-05-05 [1] CRAN (R 4.1.3)
 fansi              1.0.4     2023-01-22 [1] CRAN (R 4.1.3)
 farver             2.1.1     2022-07-06 [1] CRAN (R 4.1.3)
 fastmap            1.1.1     2023-02-24 [1] CRAN (R 4.1.3)
 foreach            1.5.2     2022-02-02 [1] CRAN (R 4.1.3)
 generics           0.1.3     2022-07-05 [1] CRAN (R 4.1.3)
 GenomeInfoDb       1.30.1    2022-01-30 [1] Bioconductor
 GenomeInfoDbData   1.2.7     2026-01-14 [1] Bioconductor
 ggplot2          * 3.4.2     2023-04-03 [1] CRAN (R 4.1.3)
 glue               1.6.2     2022-02-24 [1] CRAN (R 4.1.3)
 gtable             0.3.3     2023-03-21 [1] CRAN (R 4.1.3)
 highr              0.10      2022-12-22 [1] CRAN (R 4.1.3)
 htmltools          0.5.5     2023-03-23 [1] CRAN (R 4.1.3)
 igraph             1.3.5     2022-09-22 [1] CRAN (R 4.1.3)
 IRanges            2.28.0    2021-10-26 [1] Bioconductor
 iterators          1.0.14    2022-02-05 [1] CRAN (R 4.1.3)
 jquerylib          0.1.4     2021-04-26 [1] CRAN (R 4.1.3)
 jsonlite           1.8.5     2023-06-05 [1] CRAN (R 4.1.3)
 knitr              1.43      2023-05-25 [1] CRAN (R 4.1.3)
 labeling           0.4.2     2020-10-20 [1] CRAN (R 4.1.3)
 lattice          * 0.21-8    2023-04-05 [1] CRAN (R 4.1.3)
 lifecycle          1.0.3     2022-10-07 [1] CRAN (R 4.1.3)
 magrittr           2.0.3     2022-03-30 [1] CRAN (R 4.1.3)
 MASS               7.3-58.3  2023-03-07 [1] CRAN (R 4.1.3)
 Matrix             1.5-4.1   2023-05-18 [1] CRAN (R 4.1.3)
 mgcv               1.8-42    2023-03-02 [1] CRAN (R 4.1.3)
 multtest           2.50.0    2021-10-26 [1] Bioconductor
 munsell            0.5.0     2018-06-12 [1] CRAN (R 4.1.3)
 nlme               3.1-162   2023-01-31 [1] CRAN (R 4.1.3)
 permute          * 0.9-7     2022-01-27 [1] CRAN (R 4.1.3)
 phyloseq         * 1.38.0    2021-10-26 [1] Bioconductor
 pillar             1.9.0     2023-03-22 [1] CRAN (R 4.1.3)
 pkgconfig          2.0.3     2019-09-22 [1] CRAN (R 4.1.3)
 plyr               1.8.8     2022-11-11 [1] CRAN (R 4.1.3)
 R6                 2.5.1     2021-08-19 [1] CRAN (R 4.1.3)
 Rcpp               1.0.10    2023-01-22 [1] CRAN (R 4.1.3)
 RCurl              1.98-1.12 2023-03-27 [1] CRAN (R 4.1.3)
 reshape2         * 1.4.4     2020-04-09 [1] CRAN (R 4.1.3)
 rhdf5              2.38.1    2022-03-10 [1] Bioconductor
 rhdf5filters       1.6.0     2021-10-26 [1] Bioconductor
 Rhdf5lib           1.16.0    2021-10-26 [1] Bioconductor
 rlang              1.1.1     2023-04-28 [1] CRAN (R 4.1.3)
 rmarkdown          2.22      2023-06-01 [1] CRAN (R 4.1.3)
 rstudioapi         0.14      2022-08-22 [1] CRAN (R 4.1.3)
 S4Vectors          0.32.4    2022-03-24 [1] Bioconductor
 sass               0.4.6     2023-05-03 [1] CRAN (R 4.1.3)
 scales           * 1.2.1     2022-08-20 [1] CRAN (R 4.1.3)
 sessioninfo        1.2.2     2021-12-06 [1] CRAN (R 4.1.3)
 stringi            1.7.6     2021-11-29 [1] CRAN (R 4.1.1)
 stringr            1.5.0     2022-12-02 [1] CRAN (R 4.1.3)
 survival           3.5-5     2023-03-12 [1] CRAN (R 4.1.3)
 tibble             3.2.1     2023-03-20 [1] CRAN (R 4.1.3)
 tidyselect         1.2.0     2022-10-10 [1] CRAN (R 4.1.3)
 utf8               1.2.3     2023-01-31 [1] CRAN (R 4.1.3)
 vctrs              0.6.2     2023-04-19 [1] CRAN (R 4.1.3)
 vegan            * 2.6-4     2022-10-11 [1] CRAN (R 4.1.3)
 withr              2.5.0     2022-03-03 [1] CRAN (R 4.1.3)
 xfun               0.39      2023-04-20 [1] CRAN (R 4.1.3)
 XVector            0.34.0    2021-10-26 [1] Bioconductor
 yaml               2.3.7     2023-01-23 [1] CRAN (R 4.1.3)
 zlibbioc           1.40.0    2021-10-26 [1] Bioconductor

 [1] /home/maria/miniforge3/envs/frogs@5.1.0/lib/R/library

──────────────────────────────────────────────────────────────────────────────
LS0tCmNvcHlyaWdodDogIkNvcHlyaWdodCAoQykgMjAyNSBJTlJBRSIKbGljZW5zZTogIkdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKcGFyYW1zOgogICBwaHlsb3NlcToKICAgICAgdmFsdWU6IHgKICAgbWVhc3VyZXM6CiAgICAgIHZhbHVlOiB4CiAgIHZhckV4cDoKICAgICAgdmFsdWU6IHgKICAgZmlsZUFscGhhOgogICAgICB2YWx1ZTogeAogICBsaWJkaXI6CiAgICAgIHZhbHVlOiB4CiAgIHZlcnNpb246CiAgICAgIHZhbHVlOiB4CiAgIHRvb2w6CiAgICB2YWx1ZTogeAotLS0KCjxtZXRhIGh0dHAtZXF1aXY9IkNvbnRlbnQtVHlwZSIgY29udGVudD0idGV4dC9odG1sOyBjaGFyc2V0PVVURi04IiAvPgo8bWV0YSBuYW1lPSJhdXRob3IiIGNvbnRlbnQ9IlRhIFRoaSBOZ2FuIC0gU0lHRU5BRS9HQUJJICYgTWFyaWEgQmVybmFyZCAtIFNJR0VOQUUvR0FCSSIgLz4KPG1ldGEgbmFtZT0idmVyc2lvbiIgY29udGVudD0iNS4xLjAiIC8+CjxtZXRhIG5hbWU9ImNvcHlyaWdodCIgY29udGVudD0iQ29weXJpZ2h0IChDKSAyMDI1IElOUkFFIiAvPgoKYGBge3IsIGVjaG89RkFMU0UsIHJlc3VsdHM9ImFzaXMifQpDVVJSRU5UX0RJUiA8LSBrbml0cjo6b3B0c19rbml0JGdldCgib3V0cHV0LmRpciIpCmlmIChpcy5udWxsKENVUlJFTlRfRElSKSkgewogIENVUlJFTlRfRElSIDwtIGdldHdkKCkKfQpUSEVNRV9ESVIgPC0gbm9ybWFsaXplUGF0aCgKICBmaWxlLnBhdGgoZGlybmFtZShDVVJSRU5UX0RJUiksICJzdGF0aWMiKSwKICB3aW5zbGFzaCA9ICIvIiwKICBtdXN0V29yayA9IEZBTFNFCikKCmpzX3RoZW1lX2ZpbGUgPC0gbm9ybWFsaXplUGF0aCgKICBmaWxlLnBhdGgoVEhFTUVfRElSLCAianMiLCAidGhlbWUuanMiKSwKICB3aW5zbGFzaCA9ICIvIiwKICBtdXN0V29yayA9IEZBTFNFCikKCmxpbmVzIDwtIHJlYWRMaW5lcyhqc190aGVtZV9maWxlLCB3YXJuID0gRkFMU0UpCgpzZXAgPC0gd2hpY2goZ3JlcGwoIl4vLyMjIiwgdHJpbXdzKGxpbmVzKSkpCgppZiAobGVuZ3RoKHNlcCkgPCAyKSB7CiAgc3RvcCgiVGFnIG5vdCBmb3VuZCBpbiB0aGVtZS5qczogJy8vIyMnIikKfQoKanNfY29kZSA8LSBsaW5lc1soc2VwWzFdICsgMSk6KHNlcFsyXSAtIDEpXQoKY2F0KCI8c2NyaXB0PlxuIikKY2F0KGpzX2NvZGUsIHNlcCA9ICJcbiIpCmNhdCgiXG48L3NjcmlwdD4iKQpgYGAKCjxkaXYgc3R5bGU9ImRpc3BsYXk6ZmxleDsgYWxpZ24taXRlbXM6Y2VudGVyOyBnYXA6MTVweDsgbWFyZ2luLWJvdHRvbToyMHB4OyI+CiAgPGltZyBpZD0ibG9nbyIgc3JjPSJkYXRhOmltYWdlL3BuZztiYXNlNjQsIiBhbHQ9IkZST0dTIGxvZ28iIHN0eWxlPSJoZWlnaHQ6MTgwcHg7Ij4KICA8aDEgc3R5bGU9Im1hcmdpbjowOyI+U3RhdDogQWxwaGEgRGl2ZXJzaXR5IFZpc3VhbGlzYXRpb248aT48c3BhbiBjbGFzcz0idGV4dC1hY2NlbnQiPihgciBwYXJhbXMkdG9vbGAgdmByIHBhcmFtcyR2ZXJzaW9uYCk8L3NwYW4+PC9pPjwvaDE+CjwvZGl2PgoKPHNjcmlwdD4KCiQoZnVuY3Rpb24gKCkgewogIHVwZGF0ZV9sb2dvKENVUlJFTlRfVEhFTUUpOwp9KTsKCgo8L3NjcmlwdD4KCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgo6cm9vdCB7CgkJCS0tZnJvZ3NDb2xvcjogIzAzQTVBODsgCgkJCS0tZnJvZ3NDb2xvckhvdmVyOiBoc2woZnJvbSB2YXIoLS1mcm9nc0NvbG9yKSBoIGNhbGMocyArIDQpIGNhbGMobCAtIDMpIC8gMSk7CgkJCS0tZnJvZ3NQcmVDb2xvcjogaHNsKGZyb20gdmFyKC0tZnJvZ3NDb2xvcikgaCBzIGwgLyAwLjEpOwoJCX0KLnRleHQtYWNjZW50IHsKICBjb2xvcjogIzU5NWM1ZjsKfQoubWFpbi1jb250YWluZXJ7CgltYXgtd2lkdGg6IDc1JSAhaW1wb3J0YW50Owp9CmgxewoJY29sb3I6IHZhcigtLWZyb2dzQ29sb3IpOwp9CmgyewoJY29sb3I6IHZhcigtLWZyb2dzQ29sb3IpOwp9CmEgewoJY29sb3I6IHZhcigtLWZyb2dzQ29sb3IpOwp9CmE6aG92ZXJ7Cgljb2xvcjogdmFyKC0tZnJvZ3NDb2xvckhvdmVyKTsKfQoubmF2LXBpbGxzID4gbGkuYWN0aXZlID4gYSwgLm5hdi1waWxscyA+IGxpLmFjdGl2ZSA+IGE6Zm9jdXN7Cgljb2xvcjogI2ZmZjsKCWJhY2tncm91bmQtY29sb3I6IHZhcigtLWZyb2dzQ29sb3IpOwoJYm9yZGVyLWNvbG9yOiAjZGVlMmU2ICNkZWUyZTYgI2ZmZjsKfQoubmF2LXBpbGxzID4gbGkuYWN0aXZlID4gYTpob3ZlciB7CgliYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1mcm9nc0NvbG9ySG92ZXIpOwp9Ci5idXR0b24gewogICAgYmFja2dyb3VuZC1jb2xvcjogdmFyKC0tZnJvZ3NDb2xvcikgOwogICAgYm9yZGVyICAgICAgICAgIDogbm9uZTsKICAgIGNvbG9yICAgICAgICAgICA6IHdoaXRlOwogICAgcGFkZGluZyAgICAgICAgIDogNXB4IDEwcHg7CiAgICB0ZXh0LWFsaWduICAgICAgOiBjZW50ZXI7CiAgICB0ZXh0LWRlY29yYXRpb24gOiBub25lOwogICAgZGlzcGxheSAgICAgICAgIDogaW5saW5lLWJsb2NrOwogICAgZm9udC1zaXplICAgICAgIDogMTJweDsKICAgIG1hcmdpbiAgICAgICAgICA6IDRweCAycHg7CiAgICBjdXJzb3IgICAgICAgICAgOiBwb2ludGVyOwogICAgYm9yZGVyLXJhZGl1cyAgIDogOHB4Owp9Cmg0IHsgCiAgICBkaXNwbGF5ICAgICAgOiBibG9jazsKICAgIGZvbnQtc2l6ZSAgICA6IDFlbTsKICAgIG1hcmdpbi10b3AgICA6IDEuMzNlbTsKICAgIG1hcmdpbi1ib3R0b206IDEuMzNlbTsKICAgIG1hcmdpbi1sZWZ0ICA6IDA7CiAgICBtYXJnaW4tcmlnaHQgOiAwOwogICAgZm9udC13ZWlnaHQgIDogYm9sZDsKICAgIGNvbG9yICAgICAgICA6IHZhcigtLWZyb2dzQ29sb3IpOwp9CmNvZGUucnsgLyogQ29kZSBibG9jayAqLwogIGZvbnQtc2l6ZTogMTFweDsKfQpwcmV7CiAgZm9udC1zaXplOiAxMXB4IDsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2YXIoLS1mcm9nc1ByZUNvbG9yKSAhaW1wb3J0YW50Owp9Ci5mcm9nc3ZlcnNpb257Cglwb3NpdGlvbjogYWJzb2x1dGU7CglyaWdodDogNSU7Cgljb2xvcjogdmFyKC0tZnJvZ3NDb2xvcik7Cglmb250LXN0eWxlOiBpdGFsaWM7Cglmb250LXNpemU6IHNtYWxsZXI7CglwYWRkaW5nOjhweDsKfQouZnJvZ3N2ZXJzaW9uID4gYXsKCWJvcmRlcjogbm9uZSAhaW1wb3J0YW50OwoJZGlzcGxheTogaW5saW5lLWJsb2NrOwp9CiN0aGVtZWNob2ljZXsKICBib3JkZXItcmFkaXVzOiAwLjI1ZW07CiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGU7CiAgYm9yZGVyOiAxcHggc29saWQgI2RlZTJlNjsKICBmb250LXdlaWdodDogNDAwOwogIGxpbmUtaGVpZ2h0OiAxLjU7CiAgcGFkZGluZzogLjM3NXJlbSAyLjI1cmVtIC4zNzVyZW0gLjc1cmVtOwp9Cjwvc3R5bGU+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBlcnJvcj0gVFJVRSkKYGBgCgoKYGBge3IgaW1wb3J0LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyBJbXBvcnQgcGFja2FnZXMKbGlicmFyeShwaHlsb3NlcSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHZlZ2FuKQpzb3VyY2UoZmlsZS5wYXRoKHBhcmFtcyRsaWJkaXIsICJncmFwaGljYWxfbWV0aG9kcy5SIikpCiMjIEFsdGVybmF0aXZlIHRvIHNvdXJjZSBhbGwgZXh0cmEgZnVuY3Rpb24gZnJvbSBhIGdpdGh1YiByZXBvCiMjIHNvdXJjZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL21haGVuZHJhLW1hcmlhZGFzc291L3BoeWxvc2VxLWV4dGVuZGVkL21hc3Rlci9sb2FkLWV4dHJhLWZ1bmN0aW9ucy5SIikKCiMjIFNldHRpbmcgdmFyaWFibGVzCiAgIyMgVGhlIFBoeWxvc2VxIG9iamVjdCAoZm9ybWF0IHJkYXRhKQogICMgcGh5bG9zZXEgPC0gIiIKCiAgIyMgVGhlIGV4cGVyaW1lbnQgdmFyaWFibGUgdGhhdCB5b3Ugd2FudCB0byBhbmFseXNlCiAgIyB2YXJFeHAgPC0gIiIKCiAgIyMgIlRoZSBhbHBoYSBkaXZlcnNpdHkgaW5kaWNlcyB0byBjb21wdXRlLiBNdWx0aXBsZSBpbmRpY2UgbWF5IGJlIGluZGljYXRlZCBieSBzZXBhcmF0aW5nIHRoZW0gYnkgYSBjb21tYS4KICAjIyBBdmFpbGFibGUgaW5kaWNlcyBhcmUgOiBPYnNlcnZlZCwgQ2hhbzEsIFNoYW5ub24sIEludlNpbXBzb24sIFNpbXBzb24sIEFDRSwgRmlzaGVyCiAgIyBtZWFzdXJlcyA8LSAiIgoKIyMgQ3JlYXRlIGlucHV0IGFuZCBwYXJhbWV0ZXJzIGRhdGFmcmFtZQogICMgcGFyYW1zIDwtIGRhdGEuZnJhbWUoICJwaHlsb3NlcSIgPSBwaHlsb3NlLCAibWVhc3VyZXMiID0gbWVhc3VyZXMsICJ2YXJFeHAiID0gdmFyRXhwKQoKIyMgTG9hZCBkYXRhCmxvYWQocGFyYW1zJHBoeWxvc2VxKQoKIyMgQ29udmVydCBtZWFzdXJlcyB0byBsaXN0Cm1lYXN1cmVzIDwtIGFzLmxpc3Qoc3Ryc3BsaXQocGFyYW1zJG1lYXN1cmVzLCAiLCIpW1sxXV0pCgojIyBDb21wdXRlIG51bWVyaWMgdmFsdWVzIG9mIGFscGhhIGRpdmVyc2l0eSBpbmRpY2VzCmFscGhhLmRpdmVyc2l0eSA8LSBlc3RpbWF0ZV9yaWNobmVzcyhkYXRhLCBtZWFzdXJlcyA9IG1lYXN1cmVzKQoKIyMgRXhwb3J0IGRpdmVyc2l0eSBpbmRpY2VzIHRvIHRleHQgZmlsZQp3cml0ZS50YWJsZShhbHBoYS5kaXZlcnNpdHksIHBhcmFtcyRmaWxlQWxwaGEsIHNlcD0iXHQiLCBxdW90ZSA9IEZBTFNFLCBjb2wubmFtZXMgPSBOQSkKYGBgCgojIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQoKYGBgez1odG1sfQo8ZGl2IGNsYXNzPSJyb3ciPgogIDxkaXYgc3R5bGU9ImZsb2F0OnJpZ2h0Ij4KICA8c2VsZWN0IGlkPSJ0aGVtZWNob2ljZSIKICAgICAgICAgIGNsYXNzPSJmb3JtLXNlbGVjdCBmb3JtLXNlbGVjdC1zbSIKICAgICAgICAgIHN0eWxlPSJ3aWR0aDogYXV0bzsiCiAgICAgICAgICBvbmNoYW5nZT0idXBkYXRlX3RoZW1lX1JtZCh0aGlzLnZhbHVlKSIKICAgICAgICAgIGFyaWEtbGFiZWw9IlN3aXRjaCB0aGVtZSI+CiAgICA8b3B0aW9uIHNlbGVjdGVkIGRpc2FibGVkIHZhbHVlPSIiPlN3aXRjaCB0aGVtZTwvb3B0aW9uPgogICAgPG9wdGlvbiBkaXNhYmxlZCB2YWx1ZT0iRGVmYXVsdFRoZW1lIj5EZWZhdWx0PC9vcHRpb24+CiAgICA8b3B0aW9uIHZhbHVlPSJDb3JhbFRoZW1lIj5Db3JhbDwvb3B0aW9uPgogICAgPG9wdGlvbiB2YWx1ZT0iR29sZFRoZW1lIj5Hb2xkPC9vcHRpb24+CiAgICA8b3B0aW9uIHZhbHVlPSJTdGVlbFRoZW1lIj5TdGVlbDwvb3B0aW9uPgogIDwvc2VsZWN0PgogIDwvZGl2Pgo8L2Rpdj4KYGBgCgojIyBSaWNobmVzcyBwbG90CmBgYHtyIHJpY2huZXNzLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgd2FybmluZz1GQUxTRX0KcCA8LSBwbG90X3JpY2huZXNzKGRhdGEsIHggPSBwYXJhbXMkdmFyRXhwLCBjb2xvciA9IHBhcmFtcyR2YXJFeHAsIG1lYXN1cmVzID0gbWVhc3VyZXMpICsgZ2d0aXRsZShwYXN0ZSgiQWxwaGEgZGl2ZXJzaXR5IGRpc3RyaWJ1dGlvbiBieSIsIHBhcmFtcyR2YXJFeHApKSsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCnBsb3QocCkKYGBgCgojIyBSaWNobmVzcyBwbG90IHdpdGggYm94cGxvdApgYGB7ciByaWNobmVzc0JveHBsb3QsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpwIDwtIHAgKyBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjIpICsKICAgICAgICAgZ2VvbV9wb2ludCgpKyB0aGVtZV9ncmV5KCkgKwogICAgICAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MC41KSkgKwogICAgICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKcGxvdChwKQpgYGAKCiMjIEFscGhhIERpdmVyc2l0eSBJbmRpY2UgQW5vdmEgQW5hbHlzaXMKYGBge3IgYW5vdmF9CmFub3ZhX2RhdGEgPC0gY2JpbmQoc2FtcGxlX2RhdGEoZGF0YSksIGFscGhhLmRpdmVyc2l0eSkKYW5vdmFfZGF0YSREZXB0aCA8LSBzYW1wbGVfc3VtcyhkYXRhKQoKdmFyaWFibGVzIDwtIHBhc3RlKHNlcD0iICsgIiwgIkRlcHRoIiwgcGFyYW1zJHZhckV4cCApCgojIyBQZXJmb3JtIEFOT1ZBIG9uIG9ic2VydmVkIHJpY2huZXNzLCB3aGljaCBlZmZlY3RzIGFyZSBzaWduaWZpY2FudApmb3IgKG0gaW4gbWVhc3VyZXMpewogICAgZiA8LSBwYXN0ZShtLCIgfiAiLCB2YXJpYWJsZXMpCiAgICBjYXQoc2VwID0gIiIsICIjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyNcbiNQZXJmb3JtIEFOT1ZBIG9uICIsbSwiLCB3aGljaCBlZmZlY3RzIGFyZSBzaWduaWZpY2FudFxuYW5vdmEuIixtLCIgPC1hb3YoICIsZiwiLCBhbm92YV9kYXRhKVxuc3VtbWFyeShhbm92YS4iLG0sIilcbiIpCiAgICBhbm92YV9yZXMgPC0gYW92KCBhcy5mb3JtdWxhKGYpLCBhbm92YV9kYXRhKQogICAgcmVzIDwtIHN1bW1hcnkoYW5vdmFfcmVzKQogICAgcHJpbnQocmVzKQogICAgY2F0KCJcblxuIikKfQpgYGAKCiMjIFJhcmVmYWN0aW9uIGN1cnZlcwpgYGB7ciByYXJlZmFjdGlvbiwgbWVzc2FnZT1GQUxTRX0KIyNjb2RlIG9mIE1haGVuZHJhIE1hcmlhZGFzc291LCBJTlJBCgojIyBJbXBvcnQgYWRkaXRpb25uYWwgcGFja2FnZXMKIyBsaWJyYXJ5KHBhcmFsbGVsKQoKIyMgUmFyZWZhY3Rpb24gY3VydmUsIGdncGxvdCBzdHlsZSAoYWRkaXRpb25uYWwgcGh5bG9zZXEtZXh0ZW5kIGZ1bmN0aW9uLCBub3QgeWV0IHJlbGVhc2VkKQpnZ3JhcmUgPC0gZnVuY3Rpb24ocGh5c2VxLCBzdGVwID0gMTAsIGxhYmVsID0gTlVMTCwgY29sb3IgPSBOVUxMLCBwbG90ID0gVFJVRSwgcGFyYWxsZWwgPSBGQUxTRSwgc2UgPSBUUlVFKSB7CiAgICAjIyBBcmdzOgogICAgIyMgLSBwaHlzZXE6IHBoeWxvc2VxIGNsYXNzIG9iamVjdCwgZnJvbSB3aGljaCBhYnVuZGFuY2UgZGF0YSBhcmUgZXh0cmFjdGVkCiAgICAjIyAtIHN0ZXA6IFN0ZXAgc2l6ZSBmb3Igc2FtcGxlIHNpemUgaW4gcmFyZWZhY3Rpb24gY3VydmVzCiAgICAjIyAtIGxhYmVsOiBEZWZhdWx0IGBOVUxMYC4gQ2hhcmFjdGVyIHN0cmluZy4gVGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlCiAgICAjIyAgICAgICAgICB0byBtYXAgdG8gdGV4dCBsYWJlbHMgb24gdGhlIHBsb3QuIFNpbWlsYXIgdG8gY29sb3Igb3B0aW9uCiAgICAjIyAgICAgICAgICBidXQgZm9yIHBsb3R0aW5nIHRleHQuCiAgICAjIyAtIGNvbG9yOiAoT3B0aW9uYWwpLiBEZWZhdWx0IOKAmE5VTEzigJkuIENoYXJhY3RlciBzdHJpbmcuIFRoZSBuYW1lIG9mIHRoZQogICAgIyMgICAgICAgICAgdmFyaWFibGUgdG8gbWFwIHRvIGNvbG9ycyBpbiB0aGUgcGxvdC4gVGhpcyBjYW4gYmUgYSBzYW1wbGUKICAgICMjICAgICAgICAgIHZhcmlhYmxlIChhbW9uZyB0aGUgc2V0IHJldHVybmVkIGJ5CiAgICAjIyAgICAgICAgICDigJhzYW1wbGVfdmFyaWFibGVzKHBoeXNlcSnigJkgKSBvciB0YXhvbm9taWMgcmFuayAoYW1vbmcgdGhlIHNldAogICAgIyMgICAgICAgICAgcmV0dXJuZWQgYnkg4oCYcmFua19uYW1lcyhwaHlzZXEp4oCZKS4KICAgICMjCiAgICAjIyAgICAgICAgICBGaW5hbGx5LCBUaGUgY29sb3Igc2NoZW1lIGlzIGNob3NlbiBhdXRvbWF0aWNhbGx5IGJ5CiAgICAjIyAgICAgICAgICDigJhsaW5re2dncGxvdH3igJksIGJ1dCBpdCBjYW4gYmUgbW9kaWZpZWQgYWZ0ZXJ3YXJkIHdpdGggYW4KICAgICMjICAgICAgICAgIGFkZGl0aW9uYWwgbGF5ZXIgdXNpbmcg4oCYc2NhbGVfY29sb3JfbWFudWFs4oCZLgogICAgIyMgLSBjb2xvcjogRGVmYXVsdCBgTlVMTGAuIENoYXJhY3RlciBzdHJpbmcuIFRoZSBuYW1lIG9mIHRoZSB2YXJpYWJsZQogICAgIyMgICAgICAgICAgdG8gbWFwIHRvIHRleHQgbGFiZWxzIG9uIHRoZSBwbG90LiBTaW1pbGFyIHRvIGNvbG9yIG9wdGlvbgogICAgIyMgICAgICAgICAgYnV0IGZvciBwbG90dGluZyB0ZXh0LgogICAgIyMgLSBwbG90OiAgTG9naWNhbCwgc2hvdWxkIHRoZSBncmFwaGljIGJlIHBsb3R0ZWQuCiAgICAjIyAtIHBhcmFsbGVsOiBzaG91bGQgcmFyZWZhY3Rpb24gYmUgcGFyYWxsZWxpemVkICh1c2luZyBwYXJhbGxlbCBmcmFtZXdvcmspCiAgICAjIyAtIHNlOiAgICBEZWZhdWx0IFRSVUUuIExvZ2ljYWwuIFNob3VsZCBzdGFuZGFyZCBlcnJvcnMgYmUgY29tcHV0ZWQuIAogICAgIyMgcmVxdWlyZSB2ZWdhbgogICAgeCA8LSBhcyhvdHVfdGFibGUocGh5c2VxKSwgIm1hdHJpeCIpCiAgICBpZiAodGF4YV9hcmVfcm93cyhwaHlzZXEpKSB7IHggPC0gdCh4KSB9CgogICAgIyMgVGhpcyBzY3JpcHQgaXMgYWRhcHRlZCBmcm9tIHZlZ2FuIGByYXJlY3VydmVgIGZ1bmN0aW9uCiAgICB0b3QgPC0gcm93U3Vtcyh4KQogICAgUyAgIDwtIHJvd1N1bXMoeCA+IDApCiAgICBuciAgPC0gbnJvdyh4KQoKICAgIHJhcmVmdW4gPC0gZnVuY3Rpb24oaSkgewogICAgICAgICMgY2F0KHBhc3RlKCJyYXJlZnlpbmcgc2FtcGxlIiwgcm93bmFtZXMoeClbaV0pLCBzZXAgPSAiXG4iKQogICAgICAgIG4gPC0gc2VxKDEsIHRvdFtpXSwgYnkgPSBzdGVwKQogICAgICAgIGlmIChuW2xlbmd0aChuKV0gIT0gdG90W2ldKSB7CiAgICAgICAgICAgIG4gPC0gYyhuLCB0b3RbaV0pCiAgICAgICAgfQogICAgICAgIHkgPC0gcmFyZWZ5KHhbaSwgLGRyb3AgPSBGQUxTRV0sIG4sIHNlID0gc2UpCiAgICAgICAgaWYgKG5yb3coeSkgIT0gMSkgewoJICAgIHJvd25hbWVzKHkpIDwtIGMoIi5TIiwgIi5zZSIpCiAgICAgICAgICAgIHJldHVybihkYXRhLmZyYW1lKHQoeSksIFNpemUgPSBuLCBTYW1wbGUgPSByb3duYW1lcyh4KVtpXSkpCiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgcmV0dXJuKGRhdGEuZnJhbWUoLlMgPSB5WzEsIF0sIFNpemUgPSBuLCBTYW1wbGUgPSByb3duYW1lcyh4KVtpXSkpCiAgICAgICAgfQogICAgfQogICAgaWYgKHBhcmFsbGVsKSB7CiAgICAgICAgb3V0IDwtIG1jbGFwcGx5KHNlcV9sZW4obnIpLCByYXJlZnVuLCBtYy5wcmVzY2hlZHVsZSA9IEZBTFNFKQogICAgfSBlbHNlIHsKICAgICAgICBvdXQgPC0gbGFwcGx5KHNlcV9sZW4obnIpLCByYXJlZnVuKQogICAgfQogICAgZGYgPC0gZG8uY2FsbChyYmluZCwgb3V0KQogICAgCiAgICAjIyBHZXQgc2FtcGxlIGRhdGEgCiAgICBpZiAoIWlzLm51bGwoc2FtcGxlX2RhdGEocGh5c2VxLCBGQUxTRSkpKSB7CiAgICAgICAgc2RmIDwtIGFzKHNhbXBsZV9kYXRhKHBoeXNlcSksICJkYXRhLmZyYW1lIikKICAgICAgICBzZGYkU2FtcGxlIDwtIHJvd25hbWVzKHNkZikKICAgICAgICBkYXRhIDwtIG1lcmdlKGRmLCBzZGYsIGJ5ID0gIlNhbXBsZSIpCiAgICAgICAgbGFiZWxzIDwtIGRhdGEuZnJhbWUoeCA9IHRvdCwgeSA9IFMsIFNhbXBsZSA9IHJvd25hbWVzKHgpKQogICAgICAgIGxhYmVscyA8LSBtZXJnZShsYWJlbHMsIHNkZiwgYnkgPSAiU2FtcGxlIikKICAgIH0KICAgIAogICAgIyMgQWRkLCBhbnkgY3VzdG9tLXN1cHBsaWVkIHBsb3QtbWFwcGVkIHZhcmlhYmxlcwogICAgaWYoIGxlbmd0aChjb2xvcikgPiAxICl7CiAgICAgICAgZGF0YSRjb2xvciA8LSBjb2xvcgogICAgICAgIG5hbWVzKGRhdGEpW25hbWVzKGRhdGEpPT0iY29sb3IiXSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY29sb3IpKQogICAgICAgIGNvbG9yIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShjb2xvcikpCiAgICB9CiAgICBpZiggbGVuZ3RoKGxhYmVsKSA+IDEgKXsKICAgICAgICBsYWJlbHMkbGFiZWwgPC0gbGFiZWwKICAgICAgICBuYW1lcyhsYWJlbHMpW25hbWVzKGxhYmVscyk9PSJsYWJlbCJdIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShsYWJlbCkpCiAgICAgICAgbGFiZWwgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGxhYmVsKSkKICAgIH0KICAgIAogICAgcCA8LSBnZ3Bsb3QoZGF0YSA9IGRhdGEsIGFlc19zdHJpbmcoeCA9ICJTaXplIiwgeSA9ICIuUyIsIGdyb3VwID0gIlNhbXBsZSIsIGNvbG9yID0gY29sb3IpKQogICAgcCA8LSBwICsgbGFicyh4ID0gIlNhbXBsZSBTaXplIiwgeSA9ICJBU1YgUmljaG5lc3MiKQogICAgaWYgKCFpcy5udWxsKGxhYmVsKSkgewogICAgICAgIHAgPC0gcCArIGdlb21fdGV4dChkYXRhID0gbGFiZWxzLCBhZXNfc3RyaW5nKHggPSAieCIsIHkgPSAieSIsIGxhYmVsID0gbGFiZWwsIGNvbG9yID0gY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gNCwgaGp1c3QgPSAwKQogICAgfQogICAgcCA8LSBwICsgZ2VvbV9saW5lKCkKICAgIGlmIChzZSkgeyAjIyBhZGQgc3RhbmRhcmQgZXJyb3IgaWYgYXZhaWxhYmxlCiAgICAgICAgcCA8LSBwICsgZ2VvbV9yaWJib24oYWVzX3N0cmluZyh5bWluID0gIi5TIC0gLnNlIiwgeW1heCA9ICIuUyArIC5zZSIsIGNvbG9yID0gTlVMTCwgZmlsbCA9IGNvbG9yKSwgYWxwaGEgPSAwLjIpCiAgICB9CiAgICBpZiAocGxvdCkgewogICAgICAgIHBsb3QocCkKICAgIH0KICAgIGludmlzaWJsZShwKQp9CgpyYXJlLmxldmVsIDwtIHNhbXBsZV9zdW1zKGRhdGEpW1sxXV0KZmFjZXQgPC0gcGFzdGUoJ2ZhY2V0X3dyYXAoficscGFyYW1zJHZhckV4cCwnKScpCgpwIDwtIGdncmFyZShkYXRhLCBzdGVwID0gNTAwLCBjb2xvciA9IHBhcmFtcyR2YXJFeHAsIHBsb3QgPSBGQUxTRSkgKyAKICAgICAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSByYXJlLmxldmVsLCBjb2xvciA9ICJncmF5NjAiKSArIGV2YWwocGFyc2UodGV4dCA9IGZhY2V0KSkKcGxvdChwKQpgYGAKCiMjIFJlcHJvZHVjaWJpbGl0eSB0b2tlbgoKYGBge3Igc2Vzc2lvbiwgZWNobz1UUlVFLCBldmFsPVRSVUV9CnNlc3Npb25pbmZvOjpzZXNzaW9uX2luZm8oKQpgYGA=