Last updated: 2026-04-07

Checks: 6 1

Knit directory: Integrating-nir-genomic-kernel/

This reproducible R Markdown analysis was created with workflowr (version 1.7.2). The Checks tab describes the reproducibility checks that were applied when the results were created. The Past versions tab lists the development history.


The R Markdown file has unstaged changes. To know which version of the R Markdown file created these results, you’ll want to first commit it to the Git repo. If you’re still working on the analysis, you can ignore this warning. When you’re finished, you can run wflow_publish to commit the R Markdown file and build the HTML.

Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.

The command set.seed(20250829) was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.

Great job! Recording the operating system, R version, and package versions is critical for reproducibility.

Nice! There were no cached chunks for this analysis, so you can be confident that you successfully produced the results during this run.

Great job! Using relative paths to the files within your workflowr project makes it easier to run your code on other machines.

Great! You are using Git for version control. Tracking code development and connecting the code version to the results is critical for reproducibility.

The results in this page were generated with repository version d4d1044. See the Past versions tab to see a history of the changes made to the R Markdown and HTML files.

Note that you need to be careful to ensure that all relevant files for the analysis have been committed to Git prior to generating the results (you can use wflow_publish or wflow_git_commit). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:


Ignored files:
    Ignored:    .Rhistory
    Ignored:    .Rproj.user/
    Ignored:    analysis/analysis.Rmd
    Ignored:    analysis/run_cv0_cv00_only.R
    Ignored:    analysis/run_cv1_cv2_only.R
    Ignored:    data/Article_documents/
    Ignored:    data/Maize-NIRS-GBS-main/
    Ignored:    output/Matrizes/Geno.all.rds
    Ignored:    output/Matrizes/NIR.all.rds
    Ignored:    output/Matrizes/figures/
    Ignored:    output/adjust_models.rar
    Ignored:    output/climate_results/additional_climate_plots.tiff
    Ignored:    output/climate_results/combined_additional_climate_plots.tiff
    Ignored:    output/climate_results/combined_climate_plot.tiff
    Ignored:    output/cv-schemes.tiff

Untracked files:
    Untracked:  output/figures/
    Untracked:  output/results/
    Untracked:  output/tables/
    Untracked:  output/variance_components/

Unstaged changes:
    Modified:   analysis/analysis_prediction.Rmd
    Modified:   analysis/analysis_prediction_pt.Rmd
    Modified:   analysis/analysis_prediction_run_outputs.Rmd
    Modified:   analysis/analysis_prediction_run_outputs_pt.Rmd
    Modified:   analysis/variance_components.Rmd
    Modified:   analysis/variance_components_pt.Rmd
    Modified:   analysis/visualization.Rmd
    Modified:   analysis/visualization_pt.Rmd

Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.


These are the previous versions of the repository in which changes were made to the R Markdown (analysis/variance_components.Rmd) and HTML (docs/variance_components.html) files. If you’ve configured a remote Git repository (see ?wflow_git_remote), click on the hyperlinks in the table below to view the files as they were in that past version.

File Version Author Date Message
Rmd 2d2b88e WevertonGomesCosta 2026-04-06 update
Rmd 4fb14e1 WevertonGomesCosta 2026-04-06 update variance components
html 4fb14e1 WevertonGomesCosta 2026-04-06 update variance components
Rmd d1beaba WevertonGomesCosta 2026-04-06 update
html d1beaba WevertonGomesCosta 2026-04-06 update
html a7d077a WevertonGomesCosta 2026-04-03 update
Rmd b3df8b2 WevertonGomesCosta 2026-04-03 update
Rmd 0a6059c WevertonGomesCosta 2026-04-03 update
html f6d5560 WevertonGomesCosta 2026-04-03 update files
html 005c393 WevertonGomesCosta 2026-04-02 update
html d54afd7 WevertonGomesCosta 2026-04-01 update
Rmd 00ef3ef WevertonGomesCosta 2026-04-01 update
html 00ef3ef WevertonGomesCosta 2026-04-01 update
Rmd 1915912 WevertonGomesCosta 2026-03-31 update
Rmd 9827bcc WevertonGomesCosta 2026-03-31 update scripts rmd
html 4e5af9a WevertonGomesCosta 2026-03-27 update
Rmd 640dfbe WevertonGomesCosta 2026-03-27 update
Rmd 0a6715e WevertonGomesCosta 2026-03-27 update
html 3573a52 WevertonGomesCosta 2026-03-27 update
Rmd 717d6b9 WevertonGomesCosta 2026-02-24 update
html 717d6b9 WevertonGomesCosta 2026-02-24 update
Rmd c996e1e WevertonGomesCosta 2026-02-24 update
Rmd 5b10ff7 WevertonGomesCosta 2026-02-24 update
Rmd 1a46586 WevertonGomesCosta 2025-11-04 update variance_components .rmd and .html
html 1a46586 WevertonGomesCosta 2025-11-04 update variance_components .rmd and .html
Rmd f18937a WevertonGomesCosta 2025-11-04 chance components_variance to variance_components

The document starts by setting knitting options so that tables, code, and figures are displayed consistently throughout the tutorial.

Language / Idioma: English | Português

1. Introduction

This tutorial summarizes variance components for the predictive models used in this study. In the current workflow, this file is used mainly to consolidate and interpret results that were already estimated previously, although it still keeps the code structure needed to fit the models when that step must be rerun.

The goal is to quantify how much of the total phenotypic variance is attributed to the main effects and interaction terms included in each model. In practical terms, this script helps answer questions such as:

  • How much variance is associated with the categorical environment effect (E)?
  • How much is captured by the genomic kernels?
  • How much is captured by the phenomic/NIRS kernels (P)?
  • How much is associated with the weather kernel (W), which is used mainly in models Eta10-Eta18?
  • How relevant are the interaction terms such as G × E, G × W, P × E, and P × W?

This script is organized into four stages:

  1. Load kernels and phenotypic data produced in matrizes.Rmd.
  2. Define the 18 variance-component models.
  3. Read or rerun the BGLR outputs, depending on whether the saved posterior summaries already exist.
  4. Process and visualize the variance decomposition for Grain Yield (GY) and Kernel Weight (KW).

A high variance share for a given source should not be interpreted automatically as evidence of biological complementarity or better predictive performance. This point is especially important for P, because NIRS-based similarity may reflect strong physiological response to the environment in addition to any stable biological signal, and for W, because the weather kernel complements but does not replace the categorical environment effect.

Inputs, outputs and next step

Inputs of this step - matrices and kernels generated by matrizes.Rmd - the reduced catalog of 18 models used in the project

Outputs of this step - raw variance-component results - processed variance-component summaries - percentage tables and diagnostic figures

Next step - this stage complements the structural interpretation of the models and should be read together with the predictive results generated later in analysis_prediction.Rmd.

2. R Environment and Required Inputs

First, we load the packages used to manipulate the data, fit the Bayesian models, and generate the final article-quality figures.

The next step loads the packages needed for reading the saved artifacts, reshaping the results, plotting summaries, and running the heavy estimation stage when required.

library(dplyr)
library(tidyr)
library(readr)
library(stringr)
library(tibble)
library(ggplot2)
library(BGLR)
library(parallel)
library(doParallel)
library(foreach)
library(ggthemes)
library(scales)
library(fs)
library(grid)
library(knitr)
library(kableExtra)

2.1. File paths

All kernels and phenotypic objects are expected to be available in output/Matrizes/, which is the output directory created in matrizes.Rmd. The variance-component results generated here are stored in output/variance_components/.

The following lines define the project directories used to find matrices, store outputs, and organize the saved variance-component files.

kernel_dir <- "output/Matrizes"
results_dir <- "output/variance_components"
figure_dir <- file.path(results_dir, "figures")
bglr_runs_dir <- file.path(results_dir, "bglr_runs")

dir.create(results_dir, recursive = TRUE, showWarnings = FALSE)
dir.create(bglr_runs_dir, recursive = TRUE, showWarnings = FALSE)
dir.create(figure_dir, recursive = TRUE, showWarnings = FALSE)

2.2. Input checks

Before reading the files, we check whether the expected .rds objects are present in the matrices output folder. This makes the tutorial easier to follow because the script stops immediately if a required object is missing.

Before moving into model definition or result processing, the script checks that the required input files and directories are available.

required_files <- c(
  "ZG.rds", "ZP.rds", "ZE.rds", "ZEZE.rds", "ZW.rds",
  "ZGZE.rds", "ZPZE.rds", "ZGZW.rds", "ZPZW.rds",
  "GGK.rds", "PGK.rds", "GAK.rds", "PAK.rds",
  "GGKE.rds", "PGKE.rds", "GGKW.rds", "PGKW.rds",
  "GAKE.rds", "PAKE.rds", "GAKW.rds", "PAKW.rds",
  "Pheno.rds", "Pedigree.rds"
)

missing_files <- required_files[!file.exists(file.path(kernel_dir, required_files))]

if (length(missing_files) > 0) {
  stop(
    "The following required files were not found in output/Matrizes/: ",
    paste(missing_files, collapse = ", "),
    ". Run matrizes.Rmd first."
  )
}

2.3. Labels used in the final tables and figures

These vectors define how the variance components will appear in the final outputs. Keeping these labels visible in the main script helps the reader understand how internal model names are translated into article-ready labels.

Here the script defines the labels and naming conventions that will be reused in tables, output files, and figures.

component_display <- c(
  E = "Environment (E)",
  G = "Genomic linear (G)",
  P = "Phenomic linear (P)",
  W = "Weather kernel (W)",
  GE = "G × E",
  PE = "P × E",
  GW = "G × W",
  PW = "P × W",
  GGK = "Genomic Gaussian (GGK)",
  PGK = "Phenomic Gaussian (PGK)",
  GAK = "Genomic arc-cosine (GAK)",
  PAK = "Phenomic arc-cosine (PAK)",
  GGKE = "GGK × E",
  PGKE = "PGK × E",
  GAKE = "GAK × E",
  PAKE = "PAK × E",
  GGKW = "GGK × W",
  PGKW = "PGK × W",
  GAKW = "GAK × W",
  PAKW = "PAK × W",
  Residual = "Residual"
)

component_order <- c(
  "Environment (E)",
  "Genomic linear (G)",
  "Phenomic linear (P)",
  "Weather kernel (W)",
  "G × E",
  "P × E",
  "G × W",
  "P × W",
  "Genomic Gaussian (GGK)",
  "Phenomic Gaussian (PGK)",
  "Genomic arc-cosine (GAK)",
  "Phenomic arc-cosine (PAK)",
  "GGK × E",
  "PGK × E",
  "GAK × E",
  "PAK × E",
  "GGK × W",
  "PGK × W",
  "GAK × W",
  "PAKW × W",
  "Residual"
)

component_order[component_order == "PAKW × W"] <- "PAK × W"

normalize_component_name <- function(component_name, model_value = NA_integer_) {
  component_name <- as.character(component_name)

  if (length(model_value) == 1L && length(component_name) > 1L) {
    model_value <- rep(model_value, length(component_name))
  }

  if (length(model_value) != length(component_name)) {
    stop("normalize_component_name() requires 'component_name' and 'model_value' to have the same length, or a scalar 'model_value'.")
  }

  vapply(seq_along(component_name), function(i) {
    current_component <- component_name[i]
    current_model <- model_value[i]

    if (is.factor(current_model)) {
      current_model <- as.character(current_model)
    }

    if (is.character(current_model)) {
      current_model <- suppressWarnings(as.integer(sub("^Eta", "", current_model)))
    }

    if (length(current_component) == 0 || is.na(current_component) || !nzchar(current_component)) {
      return(current_component)
    }

    if (length(current_model) == 1L && !is.na(current_model) && grepl("^ETA_[0-9]+$", current_component)) {
      effect_index <- suppressWarnings(as.integer(sub("^ETA_", "", current_component)))
      if (!is.na(effect_index) && current_model >= 1 && current_model <= length(models)) {
        model_names <- names(models[[current_model]])
        if (!is.null(model_names) && effect_index >= 1 && effect_index <= length(model_names)) {
          current_component <- model_names[effect_index]
        }
      }
    }

    current_component <- sub("_(BRR|RKHS)$", "", current_component)
    current_component
  }, character(1))
}

3. Loading the Kernels and Phenotypic Objects

This section loads the matrices generated in matrizes.Rmd. These objects are the core inputs for the variance-component analysis.

At this point, the script reads the matrices and kernels generated in the previous module so the variance-component models use the same aligned inputs as the prediction stage.

ZG    <- readRDS(file.path(kernel_dir, "ZG.rds"))
ZP    <- readRDS(file.path(kernel_dir, "ZP.rds"))
ZE    <- readRDS(file.path(kernel_dir, "ZE.rds"))
ZEZE  <- readRDS(file.path(kernel_dir, "ZEZE.rds"))
ZW    <- readRDS(file.path(kernel_dir, "ZW.rds"))

ZGZE  <- readRDS(file.path(kernel_dir, "ZGZE.rds"))
ZPZE  <- readRDS(file.path(kernel_dir, "ZPZE.rds"))
ZGZW  <- readRDS(file.path(kernel_dir, "ZGZW.rds"))
ZPZW  <- readRDS(file.path(kernel_dir, "ZPZW.rds"))

GGK   <- readRDS(file.path(kernel_dir, "GGK.rds"))
PGK   <- readRDS(file.path(kernel_dir, "PGK.rds"))
GAK   <- readRDS(file.path(kernel_dir, "GAK.rds"))
PAK   <- readRDS(file.path(kernel_dir, "PAK.rds"))

GGKE  <- readRDS(file.path(kernel_dir, "GGKE.rds"))
PGKE  <- readRDS(file.path(kernel_dir, "PGKE.rds"))
GGKW  <- readRDS(file.path(kernel_dir, "GGKW.rds"))
PGKW  <- readRDS(file.path(kernel_dir, "PGKW.rds"))

GAKE  <- readRDS(file.path(kernel_dir, "GAKE.rds"))
PAKE  <- readRDS(file.path(kernel_dir, "PAKE.rds"))
GAKW  <- readRDS(file.path(kernel_dir, "GAKW.rds"))
PAKW  <- readRDS(file.path(kernel_dir, "PAKW.rds"))

Pheno    <- readRDS(file.path(kernel_dir, "Pheno.rds"))
Pedigree <- readRDS(file.path(kernel_dir, "Pedigree.rds"))

n_obs <- nrow(Pheno)
cat("Number of phenotypic observations:", n_obs, "\n")
Number of phenotypic observations: 982 
cat("Number of pedigrees:", length(Pedigree), "\n")
Number of pedigrees: 329 

3.1. Alignment diagnostics

Before fitting the models, it is important to verify that the kernels are square and aligned to the same observation order used in Pheno. In a tutorial context, this is an important check because many downstream errors are caused by mismatched row and column orders.

The loaded kernels are then checked for compatibility in dimension and ordering before they are combined in the Bayesian models.

reference_ids <- Pheno$ObsID

ZE <- ZE[match(reference_ids, rownames(ZE)), , drop = FALSE]

ZG   <- ZG[reference_ids, reference_ids, drop = FALSE]
ZP   <- ZP[reference_ids, reference_ids, drop = FALSE]
ZEZE <- ZEZE[reference_ids, reference_ids, drop = FALSE]
ZW   <- ZW[reference_ids, reference_ids, drop = FALSE]
ZGZE <- ZGZE[reference_ids, reference_ids, drop = FALSE]
ZPZE <- ZPZE[reference_ids, reference_ids, drop = FALSE]
ZGZW <- ZGZW[reference_ids, reference_ids, drop = FALSE]
ZPZW <- ZPZW[reference_ids, reference_ids, drop = FALSE]
GGK  <- GGK[reference_ids, reference_ids, drop = FALSE]
PGK  <- PGK[reference_ids, reference_ids, drop = FALSE]
GAK  <- GAK[reference_ids, reference_ids, drop = FALSE]
PAK  <- PAK[reference_ids, reference_ids, drop = FALSE]
GGKE <- GGKE[reference_ids, reference_ids, drop = FALSE]
PGKE <- PGKE[reference_ids, reference_ids, drop = FALSE]
GGKW <- GGKW[reference_ids, reference_ids, drop = FALSE]
PGKW <- PGKW[reference_ids, reference_ids, drop = FALSE]
GAKE <- GAKE[reference_ids, reference_ids, drop = FALSE]
PAKE <- PAKE[reference_ids, reference_ids, drop = FALSE]
GAKW <- GAKW[reference_ids, reference_ids, drop = FALSE]
PAKW <- PAKW[reference_ids, reference_ids, drop = FALSE]

kernel_inventory <- tibble(
  kernel = c("ZG", "ZP", "ZEZE", "ZW", "GGK", "PGK", "GAK", "PAK"),
  n_rows = c(nrow(ZG), nrow(ZP), nrow(ZEZE), nrow(ZW), nrow(GGK), nrow(PGK), nrow(GAK), nrow(PAK)),
  n_cols = c(ncol(ZG), ncol(ZP), ncol(ZEZE), ncol(ZW), ncol(GGK), ncol(PGK), ncol(GAK), ncol(PAK)),
  is_square = c(
    nrow(ZG) == ncol(ZG),
    nrow(ZP) == ncol(ZP),
    nrow(ZEZE) == ncol(ZEZE),
    nrow(ZW) == ncol(ZW),
    nrow(GGK) == ncol(GGK),
    nrow(PGK) == ncol(PGK),
    nrow(GAK) == ncol(GAK),
    nrow(PAK) == ncol(PAK)
  ),
  symmetric_names = c(
    identical(rownames(ZG), colnames(ZG)),
    identical(rownames(ZP), colnames(ZP)),
    identical(rownames(ZEZE), colnames(ZEZE)),
    identical(rownames(ZW), colnames(ZW)),
    identical(rownames(GGK), colnames(GGK)),
    identical(rownames(PGK), colnames(PGK)),
    identical(rownames(GAK), colnames(GAK)),
    identical(rownames(PAK), colnames(PAK))
  ),
  aligned_with_reference = c(
    identical(rownames(ZG), reference_ids),
    identical(rownames(ZP), reference_ids),
    identical(rownames(ZEZE), reference_ids),
    identical(rownames(ZW), reference_ids),
    identical(rownames(GGK), reference_ids),
    identical(rownames(PGK), reference_ids),
    identical(rownames(GAK), reference_ids),
    identical(rownames(PAK), reference_ids)
  )
)

kernel_inventory %>%
  dplyr::mutate(
    n_rows = format(n_rows, big.mark = ","),
    n_cols = format(n_cols, big.mark = ",")
  ) %>%
  knitr::kable(
    caption = "Diagnostic summary of the main kernels used in the variance-component analysis after explicit alignment to the phenotypic observation order.",
    align = "lcccccc"
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "left"
  ) %>%
  kableExtra::scroll_box(width = "100%", height = "260px")
Diagnostic summary of the main kernels used in the variance-component analysis after explicit alignment to the phenotypic observation order.
kernel n_rows n_cols is_square symmetric_names aligned_with_reference
ZG 982 982 TRUE TRUE TRUE
ZP 982 982 TRUE TRUE TRUE
ZEZE 982 982 TRUE TRUE TRUE
ZW 982 982 TRUE TRUE TRUE
GGK 982 982 TRUE TRUE TRUE
PGK 982 982 TRUE TRUE TRUE
GAK 982 982 TRUE TRUE TRUE
PAK 982 982 TRUE TRUE TRUE

4. Definition of the 18 Predictive Models

Each model is defined as a list in the format required by the ETA argument of BGLR. The environment term (E) is included as a fixed-effect design matrix with model = "BRR", while the kernels are fitted as random effects with model = "RKHS".

The reduced set of 18 Bayesian multi-kernel models is defined below in the form required for the variance-component workflow.

models <- list()

models[[1]] <- list(
  E  = list(X = ZE, model = "BRR"),
  G  = list(K = ZG, model = "RKHS"),
  GE = list(K = ZGZE, model = "RKHS")
)

models[[2]] <- list(
  E  = list(X = ZE, model = "BRR"),
  P  = list(K = ZP, model = "RKHS"),
  PE = list(K = ZPZE, model = "RKHS")
)

models[[3]] <- list(
  E    = list(X = ZE, model = "BRR"),
  GGK  = list(K = GGK, model = "RKHS"),
  GGKE = list(K = GGKE, model = "RKHS")
)

models[[4]] <- list(
  E    = list(X = ZE, model = "BRR"),
  PGK  = list(K = PGK, model = "RKHS"),
  PGKE = list(K = PGKE, model = "RKHS")
)

models[[5]] <- list(
  E    = list(X = ZE, model = "BRR"),
  GAK  = list(K = GAK, model = "RKHS"),
  GAKE = list(K = GAKE, model = "RKHS")
)

models[[6]] <- list(
  E    = list(X = ZE, model = "BRR"),
  PAK  = list(K = PAK, model = "RKHS"),
  PAKE = list(K = PAKE, model = "RKHS")
)

models[[7]] <- list(
  E  = list(X = ZE, model = "BRR"),
  G  = list(K = ZG, model = "RKHS"),
  P  = list(K = ZP, model = "RKHS"),
  GE = list(K = ZGZE, model = "RKHS"),
  PE = list(K = ZPZE, model = "RKHS")
)

models[[8]] <- list(
  E    = list(X = ZE, model = "BRR"),
  GGK  = list(K = GGK, model = "RKHS"),
  PGK  = list(K = PGK, model = "RKHS"),
  GGKE = list(K = GGKE, model = "RKHS"),
  PGKE = list(K = PGKE, model = "RKHS")
)

models[[9]] <- list(
  E    = list(X = ZE, model = "BRR"),
  GAK  = list(K = GAK, model = "RKHS"),
  PAK  = list(K = PAK, model = "RKHS"),
  GAKE = list(K = GAKE, model = "RKHS"),
  PAKE = list(K = PAKE, model = "RKHS")
)

models[[10]] <- list(
  E  = list(X = ZE, model = "BRR"),
  W  = list(K = ZW, model = "RKHS"),
  G  = list(K = ZG, model = "RKHS"),
  GE = list(K = ZGZE, model = "RKHS"),
  GW = list(K = ZGZW, model = "RKHS")
)

models[[11]] <- list(
  E  = list(X = ZE, model = "BRR"),
  W  = list(K = ZW, model = "RKHS"),
  P  = list(K = ZP, model = "RKHS"),
  PE = list(K = ZPZE, model = "RKHS"),
  PW = list(K = ZPZW, model = "RKHS")
)

models[[12]] <- list(
  E  = list(X = ZE, model = "BRR"),
  W  = list(K = ZW, model = "RKHS"),
  G  = list(K = ZG, model = "RKHS"),
  P  = list(K = ZP, model = "RKHS"),
  GE = list(K = ZGZE, model = "RKHS"),
  PE = list(K = ZPZE, model = "RKHS"),
  GW = list(K = ZGZW, model = "RKHS"),
  PW = list(K = ZPZW, model = "RKHS")
)

models[[13]] <- list(
  E    = list(X = ZE, model = "BRR"),
  W    = list(K = ZW, model = "RKHS"),
  GGK  = list(K = GGK, model = "RKHS"),
  GGKE = list(K = GGKE, model = "RKHS"),
  GGKW = list(K = GGKW, model = "RKHS")
)

models[[14]] <- list(
  E    = list(X = ZE, model = "BRR"),
  W    = list(K = ZW, model = "RKHS"),
  PGK  = list(K = PGK, model = "RKHS"),
  PGKE = list(K = PGKE, model = "RKHS"),
  PGKW = list(K = PGKW, model = "RKHS")
)

models[[15]] <- list(
  E    = list(X = ZE, model = "BRR"),
  W    = list(K = ZW, model = "RKHS"),
  GGK  = list(K = GGK, model = "RKHS"),
  PGK  = list(K = PGK, model = "RKHS"),
  GGKE = list(K = GGKE, model = "RKHS"),
  PGKE = list(K = PGKE, model = "RKHS"),
  GGKW = list(K = GGKW, model = "RKHS"),
  PGKW = list(K = PGKW, model = "RKHS")
)

models[[16]] <- list(
  E    = list(X = ZE, model = "BRR"),
  W    = list(K = ZW, model = "RKHS"),
  GAK  = list(K = GAK, model = "RKHS"),
  GAKE = list(K = GAKE, model = "RKHS"),
  GAKW = list(K = GAKW, model = "RKHS")
)

models[[17]] <- list(
  E    = list(X = ZE, model = "BRR"),
  W    = list(K = ZW, model = "RKHS"),
  PAK  = list(K = PAK, model = "RKHS"),
  PAKE = list(K = PAKE, model = "RKHS"),
  PAKW = list(K = PAKW, model = "RKHS")
)

models[[18]] <- list(
  E    = list(X = ZE, model = "BRR"),
  W    = list(K = ZW, model = "RKHS"),
  GAK  = list(K = GAK, model = "RKHS"),
  PAK  = list(K = PAK, model = "RKHS"),
  GAKE = list(K = GAKE, model = "RKHS"),
  PAKE = list(K = PAKE, model = "RKHS"),
  GAKW = list(K = GAKW, model = "RKHS"),
  PAKW = list(K = PAKW, model = "RKHS")
)

names(models) <- sprintf("Eta%02d", seq_along(models))

4.1. Model catalog

This table is useful both for internal checks and for linking figure labels to the formal description of each model.

The code below builds a compact model catalog linking each model identifier to its category and descriptive label for later summaries.

model_info <- tibble(
  ModelID = 1:18,
  Model = paste0("Eta", 1:18),
  ETA = names(models),
  Category = c(
    rep("Single-source + E", 6),
    rep("Integrated G/P + E", 3),
    rep("Weather-augmented", 9)
  ),
  Description = c(
    "E + G + GE",
    "E + P + PE",
    "E + GGK + GGKE",
    "E + PGK + PGKE",
    "E + GAK + GAKE",
    "E + PAK + PAKE",
    "E + G + P + GE + PE",
    "E + GGK + PGK + GGKE + PGKE",
    "E + GAK + PAK + GAKE + PAKE",
    "E + W + G + GE + GW",
    "E + W + P + PE + PW",
    "E + W + G + P + GE + PE + GW + PW",
    "E + W + GGK + GGKE + GGKW",
    "E + W + PGK + PGKE + PGKW",
    "E + W + GGK + PGK + GGKE + PGKE + GGKW + PGKW",
    "E + W + GAK + GAKE + GAKW",
    "E + W + PAK + PAKE + PAKW",
    "E + W + GAK + PAK + GAKE + PAKE + GAKW + PAKW"
  )
)

readr::write_csv(model_info, file.path(results_dir, "model_catalog.csv"))

model_info %>%
  knitr::kable(
    caption = "Catalog of the 18 models evaluated in the variance-component analysis.",
    align = "cll"
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "left"
  ) %>%
  kableExtra::scroll_box(width = "100%", height = "340px")
Catalog of the 18 models evaluated in the variance-component analysis.
ModelID Model ETA Category Description
1 Eta1 Eta01 Single-source + E E + G + GE
2 Eta2 Eta02 Single-source + E E + P + PE
3 Eta3 Eta03 Single-source + E E + GGK + GGKE
4 Eta4 Eta04 Single-source + E E + PGK + PGKE
5 Eta5 Eta05 Single-source + E E + GAK + GAKE
6 Eta6 Eta06 Single-source + E E + PAK + PAKE
7 Eta7 Eta07 Integrated G/P + E E + G + P + GE + PE
8 Eta8 Eta08 Integrated G/P + E E + GGK + PGK + GGKE + PGKE
9 Eta9 Eta09 Integrated G/P + E E + GAK + PAK + GAKE + PAKE
10 Eta10 Eta10 Weather-augmented E + W + G + GE + GW
11 Eta11 Eta11 Weather-augmented E + W + P + PE + PW
12 Eta12 Eta12 Weather-augmented E + W + G + P + GE + PE + GW + PW
13 Eta13 Eta13 Weather-augmented E + W + GGK + GGKE + GGKW
14 Eta14 Eta14 Weather-augmented E + W + PGK + PGKE + PGKW
15 Eta15 Eta15 Weather-augmented E + W + GGK + PGK + GGKE + PGKE + GGKW + PGKW
16 Eta16 Eta16 Weather-augmented E + W + GAK + GAKE + GAKW
17 Eta17 Eta17 Weather-augmented E + W + PAK + PAKE + PAKW
18 Eta18 Eta18 Weather-augmented E + W + GAK + PAK + GAKE + PAKE + GAKW + PAKW

5. MCMC Settings

These values define the Gibbs sampler used by BGLR. Adjust them here if a longer chain is required for convergence diagnostics or sensitivity analyses.

This section defines the MCMC settings and execution controls that regulate the estimation stage when the heavy computation is turned on.

nIter   <- 5000
burnIn  <- 1000
thin    <- 10
num_rep <- 10
traits  <- c("GY", "KW")

6. Running BGLR for Each Model

This is the computationally expensive step. The chunk is kept with eval=FALSE so that the tutorial can be rendered without rerunning all Bayesian models every time. When you need updated variance components, change eval to TRUE and run the chunk.

The next block contains the computationally expensive parallel estimation stage that fits the variance-component models, preserves the iterative BGLR artifacts in persistent folders, and saves the raw posterior summaries.

numCores <- parallel::detectCores()
useCores <- max(1, numCores - 1)
cl <- parallel::makeCluster(useCores)
doParallel::registerDoParallel(cl)

combos <- expand.grid(
  Model = 1:18,
  REP = 1:num_rep,
  Trait = traits,
  stringsAsFactors = FALSE
)

results_list <- foreach(
  i = 1:nrow(combos),
  .packages = c("BGLR", "dplyr", "tibble")
) %dopar% {
  Model <- combos$Model[i]
  rep_num <- combos$REP[i]
  Trait <- combos$Trait[i]
  rep_label <- sprintf("rep%02d", rep_num)
  eta_label <- paste0("Eta", Model)

  pheno <- data.frame(Pheno)
  y_data <- as.numeric(pheno[[Trait]])

  out_dir <- file.path(bglr_runs_dir, Trait, rep_label, eta_label)
  dir.create(out_dir, recursive = TRUE, showWarnings = FALSE)

  save_prefix <- file.path(
    out_dir,
    paste0("VC_", Trait, "_", eta_label, "_", rep_label, "_")
  )

  start_time <- Sys.time()
  fit <- BGLR(
    y = y_data,
    ETA = models[[Model]],
    nIter = nIter,
    burnIn = burnIn,
    thin = thin,
    saveAt = save_prefix
  )
  end_time <- Sys.time()

  effect_names <- names(models[[Model]])
  effect_models <- vapply(models[[Model]], function(x) x$model, character(1))
  effect_labels <- paste0(effect_names, "_", effect_models)

  generated_files <- list.files(
    out_dir,
    pattern = paste0("^", basename(save_prefix)),
    full.names = TRUE
  )

  if (length(generated_files) > 0) {
    for (current_file in generated_files) {
      current_name <- basename(current_file)
      renamed_name <- current_name

      for (j in seq_along(effect_labels)) {
        renamed_name <- sub(
          paste0("ETA_", j),
          effect_labels[j],
          renamed_name,
          fixed = TRUE
        )
      }

      if (!identical(current_name, renamed_name)) {
        file.rename(current_file, file.path(out_dir, renamed_name))
      }
    }
  }

  variance_samples <- list()

  if (!is.null(fit$ETA)) {
    if (is.null(effect_names)) {
      effect_names <- paste0("Effect_", seq_along(fit$ETA))
    }

    for (j in seq_along(fit$ETA)) {
      parameter_names <- names(fit$ETA[[j]])
      var_parameter <- parameter_names[grepl("^var", parameter_names)]

      if (length(var_parameter) > 0) {
        variance_samples[[effect_names[j]]] <- fit$ETA[[j]][[var_parameter[1]]]
      }
    }
  }

  metrics_object <- list(
    varE_samples = fit$varE,
    variances_samples = variance_samples,
    DIC = fit$fit$DIC,
    runtime_sec = as.numeric(difftime(end_time, start_time, units = "secs"))
  )

  saveRDS(metrics_object, file.path(out_dir, "variance_metrics.rds"))

  result_table <- tibble(
    Component = "Residual",
    MeanVar = mean(metrics_object$varE_samples, na.rm = TRUE)
  )

  if (length(metrics_object$variances_samples) > 0) {
    for (nm in names(metrics_object$variances_samples)) {
      result_table <- bind_rows(
        result_table,
        tibble(
          Component = nm,
          MeanVar = mean(metrics_object$variances_samples[[nm]], na.rm = TRUE)
        )
      )
    }
  }

  rm(fit)
  gc()

  result_table %>%
    mutate(Model = Model, Rep = rep_num, Trait = Trait)
}

stopCluster(cl)

all_variance <- bind_rows(results_list)
write_csv(all_variance, file.path(results_dir, "variance_components_raw.csv"))
saveRDS(all_variance, file.path(results_dir, "variance_components_raw.rds"))

7. Consolidating Saved Posterior Summaries

In the current version of the workflow, this is the section used most often. Instead of refitting all models every time, the script reads the previously saved variance_metrics.rds files from output/variance_components/bglr_runs/ and consolidates them into one object. This keeps the tutorial practical and reproducible, while making it clear that this page is mainly a result-consolidation step unless the user intentionally reruns the full BGLR estimation stage.

The saved outputs produced by the estimation stage are consolidated below into a single structure that can be processed more easily later.

rds_files <- list.files(
  path = bglr_runs_dir,
  pattern = "(variance_metrics|_(metrics|metricas))\\.rds$",
  recursive = TRUE,
  full.names = TRUE
)

if (length(rds_files) > 0) {
  all_tables <- vector("list", length(rds_files))

  for (i in seq_along(rds_files)) {
    current_file <- normalizePath(rds_files[i], winslash = "/", mustWork = FALSE)
    path_parts <- str_split(current_file, "/")[[1]]

    rep_part <- path_parts[str_detect(path_parts, "^rep[0-9]+$|^rep_[0-9]+$")][1]
    eta_part <- path_parts[str_detect(path_parts, "^Eta")][1]
    trait_part <- path_parts[path_parts %in% traits][1]

    rep_value <- as.integer(str_remove(rep_part, "^rep_?0*"))
    model_value <- as.integer(str_remove(eta_part, "^Eta0*"))
    trait_value <- trait_part

    current_object <- readRDS(current_file)

    current_table <- tibble(
      Rep = rep_value,
      Model = model_value,
      Trait = trait_value,
      Component = "Residual",
      MeanVar = mean(current_object$varE_samples, na.rm = TRUE),
      DIC = current_object$DIC,
      runtime_sec = current_object$runtime_sec
    )

    if (length(current_object$variances_samples) > 0) {
      for (nm in names(current_object$variances_samples)) {
        current_table <- bind_rows(
          current_table,
          tibble(
            Rep = rep_value,
            Model = model_value,
            Trait = trait_value,
            Component = normalize_component_name(nm, model_value = model_value),
            MeanVar = mean(current_object$variances_samples[[nm]], na.rm = TRUE),
            DIC = current_object$DIC,
            runtime_sec = current_object$runtime_sec
          )
        )
      }
    }

    all_tables[[i]] <- current_table
  }

  all_variance <- bind_rows(all_tables)

  saveRDS(all_variance, file.path(results_dir, "variance_components_all.rds"))
  write_csv(all_variance, file.path(results_dir, "variance_components_raw.csv"))
}

8. Loading the Consolidated Variance-Component Object

This section loads the consolidated results and prepares them for summarization. If the file does not exist, the script stops and instructs the user to execute the model-fitting stage first.

The document now loads the already consolidated variance-component results so that it can summarize and visualize them without repeating the expensive estimation.

raw_file <- file.path(results_dir, "variance_components_all.rds")

if (!file.exists(raw_file)) {
  stop(
    "The file '", raw_file, "' was not found. ",
    "Run the Bayesian fitting stage first or keep the previously saved _metrics.rds files in output/variance_components/."
  )
}

all_variance <- readRDS(raw_file)

all_variance <- all_variance %>%
  mutate(
    Trait = str_extract(Trait, "GY|KW"),
    Trait = ifelse(is.na(Trait), Trait, Trait),
    Model = as.integer(Model)
  )

cat("Number of rows in consolidated variance object:", nrow(all_variance), "\n")
Number of rows in consolidated variance object: 2100 

9. Summarizing the Variance Components

Now we average the posterior means across repetitions for each model, trait, and component. Then we convert the results to percentages of total variance, which makes the models directly comparable.

The loaded results are then reshaped and annotated so that posterior means can be compared across traits, models, and component categories.

variance_summary <- all_variance %>%
  group_by(Model, Trait, Component) %>%
  summarise(
    AvgVar = mean(MeanVar, na.rm = TRUE),
    MeanDIC = mean(DIC, na.rm = TRUE),
    MeanRuntimeSec = mean(runtime_sec, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  filter(!is.na(AvgVar), AvgVar > 0) %>%
  group_by(Model, Trait) %>%
  mutate(
    TotalVar = sum(AvgVar),
    Percentage = AvgVar / TotalVar
  ) %>%
  ungroup() %>%
  left_join(model_info, by = c("Model" = "ModelID")) %>%
  mutate(
    ModelNumber = Model,
    Model = factor(paste0("Eta", ModelNumber), levels = paste0("Eta", 1:18)),
    Component = normalize_component_name(Component, model_value = ModelNumber),
    ComponentLabel = dplyr::recode(Component, !!!component_display),
    ComponentLabel = factor(ComponentLabel, levels = component_order),
    Trait = factor(Trait, levels = c("GY", "KW"))
  ) %>%
  dplyr::select(-ModelNumber)

readr::write_csv(variance_summary, file.path(results_dir, "variance_components_processed.csv"))

9.1. Wide-format summary table

This table is helpful when you need a compact numerical summary for the manuscript or supplementary material.

The next table summarizes the relative contribution of each variance component across models.

variance_table <- variance_summary %>%
  dplyr::select(Model, Trait, ComponentLabel, Percentage) %>%
  dplyr::mutate(Percentage = round(100 * Percentage, 2)) %>%
  tidyr::pivot_wider(names_from = ComponentLabel, values_from = Percentage)

readr::write_csv(variance_table, file.path(results_dir, "variance_components_table_percent.csv"))

head(variance_table, 12) %>%
  knitr::kable(
    caption = "Preview of the processed variance-component table (percent of total variance).",
    align = "lcccccccccccc"
  ) %>%
  kableExtra::kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = FALSE,
    position = "left"
  ) %>%
  kableExtra::scroll_box(width = "100%", height = "320px")
Preview of the processed variance-component table (percent of total variance).
Model Trait Environment (E) Genomic linear (G) G × E Residual Phenomic linear (P) P × E Genomic Gaussian (GGK) GGK × E Phenomic Gaussian (PGK) PGK × E Genomic arc-cosine (GAK) GAK × E Phenomic arc-cosine (PAK) PAK × E G × W Weather kernel (W) P × W GGK × W PGK × W GAK × W PAK × W
Eta1 GY 83.59 11.96 1.70 2.75 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Eta1 KW 75.42 20.80 1.46 2.33 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Eta2 GY 0.00 NA NA 0.00 100 0 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Eta2 KW 0.00 NA NA 0.00 100 0 NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
Eta3 GY 49.52 NA NA 1.92 NA NA 45.13 3.43 NA NA NA NA NA NA NA NA NA NA NA NA NA
Eta3 KW 35.42 NA NA 1.26 NA NA 61.28 2.04 NA NA NA NA NA NA NA NA NA NA NA NA NA
Eta4 GY 78.05 NA NA 10.87 NA NA NA NA 7.54 3.53 NA NA NA NA NA NA NA NA NA NA NA
Eta4 KW 73.38 NA NA 11.51 NA NA NA NA 11.07 4.04 NA NA NA NA NA NA NA NA NA NA NA
Eta5 GY 5.49 NA NA 0.26 NA NA NA NA NA NA 92.52 1.72 NA NA NA NA NA NA NA NA NA
Eta5 KW 3.38 NA NA 0.16 NA NA NA NA NA NA 95.71 0.75 NA NA NA NA NA NA NA NA NA
Eta6 GY 18.38 NA NA 3.26 NA NA NA NA NA NA NA NA 77.06 1.30 NA NA NA NA NA NA NA
Eta6 KW 10.60 NA NA 2.14 NA NA NA NA NA NA NA NA 86.37 0.89 NA NA NA NA NA NA NA

10. Figure for the Article

The next figure summarizes the percentage contribution of each variance component across the 18 models for both traits. To keep the x-axis readable, the models are displayed as Eta1 to Eta18, and the detailed descriptions remain available in the model catalog saved above.

This figure should be read as a structural summary of the fitted models, not as direct proof that combining more data sources automatically leads to stronger biological complementarity or better predictive performance.

The final figure turns the processed table into a compact visual summary for interpretation.

trait_labels <- c(
  GY = "A. Grain yield (GY)",
  KW = "B. Kernel weight (KW)"
)

variance_plot <- variance_summary %>%
  ggplot(aes(x = Model, y = Percentage, fill = ComponentLabel)) +
  geom_col(color = "white", linewidth = 0.15, width = 0.82) +
  facet_wrap(~ Trait, ncol = 1, labeller = as_labeller(trait_labels)) +
  ggthemes::scale_fill_gdocs(name = "Variance component", drop = FALSE) +
  scale_y_continuous(
    labels = scales::percent_format(accuracy = 1),
    expand = expansion(mult = c(0, 0.02))
  ) +
  labs(
    x = "Model ID",
    y = "Proportion of total variance"
  ) +
  theme_minimal(base_size = 11) +
  theme(
    plot.title = element_text(face = "bold", size = 13, hjust = 0.5),
    strip.text = element_text(face = "bold", size = 11),
    axis.title = element_text(face = "bold"),
    axis.text.x = element_text(angle = 45, hjust = 1, vjust = 1, size = 8),
    axis.text.y = element_text(size = 9),
    legend.position = "right",
    legend.box = "vertical",
    legend.title = element_text(face = "bold", size = 10),
    legend.text = element_text(size = 8),
    legend.key.height = unit(0.45, "cm"),
    legend.key.width = unit(0.55, "cm"),
    panel.grid.major.x = element_blank(),
    panel.grid.minor = element_blank()
  ) +
  guides(fill = guide_legend(ncol = 2, byrow = TRUE))

print(variance_plot)
Figure 3. Posterior mean percentage of total variance attributed to each model component across the 18 predictive models for grain yield (GY) and kernel weight (KW). Each stacked bar represents one model (Eta1-Eta18), averaged across repetitions, and colors identify the variance components included in the model structure. The weather kernel (W) and its associated interaction terms appear mainly in models Eta10-Eta18. Variance decomposition should be interpreted as a description of model structure and not automatically as evidence of stronger biological complementarity among information sources.

Figure 3. Posterior mean percentage of total variance attributed to each model component across the 18 predictive models for grain yield (GY) and kernel weight (KW). Each stacked bar represents one model (Eta1-Eta18), averaged across repetitions, and colors identify the variance components included in the model structure. The weather kernel (W) and its associated interaction terms appear mainly in models Eta10-Eta18. Variance decomposition should be interpreted as a description of model structure and not automatically as evidence of stronger biological complementarity among information sources.

Version Author Date
4fb14e1 WevertonGomesCosta 2026-04-06
d1beaba WevertonGomesCosta 2026-04-06
d54afd7 WevertonGomesCosta 2026-04-01
00ef3ef WevertonGomesCosta 2026-04-01
4e5af9a WevertonGomesCosta 2026-03-27
ggsave(
  filename = file.path(figure_dir, "variance_components_article.tiff"),
  plot = variance_plot,
  width = 14,
  height = 10,
  dpi = 300,
  compression = "lzw",
  bg = "white"
)

11. Interpretation Guide

This final section clarifies how the figure should be interpreted.

  • A large Environment (E) segment indicates that the categorical macro-environment effect dominates the variance partition.
  • Large genomic terms such as G, GGK, or GAK indicate that genomic similarity captures an important share of the trait variability.
  • Large phenomic terms such as P, PGK, or PAK indicate stronger contribution of the NIRS-derived similarity, but these terms should be interpreted with caution because NIRS may also reflect strong physiological response to the environment.
  • Large weather-related terms such as W, G × W, or P × W indicate that weather-derived covariates explain additional structure. These terms appear mainly in models Eta10-Eta18 and should be interpreted as complementary to the categorical environment effect, not as a replacement for E.
  • A large Residual segment indicates that important sources of variability remain unexplained by the fitted model.

In the context of this article, these variance decompositions complement the predictive analyses by showing which information sources contribute to model structure. However, a large variance share for a component should not be read automatically as evidence of stronger biological complementarity or of better predictive ability.

Stage navigation

Previous stage: Matrices
Next stage: Prediction — Part I


sessionInfo()
R version 4.5.1 (2025-06-13 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26200)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=Portuguese_Brazil.utf8  LC_CTYPE=Portuguese_Brazil.utf8   
[3] LC_MONETARY=Portuguese_Brazil.utf8 LC_NUMERIC=C                      
[5] LC_TIME=Portuguese_Brazil.utf8    

time zone: America/Sao_Paulo
tzcode source: internal

attached base packages:
[1] grid      parallel  stats     graphics  grDevices utils     datasets 
[8] methods   base     

other attached packages:
 [1] kableExtra_1.4.0  knitr_1.50        fs_1.6.6          scales_1.4.0     
 [5] ggthemes_5.2.0    doParallel_1.0.17 iterators_1.0.14  foreach_1.5.2    
 [9] BGLR_1.1.4        ggplot2_4.0.2     tibble_3.3.1      stringr_1.6.0    
[13] readr_2.2.0       tidyr_1.3.2       dplyr_1.2.0      

loaded via a namespace (and not attached):
 [1] gtable_0.3.6       xfun_0.54          bslib_0.9.0        tzdb_0.5.0        
 [5] vctrs_0.7.1        tools_4.5.1        generics_0.1.4     pkgconfig_2.0.3   
 [9] RColorBrewer_1.1-3 S7_0.2.1           lifecycle_1.0.5    truncnorm_1.0-9   
[13] compiler_4.5.1     farver_2.1.2       git2r_0.36.2       textshaping_1.0.4 
[17] codetools_0.2-20   httpuv_1.6.16      htmltools_0.5.9    sass_0.4.10       
[21] yaml_2.3.12        later_1.4.4        pillar_1.11.1      crayon_1.5.3      
[25] jquerylib_0.1.4    whisker_0.4.1      MASS_7.3-65        cachem_1.1.0      
[29] tidyselect_1.2.1   digest_0.6.39      stringi_1.8.7      purrr_1.2.1       
[33] labeling_0.4.3     rprojroot_2.1.1    fastmap_1.2.0      cli_3.6.5         
[37] magrittr_2.0.4     withr_3.0.2        promises_1.5.0     bit64_4.6.0-1     
[41] rmarkdown_2.30     bit_4.6.0          otel_0.2.0         workflowr_1.7.2   
[45] ragg_1.5.0         hms_1.1.4          evaluate_1.0.5     viridisLite_0.4.2 
[49] rlang_1.1.7        Rcpp_1.1.1         glue_1.8.0         xml2_1.5.1        
[53] svglite_2.2.2      rstudioapi_0.17.1  vroom_1.7.0        jsonlite_2.0.0    
[57] R6_2.6.1           systemfonts_1.3.1 

  1. Weverton Gomes da Costa, Postdoctoral Researcher, Department of Statistics - Universidade Federal de Viçosa, ↩︎

LS0tDQp0aXRsZTogIlZhcmlhbmNlIENvbXBvbmVudHMiDQphdXRob3I6DQogIC0gQ29zdGEsIFcuIEcuXltXZXZlcnRvbiBHb21lcyBkYSBDb3N0YSwgUG9zdGRvY3RvcmFsIFJlc2VhcmNoZXIsIERlcGFydG1lbnQgb2YgU3RhdGlzdGljcyAtIFVuaXZlcnNpZGFkZSBGZWRlcmFsIGRlIFZpw6dvc2EsIHdldmVydG9udWZ2QGdtYWlsLmNvbV0NCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCnNpdGU6IHdvcmtmbG93cjo6d2Zsb3dfc2l0ZQ0KdXJsOiBodHRwczovL3dldmVydG9uZ29tZXNjb3N0YS5naXRodWIuaW8vSW50ZWdyYXRpbmctbmlyLWdlbm9taWMta2VybmVsLw0Kb3V0cHV0Og0KICB3b3JrZmxvd3I6OndmbG93X2h0bWw6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAzDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlDQpnaXRodWItcmVwbzogV2V2ZXJ0b25Hb21lc0Nvc3RhL0ludGVncmF0aW5nLW5pci1nZW5vbWljLWtlcm5lbA0KLS0tDQoNClRoZSBkb2N1bWVudCBzdGFydHMgYnkgc2V0dGluZyBrbml0dGluZyBvcHRpb25zIHNvIHRoYXQgdGFibGVzLCBjb2RlLCBhbmQgZmlndXJlcyBhcmUgZGlzcGxheWVkIGNvbnNpc3RlbnRseSB0aHJvdWdob3V0IHRoZSB0dXRvcmlhbC4NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBUUlVFLA0KICB3YXJuaW5nID0gRkFMU0UsDQogIG1lc3NhZ2UgPSBGQUxTRSwNCiAgZmlnLmFsaWduID0gImNlbnRlciIsDQogIGZpZy53aWR0aCA9IDEyLA0KICBmaWcuaGVpZ2h0ID0gNywNCiAgZHBpID0gMzAwDQopDQpgYGANCg0KKipMYW5ndWFnZSAvIElkaW9tYToqKiBbRW5nbGlzaF0odmFyaWFuY2VfY29tcG9uZW50cy5odG1sKSB8IFtQb3J0dWd1w6pzXSh2YXJpYW5jZV9jb21wb25lbnRzX3B0Lmh0bWwpDQoNCiMjIDEuIEludHJvZHVjdGlvbg0KDQpUaGlzIHR1dG9yaWFsIHN1bW1hcml6ZXMgKip2YXJpYW5jZSBjb21wb25lbnRzKiogZm9yIHRoZSBwcmVkaWN0aXZlIG1vZGVscyB1c2VkIGluIHRoaXMgc3R1ZHkuIEluIHRoZSBjdXJyZW50IHdvcmtmbG93LCB0aGlzIGZpbGUgaXMgdXNlZCBtYWlubHkgdG8gKipjb25zb2xpZGF0ZSBhbmQgaW50ZXJwcmV0IHJlc3VsdHMgdGhhdCB3ZXJlIGFscmVhZHkgZXN0aW1hdGVkIHByZXZpb3VzbHkqKiwgYWx0aG91Z2ggaXQgc3RpbGwga2VlcHMgdGhlIGNvZGUgc3RydWN0dXJlIG5lZWRlZCB0byBmaXQgdGhlIG1vZGVscyB3aGVuIHRoYXQgc3RlcCBtdXN0IGJlIHJlcnVuLg0KDQpUaGUgZ29hbCBpcyB0byBxdWFudGlmeSBob3cgbXVjaCBvZiB0aGUgdG90YWwgcGhlbm90eXBpYyB2YXJpYW5jZSBpcyBhdHRyaWJ1dGVkIHRvIHRoZSBtYWluIGVmZmVjdHMgYW5kIGludGVyYWN0aW9uIHRlcm1zIGluY2x1ZGVkIGluIGVhY2ggbW9kZWwuIEluIHByYWN0aWNhbCB0ZXJtcywgdGhpcyBzY3JpcHQgaGVscHMgYW5zd2VyIHF1ZXN0aW9ucyBzdWNoIGFzOg0KDQotIEhvdyBtdWNoIHZhcmlhbmNlIGlzIGFzc29jaWF0ZWQgd2l0aCB0aGUgKipjYXRlZ29yaWNhbCBlbnZpcm9ubWVudCBlZmZlY3QgKEUpKio/DQotIEhvdyBtdWNoIGlzIGNhcHR1cmVkIGJ5IHRoZSAqKmdlbm9taWMga2VybmVscyoqPw0KLSBIb3cgbXVjaCBpcyBjYXB0dXJlZCBieSB0aGUgKipwaGVub21pYy9OSVJTIGtlcm5lbHMgKFApKio/DQotIEhvdyBtdWNoIGlzIGFzc29jaWF0ZWQgd2l0aCB0aGUgKip3ZWF0aGVyIGtlcm5lbCAoVykqKiwgd2hpY2ggaXMgdXNlZCBtYWlubHkgaW4gbW9kZWxzICoqRXRhMTAtRXRhMTgqKj8NCi0gSG93IHJlbGV2YW50IGFyZSB0aGUgaW50ZXJhY3Rpb24gdGVybXMgc3VjaCBhcyAqKkcgw5cgRSoqLCAqKkcgw5cgVyoqLCAqKlAgw5cgRSoqLCBhbmQgKipQIMOXIFcqKj8NCg0KVGhpcyBzY3JpcHQgaXMgb3JnYW5pemVkIGludG8gZm91ciBzdGFnZXM6DQoNCjEuICoqTG9hZCBrZXJuZWxzIGFuZCBwaGVub3R5cGljIGRhdGEqKiBwcm9kdWNlZCBpbiBgbWF0cml6ZXMuUm1kYC4NCjIuICoqRGVmaW5lIHRoZSAxOCB2YXJpYW5jZS1jb21wb25lbnQgbW9kZWxzKiouDQozLiAqKlJlYWQgb3IgcmVydW4gdGhlIEJHTFIgb3V0cHV0cyoqLCBkZXBlbmRpbmcgb24gd2hldGhlciB0aGUgc2F2ZWQgcG9zdGVyaW9yIHN1bW1hcmllcyBhbHJlYWR5IGV4aXN0Lg0KNC4gKipQcm9jZXNzIGFuZCB2aXN1YWxpemUgdGhlIHZhcmlhbmNlIGRlY29tcG9zaXRpb24qKiBmb3IgR3JhaW4gWWllbGQgKEdZKSBhbmQgS2VybmVsIFdlaWdodCAoS1cpLg0KDQpBIGhpZ2ggdmFyaWFuY2Ugc2hhcmUgZm9yIGEgZ2l2ZW4gc291cmNlIHNob3VsZCBub3QgYmUgaW50ZXJwcmV0ZWQgYXV0b21hdGljYWxseSBhcyBldmlkZW5jZSBvZiBiaW9sb2dpY2FsIGNvbXBsZW1lbnRhcml0eSBvciBiZXR0ZXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4gVGhpcyBwb2ludCBpcyBlc3BlY2lhbGx5IGltcG9ydGFudCBmb3IgKipQKiosIGJlY2F1c2UgTklSUy1iYXNlZCBzaW1pbGFyaXR5IG1heSByZWZsZWN0IHN0cm9uZyBwaHlzaW9sb2dpY2FsIHJlc3BvbnNlIHRvIHRoZSBlbnZpcm9ubWVudCBpbiBhZGRpdGlvbiB0byBhbnkgc3RhYmxlIGJpb2xvZ2ljYWwgc2lnbmFsLCBhbmQgZm9yICoqVyoqLCBiZWNhdXNlIHRoZSB3ZWF0aGVyIGtlcm5lbCBjb21wbGVtZW50cyBidXQgZG9lcyBub3QgcmVwbGFjZSB0aGUgY2F0ZWdvcmljYWwgZW52aXJvbm1lbnQgZWZmZWN0Lg0KDQojIyBJbnB1dHMsIG91dHB1dHMgYW5kIG5leHQgc3RlcA0KDQoqKklucHV0cyBvZiB0aGlzIHN0ZXAqKg0KLSBtYXRyaWNlcyBhbmQga2VybmVscyBnZW5lcmF0ZWQgYnkgYG1hdHJpemVzLlJtZGANCi0gdGhlIHJlZHVjZWQgY2F0YWxvZyBvZiAxOCBtb2RlbHMgdXNlZCBpbiB0aGUgcHJvamVjdA0KDQoqKk91dHB1dHMgb2YgdGhpcyBzdGVwKioNCi0gcmF3IHZhcmlhbmNlLWNvbXBvbmVudCByZXN1bHRzDQotIHByb2Nlc3NlZCB2YXJpYW5jZS1jb21wb25lbnQgc3VtbWFyaWVzDQotIHBlcmNlbnRhZ2UgdGFibGVzIGFuZCBkaWFnbm9zdGljIGZpZ3VyZXMNCg0KKipOZXh0IHN0ZXAqKg0KLSB0aGlzIHN0YWdlIGNvbXBsZW1lbnRzIHRoZSBzdHJ1Y3R1cmFsIGludGVycHJldGF0aW9uIG9mIHRoZSBtb2RlbHMgYW5kIHNob3VsZCBiZSByZWFkIHRvZ2V0aGVyIHdpdGggdGhlIHByZWRpY3RpdmUgcmVzdWx0cyBnZW5lcmF0ZWQgbGF0ZXIgaW4gYGFuYWx5c2lzX3ByZWRpY3Rpb24uUm1kYC4NCg0KIyMgMi4gUiBFbnZpcm9ubWVudCBhbmQgUmVxdWlyZWQgSW5wdXRzDQoNCkZpcnN0LCB3ZSBsb2FkIHRoZSBwYWNrYWdlcyB1c2VkIHRvIG1hbmlwdWxhdGUgdGhlIGRhdGEsIGZpdCB0aGUgQmF5ZXNpYW4gbW9kZWxzLCBhbmQgZ2VuZXJhdGUgdGhlIGZpbmFsIGFydGljbGUtcXVhbGl0eSBmaWd1cmVzLg0KDQpUaGUgbmV4dCBzdGVwIGxvYWRzIHRoZSBwYWNrYWdlcyBuZWVkZWQgZm9yIHJlYWRpbmcgdGhlIHNhdmVkIGFydGlmYWN0cywgcmVzaGFwaW5nIHRoZSByZXN1bHRzLCBwbG90dGluZyBzdW1tYXJpZXMsIGFuZCBydW5uaW5nIHRoZSBoZWF2eSBlc3RpbWF0aW9uIHN0YWdlIHdoZW4gcmVxdWlyZWQuDQoNCmBgYHtyIGxvYWQtcGFja2FnZXN9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KHRpYmJsZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoQkdMUikNCmxpYnJhcnkocGFyYWxsZWwpDQpsaWJyYXJ5KGRvUGFyYWxsZWwpDQpsaWJyYXJ5KGZvcmVhY2gpDQpsaWJyYXJ5KGdndGhlbWVzKQ0KbGlicmFyeShzY2FsZXMpDQpsaWJyYXJ5KGZzKQ0KbGlicmFyeShncmlkKQ0KbGlicmFyeShrbml0cikNCmxpYnJhcnkoa2FibGVFeHRyYSkNCmBgYA0KDQojIyMgMi4xLiBGaWxlIHBhdGhzDQoNCkFsbCBrZXJuZWxzIGFuZCBwaGVub3R5cGljIG9iamVjdHMgYXJlIGV4cGVjdGVkIHRvIGJlIGF2YWlsYWJsZSBpbiBgb3V0cHV0L01hdHJpemVzL2AsIHdoaWNoIGlzIHRoZSBvdXRwdXQgZGlyZWN0b3J5IGNyZWF0ZWQgaW4gYG1hdHJpemVzLlJtZGAuIFRoZSB2YXJpYW5jZS1jb21wb25lbnQgcmVzdWx0cyBnZW5lcmF0ZWQgaGVyZSBhcmUgc3RvcmVkIGluIGBvdXRwdXQvdmFyaWFuY2VfY29tcG9uZW50cy9gLg0KDQpUaGUgZm9sbG93aW5nIGxpbmVzIGRlZmluZSB0aGUgcHJvamVjdCBkaXJlY3RvcmllcyB1c2VkIHRvIGZpbmQgbWF0cmljZXMsIHN0b3JlIG91dHB1dHMsIGFuZCBvcmdhbml6ZSB0aGUgc2F2ZWQgdmFyaWFuY2UtY29tcG9uZW50IGZpbGVzLg0KDQpgYGB7ciBkZWZpbmUtcGF0aHN9DQprZXJuZWxfZGlyIDwtICJvdXRwdXQvTWF0cml6ZXMiDQpyZXN1bHRzX2RpciA8LSAib3V0cHV0L3ZhcmlhbmNlX2NvbXBvbmVudHMiDQpmaWd1cmVfZGlyIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwgImZpZ3VyZXMiKQ0KYmdscl9ydW5zX2RpciA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJiZ2xyX3J1bnMiKQ0KDQpkaXIuY3JlYXRlKHJlc3VsdHNfZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkNCmRpci5jcmVhdGUoYmdscl9ydW5zX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpDQpkaXIuY3JlYXRlKGZpZ3VyZV9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQ0KYGBgDQoNCiMjIyAyLjIuIElucHV0IGNoZWNrcw0KDQpCZWZvcmUgcmVhZGluZyB0aGUgZmlsZXMsIHdlIGNoZWNrIHdoZXRoZXIgdGhlIGV4cGVjdGVkIGAucmRzYCBvYmplY3RzIGFyZSBwcmVzZW50IGluIHRoZSBtYXRyaWNlcyBvdXRwdXQgZm9sZGVyLiBUaGlzIG1ha2VzIHRoZSB0dXRvcmlhbCBlYXNpZXIgdG8gZm9sbG93IGJlY2F1c2UgdGhlIHNjcmlwdCBzdG9wcyBpbW1lZGlhdGVseSBpZiBhIHJlcXVpcmVkIG9iamVjdCBpcyBtaXNzaW5nLg0KDQpCZWZvcmUgbW92aW5nIGludG8gbW9kZWwgZGVmaW5pdGlvbiBvciByZXN1bHQgcHJvY2Vzc2luZywgdGhlIHNjcmlwdCBjaGVja3MgdGhhdCB0aGUgcmVxdWlyZWQgaW5wdXQgZmlsZXMgYW5kIGRpcmVjdG9yaWVzIGFyZSBhdmFpbGFibGUuDQoNCmBgYHtyIGlucHV0LWNoZWNrc30NCnJlcXVpcmVkX2ZpbGVzIDwtIGMoDQogICJaRy5yZHMiLCAiWlAucmRzIiwgIlpFLnJkcyIsICJaRVpFLnJkcyIsICJaVy5yZHMiLA0KICAiWkdaRS5yZHMiLCAiWlBaRS5yZHMiLCAiWkdaVy5yZHMiLCAiWlBaVy5yZHMiLA0KICAiR0dLLnJkcyIsICJQR0sucmRzIiwgIkdBSy5yZHMiLCAiUEFLLnJkcyIsDQogICJHR0tFLnJkcyIsICJQR0tFLnJkcyIsICJHR0tXLnJkcyIsICJQR0tXLnJkcyIsDQogICJHQUtFLnJkcyIsICJQQUtFLnJkcyIsICJHQUtXLnJkcyIsICJQQUtXLnJkcyIsDQogICJQaGVuby5yZHMiLCAiUGVkaWdyZWUucmRzIg0KKQ0KDQptaXNzaW5nX2ZpbGVzIDwtIHJlcXVpcmVkX2ZpbGVzWyFmaWxlLmV4aXN0cyhmaWxlLnBhdGgoa2VybmVsX2RpciwgcmVxdWlyZWRfZmlsZXMpKV0NCg0KaWYgKGxlbmd0aChtaXNzaW5nX2ZpbGVzKSA+IDApIHsNCiAgc3RvcCgNCiAgICAiVGhlIGZvbGxvd2luZyByZXF1aXJlZCBmaWxlcyB3ZXJlIG5vdCBmb3VuZCBpbiBvdXRwdXQvTWF0cml6ZXMvOiAiLA0KICAgIHBhc3RlKG1pc3NpbmdfZmlsZXMsIGNvbGxhcHNlID0gIiwgIiksDQogICAgIi4gUnVuIG1hdHJpemVzLlJtZCBmaXJzdC4iDQogICkNCn0NCmBgYA0KDQojIyMgMi4zLiBMYWJlbHMgdXNlZCBpbiB0aGUgZmluYWwgdGFibGVzIGFuZCBmaWd1cmVzDQoNClRoZXNlIHZlY3RvcnMgZGVmaW5lIGhvdyB0aGUgdmFyaWFuY2UgY29tcG9uZW50cyB3aWxsIGFwcGVhciBpbiB0aGUgZmluYWwgb3V0cHV0cy4gS2VlcGluZyB0aGVzZSBsYWJlbHMgdmlzaWJsZSBpbiB0aGUgbWFpbiBzY3JpcHQgaGVscHMgdGhlIHJlYWRlciB1bmRlcnN0YW5kIGhvdyBpbnRlcm5hbCBtb2RlbCBuYW1lcyBhcmUgdHJhbnNsYXRlZCBpbnRvIGFydGljbGUtcmVhZHkgbGFiZWxzLg0KDQpIZXJlIHRoZSBzY3JpcHQgZGVmaW5lcyB0aGUgbGFiZWxzIGFuZCBuYW1pbmcgY29udmVudGlvbnMgdGhhdCB3aWxsIGJlIHJldXNlZCBpbiB0YWJsZXMsIG91dHB1dCBmaWxlcywgYW5kIGZpZ3VyZXMuDQoNCmBgYHtyIGxhYmVscy1mb3Itb3V0cHV0fQ0KY29tcG9uZW50X2Rpc3BsYXkgPC0gYygNCiAgRSA9ICJFbnZpcm9ubWVudCAoRSkiLA0KICBHID0gIkdlbm9taWMgbGluZWFyIChHKSIsDQogIFAgPSAiUGhlbm9taWMgbGluZWFyIChQKSIsDQogIFcgPSAiV2VhdGhlciBrZXJuZWwgKFcpIiwNCiAgR0UgPSAiRyDDlyBFIiwNCiAgUEUgPSAiUCDDlyBFIiwNCiAgR1cgPSAiRyDDlyBXIiwNCiAgUFcgPSAiUCDDlyBXIiwNCiAgR0dLID0gIkdlbm9taWMgR2F1c3NpYW4gKEdHSykiLA0KICBQR0sgPSAiUGhlbm9taWMgR2F1c3NpYW4gKFBHSykiLA0KICBHQUsgPSAiR2Vub21pYyBhcmMtY29zaW5lIChHQUspIiwNCiAgUEFLID0gIlBoZW5vbWljIGFyYy1jb3NpbmUgKFBBSykiLA0KICBHR0tFID0gIkdHSyDDlyBFIiwNCiAgUEdLRSA9ICJQR0sgw5cgRSIsDQogIEdBS0UgPSAiR0FLIMOXIEUiLA0KICBQQUtFID0gIlBBSyDDlyBFIiwNCiAgR0dLVyA9ICJHR0sgw5cgVyIsDQogIFBHS1cgPSAiUEdLIMOXIFciLA0KICBHQUtXID0gIkdBSyDDlyBXIiwNCiAgUEFLVyA9ICJQQUsgw5cgVyIsDQogIFJlc2lkdWFsID0gIlJlc2lkdWFsIg0KKQ0KDQpjb21wb25lbnRfb3JkZXIgPC0gYygNCiAgIkVudmlyb25tZW50IChFKSIsDQogICJHZW5vbWljIGxpbmVhciAoRykiLA0KICAiUGhlbm9taWMgbGluZWFyIChQKSIsDQogICJXZWF0aGVyIGtlcm5lbCAoVykiLA0KICAiRyDDlyBFIiwNCiAgIlAgw5cgRSIsDQogICJHIMOXIFciLA0KICAiUCDDlyBXIiwNCiAgIkdlbm9taWMgR2F1c3NpYW4gKEdHSykiLA0KICAiUGhlbm9taWMgR2F1c3NpYW4gKFBHSykiLA0KICAiR2Vub21pYyBhcmMtY29zaW5lIChHQUspIiwNCiAgIlBoZW5vbWljIGFyYy1jb3NpbmUgKFBBSykiLA0KICAiR0dLIMOXIEUiLA0KICAiUEdLIMOXIEUiLA0KICAiR0FLIMOXIEUiLA0KICAiUEFLIMOXIEUiLA0KICAiR0dLIMOXIFciLA0KICAiUEdLIMOXIFciLA0KICAiR0FLIMOXIFciLA0KICAiUEFLVyDDlyBXIiwNCiAgIlJlc2lkdWFsIg0KKQ0KDQpjb21wb25lbnRfb3JkZXJbY29tcG9uZW50X29yZGVyID09ICJQQUtXIMOXIFciXSA8LSAiUEFLIMOXIFciDQoNCm5vcm1hbGl6ZV9jb21wb25lbnRfbmFtZSA8LSBmdW5jdGlvbihjb21wb25lbnRfbmFtZSwgbW9kZWxfdmFsdWUgPSBOQV9pbnRlZ2VyXykgew0KICBjb21wb25lbnRfbmFtZSA8LSBhcy5jaGFyYWN0ZXIoY29tcG9uZW50X25hbWUpDQoNCiAgaWYgKGxlbmd0aChtb2RlbF92YWx1ZSkgPT0gMUwgJiYgbGVuZ3RoKGNvbXBvbmVudF9uYW1lKSA+IDFMKSB7DQogICAgbW9kZWxfdmFsdWUgPC0gcmVwKG1vZGVsX3ZhbHVlLCBsZW5ndGgoY29tcG9uZW50X25hbWUpKQ0KICB9DQoNCiAgaWYgKGxlbmd0aChtb2RlbF92YWx1ZSkgIT0gbGVuZ3RoKGNvbXBvbmVudF9uYW1lKSkgew0KICAgIHN0b3AoIm5vcm1hbGl6ZV9jb21wb25lbnRfbmFtZSgpIHJlcXVpcmVzICdjb21wb25lbnRfbmFtZScgYW5kICdtb2RlbF92YWx1ZScgdG8gaGF2ZSB0aGUgc2FtZSBsZW5ndGgsIG9yIGEgc2NhbGFyICdtb2RlbF92YWx1ZScuIikNCiAgfQ0KDQogIHZhcHBseShzZXFfYWxvbmcoY29tcG9uZW50X25hbWUpLCBmdW5jdGlvbihpKSB7DQogICAgY3VycmVudF9jb21wb25lbnQgPC0gY29tcG9uZW50X25hbWVbaV0NCiAgICBjdXJyZW50X21vZGVsIDwtIG1vZGVsX3ZhbHVlW2ldDQoNCiAgICBpZiAoaXMuZmFjdG9yKGN1cnJlbnRfbW9kZWwpKSB7DQogICAgICBjdXJyZW50X21vZGVsIDwtIGFzLmNoYXJhY3RlcihjdXJyZW50X21vZGVsKQ0KICAgIH0NCg0KICAgIGlmIChpcy5jaGFyYWN0ZXIoY3VycmVudF9tb2RlbCkpIHsNCiAgICAgIGN1cnJlbnRfbW9kZWwgPC0gc3VwcHJlc3NXYXJuaW5ncyhhcy5pbnRlZ2VyKHN1YigiXkV0YSIsICIiLCBjdXJyZW50X21vZGVsKSkpDQogICAgfQ0KDQogICAgaWYgKGxlbmd0aChjdXJyZW50X2NvbXBvbmVudCkgPT0gMCB8fCBpcy5uYShjdXJyZW50X2NvbXBvbmVudCkgfHwgIW56Y2hhcihjdXJyZW50X2NvbXBvbmVudCkpIHsNCiAgICAgIHJldHVybihjdXJyZW50X2NvbXBvbmVudCkNCiAgICB9DQoNCiAgICBpZiAobGVuZ3RoKGN1cnJlbnRfbW9kZWwpID09IDFMICYmICFpcy5uYShjdXJyZW50X21vZGVsKSAmJiBncmVwbCgiXkVUQV9bMC05XSskIiwgY3VycmVudF9jb21wb25lbnQpKSB7DQogICAgICBlZmZlY3RfaW5kZXggPC0gc3VwcHJlc3NXYXJuaW5ncyhhcy5pbnRlZ2VyKHN1YigiXkVUQV8iLCAiIiwgY3VycmVudF9jb21wb25lbnQpKSkNCiAgICAgIGlmICghaXMubmEoZWZmZWN0X2luZGV4KSAmJiBjdXJyZW50X21vZGVsID49IDEgJiYgY3VycmVudF9tb2RlbCA8PSBsZW5ndGgobW9kZWxzKSkgew0KICAgICAgICBtb2RlbF9uYW1lcyA8LSBuYW1lcyhtb2RlbHNbW2N1cnJlbnRfbW9kZWxdXSkNCiAgICAgICAgaWYgKCFpcy5udWxsKG1vZGVsX25hbWVzKSAmJiBlZmZlY3RfaW5kZXggPj0gMSAmJiBlZmZlY3RfaW5kZXggPD0gbGVuZ3RoKG1vZGVsX25hbWVzKSkgew0KICAgICAgICAgIGN1cnJlbnRfY29tcG9uZW50IDwtIG1vZGVsX25hbWVzW2VmZmVjdF9pbmRleF0NCiAgICAgICAgfQ0KICAgICAgfQ0KICAgIH0NCg0KICAgIGN1cnJlbnRfY29tcG9uZW50IDwtIHN1YigiXyhCUlJ8UktIUykkIiwgIiIsIGN1cnJlbnRfY29tcG9uZW50KQ0KICAgIGN1cnJlbnRfY29tcG9uZW50DQogIH0sIGNoYXJhY3RlcigxKSkNCn0NCmBgYA0KDQojIyAzLiBMb2FkaW5nIHRoZSBLZXJuZWxzIGFuZCBQaGVub3R5cGljIE9iamVjdHMNCg0KVGhpcyBzZWN0aW9uIGxvYWRzIHRoZSBtYXRyaWNlcyBnZW5lcmF0ZWQgaW4gYG1hdHJpemVzLlJtZGAuIFRoZXNlIG9iamVjdHMgYXJlIHRoZSBjb3JlIGlucHV0cyBmb3IgdGhlIHZhcmlhbmNlLWNvbXBvbmVudCBhbmFseXNpcy4NCg0KQXQgdGhpcyBwb2ludCwgdGhlIHNjcmlwdCByZWFkcyB0aGUgbWF0cmljZXMgYW5kIGtlcm5lbHMgZ2VuZXJhdGVkIGluIHRoZSBwcmV2aW91cyBtb2R1bGUgc28gdGhlIHZhcmlhbmNlLWNvbXBvbmVudCBtb2RlbHMgdXNlIHRoZSBzYW1lIGFsaWduZWQgaW5wdXRzIGFzIHRoZSBwcmVkaWN0aW9uIHN0YWdlLg0KDQpgYGB7ciBsb2FkLW1hdHJpY2VzfQ0KWkcgICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlpHLnJkcyIpKQ0KWlAgICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlpQLnJkcyIpKQ0KWkUgICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlpFLnJkcyIpKQ0KWkVaRSAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlpFWkUucmRzIikpDQpaVyAgICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiWlcucmRzIikpDQoNClpHWkUgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJaR1pFLnJkcyIpKQ0KWlBaRSAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlpQWkUucmRzIikpDQpaR1pXICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiWkdaVy5yZHMiKSkNClpQWlcgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJaUFpXLnJkcyIpKQ0KDQpHR0sgICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiR0dLLnJkcyIpKQ0KUEdLICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlBHSy5yZHMiKSkNCkdBSyAgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJHQUsucmRzIikpDQpQQUsgICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiUEFLLnJkcyIpKQ0KDQpHR0tFICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiR0dLRS5yZHMiKSkNClBHS0UgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJQR0tFLnJkcyIpKQ0KR0dLVyAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIkdHS1cucmRzIikpDQpQR0tXICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiUEdLVy5yZHMiKSkNCg0KR0FLRSAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIkdBS0UucmRzIikpDQpQQUtFICA8LSByZWFkUkRTKGZpbGUucGF0aChrZXJuZWxfZGlyLCAiUEFLRS5yZHMiKSkNCkdBS1cgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJHQUtXLnJkcyIpKQ0KUEFLVyAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoa2VybmVsX2RpciwgIlBBS1cucmRzIikpDQoNClBoZW5vICAgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJQaGVuby5yZHMiKSkNClBlZGlncmVlIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGtlcm5lbF9kaXIsICJQZWRpZ3JlZS5yZHMiKSkNCg0Kbl9vYnMgPC0gbnJvdyhQaGVubykNCmNhdCgiTnVtYmVyIG9mIHBoZW5vdHlwaWMgb2JzZXJ2YXRpb25zOiIsIG5fb2JzLCAiXG4iKQ0KY2F0KCJOdW1iZXIgb2YgcGVkaWdyZWVzOiIsIGxlbmd0aChQZWRpZ3JlZSksICJcbiIpDQpgYGANCg0KIyMjIDMuMS4gQWxpZ25tZW50IGRpYWdub3N0aWNzDQoNCkJlZm9yZSBmaXR0aW5nIHRoZSBtb2RlbHMsIGl0IGlzIGltcG9ydGFudCB0byB2ZXJpZnkgdGhhdCB0aGUga2VybmVscyBhcmUgc3F1YXJlIGFuZCBhbGlnbmVkIHRvIHRoZSBzYW1lIG9ic2VydmF0aW9uIG9yZGVyIHVzZWQgaW4gYFBoZW5vYC4gSW4gYSB0dXRvcmlhbCBjb250ZXh0LCB0aGlzIGlzIGFuIGltcG9ydGFudCBjaGVjayBiZWNhdXNlIG1hbnkgZG93bnN0cmVhbSBlcnJvcnMgYXJlIGNhdXNlZCBieSBtaXNtYXRjaGVkIHJvdyBhbmQgY29sdW1uIG9yZGVycy4NCg0KVGhlIGxvYWRlZCBrZXJuZWxzIGFyZSB0aGVuIGNoZWNrZWQgZm9yIGNvbXBhdGliaWxpdHkgaW4gZGltZW5zaW9uIGFuZCBvcmRlcmluZyBiZWZvcmUgdGhleSBhcmUgY29tYmluZWQgaW4gdGhlIEJheWVzaWFuIG1vZGVscy4NCg0KYGBge3Iga2VybmVsLWFsaWdubWVudC1jaGVja30NCnJlZmVyZW5jZV9pZHMgPC0gUGhlbm8kT2JzSUQNCg0KWkUgPC0gWkVbbWF0Y2gocmVmZXJlbmNlX2lkcywgcm93bmFtZXMoWkUpKSwgLCBkcm9wID0gRkFMU0VdDQoNClpHICAgPC0gWkdbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KWlAgICA8LSBaUFtyZWZlcmVuY2VfaWRzLCByZWZlcmVuY2VfaWRzLCBkcm9wID0gRkFMU0VdDQpaRVpFIDwtIFpFWkVbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KWlcgICA8LSBaV1tyZWZlcmVuY2VfaWRzLCByZWZlcmVuY2VfaWRzLCBkcm9wID0gRkFMU0VdDQpaR1pFIDwtIFpHWkVbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KWlBaRSA8LSBaUFpFW3JlZmVyZW5jZV9pZHMsIHJlZmVyZW5jZV9pZHMsIGRyb3AgPSBGQUxTRV0NClpHWlcgPC0gWkdaV1tyZWZlcmVuY2VfaWRzLCByZWZlcmVuY2VfaWRzLCBkcm9wID0gRkFMU0VdDQpaUFpXIDwtIFpQWldbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KR0dLICA8LSBHR0tbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KUEdLICA8LSBQR0tbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KR0FLICA8LSBHQUtbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KUEFLICA8LSBQQUtbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KR0dLRSA8LSBHR0tFW3JlZmVyZW5jZV9pZHMsIHJlZmVyZW5jZV9pZHMsIGRyb3AgPSBGQUxTRV0NClBHS0UgPC0gUEdLRVtyZWZlcmVuY2VfaWRzLCByZWZlcmVuY2VfaWRzLCBkcm9wID0gRkFMU0VdDQpHR0tXIDwtIEdHS1dbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KUEdLVyA8LSBQR0tXW3JlZmVyZW5jZV9pZHMsIHJlZmVyZW5jZV9pZHMsIGRyb3AgPSBGQUxTRV0NCkdBS0UgPC0gR0FLRVtyZWZlcmVuY2VfaWRzLCByZWZlcmVuY2VfaWRzLCBkcm9wID0gRkFMU0VdDQpQQUtFIDwtIFBBS0VbcmVmZXJlbmNlX2lkcywgcmVmZXJlbmNlX2lkcywgZHJvcCA9IEZBTFNFXQ0KR0FLVyA8LSBHQUtXW3JlZmVyZW5jZV9pZHMsIHJlZmVyZW5jZV9pZHMsIGRyb3AgPSBGQUxTRV0NClBBS1cgPC0gUEFLV1tyZWZlcmVuY2VfaWRzLCByZWZlcmVuY2VfaWRzLCBkcm9wID0gRkFMU0VdDQoNCmtlcm5lbF9pbnZlbnRvcnkgPC0gdGliYmxlKA0KICBrZXJuZWwgPSBjKCJaRyIsICJaUCIsICJaRVpFIiwgIlpXIiwgIkdHSyIsICJQR0siLCAiR0FLIiwgIlBBSyIpLA0KICBuX3Jvd3MgPSBjKG5yb3coWkcpLCBucm93KFpQKSwgbnJvdyhaRVpFKSwgbnJvdyhaVyksIG5yb3coR0dLKSwgbnJvdyhQR0spLCBucm93KEdBSyksIG5yb3coUEFLKSksDQogIG5fY29scyA9IGMobmNvbChaRyksIG5jb2woWlApLCBuY29sKFpFWkUpLCBuY29sKFpXKSwgbmNvbChHR0spLCBuY29sKFBHSyksIG5jb2woR0FLKSwgbmNvbChQQUspKSwNCiAgaXNfc3F1YXJlID0gYygNCiAgICBucm93KFpHKSA9PSBuY29sKFpHKSwNCiAgICBucm93KFpQKSA9PSBuY29sKFpQKSwNCiAgICBucm93KFpFWkUpID09IG5jb2woWkVaRSksDQogICAgbnJvdyhaVykgPT0gbmNvbChaVyksDQogICAgbnJvdyhHR0spID09IG5jb2woR0dLKSwNCiAgICBucm93KFBHSykgPT0gbmNvbChQR0spLA0KICAgIG5yb3coR0FLKSA9PSBuY29sKEdBSyksDQogICAgbnJvdyhQQUspID09IG5jb2woUEFLKQ0KICApLA0KICBzeW1tZXRyaWNfbmFtZXMgPSBjKA0KICAgIGlkZW50aWNhbChyb3duYW1lcyhaRyksIGNvbG5hbWVzKFpHKSksDQogICAgaWRlbnRpY2FsKHJvd25hbWVzKFpQKSwgY29sbmFtZXMoWlApKSwNCiAgICBpZGVudGljYWwocm93bmFtZXMoWkVaRSksIGNvbG5hbWVzKFpFWkUpKSwNCiAgICBpZGVudGljYWwocm93bmFtZXMoWlcpLCBjb2xuYW1lcyhaVykpLA0KICAgIGlkZW50aWNhbChyb3duYW1lcyhHR0spLCBjb2xuYW1lcyhHR0spKSwNCiAgICBpZGVudGljYWwocm93bmFtZXMoUEdLKSwgY29sbmFtZXMoUEdLKSksDQogICAgaWRlbnRpY2FsKHJvd25hbWVzKEdBSyksIGNvbG5hbWVzKEdBSykpLA0KICAgIGlkZW50aWNhbChyb3duYW1lcyhQQUspLCBjb2xuYW1lcyhQQUspKQ0KICApLA0KICBhbGlnbmVkX3dpdGhfcmVmZXJlbmNlID0gYygNCiAgICBpZGVudGljYWwocm93bmFtZXMoWkcpLCByZWZlcmVuY2VfaWRzKSwNCiAgICBpZGVudGljYWwocm93bmFtZXMoWlApLCByZWZlcmVuY2VfaWRzKSwNCiAgICBpZGVudGljYWwocm93bmFtZXMoWkVaRSksIHJlZmVyZW5jZV9pZHMpLA0KICAgIGlkZW50aWNhbChyb3duYW1lcyhaVyksIHJlZmVyZW5jZV9pZHMpLA0KICAgIGlkZW50aWNhbChyb3duYW1lcyhHR0spLCByZWZlcmVuY2VfaWRzKSwNCiAgICBpZGVudGljYWwocm93bmFtZXMoUEdLKSwgcmVmZXJlbmNlX2lkcyksDQogICAgaWRlbnRpY2FsKHJvd25hbWVzKEdBSyksIHJlZmVyZW5jZV9pZHMpLA0KICAgIGlkZW50aWNhbChyb3duYW1lcyhQQUspLCByZWZlcmVuY2VfaWRzKQ0KICApDQopDQoNCmtlcm5lbF9pbnZlbnRvcnkgJT4lDQogIGRwbHlyOjptdXRhdGUoDQogICAgbl9yb3dzID0gZm9ybWF0KG5fcm93cywgYmlnLm1hcmsgPSAiLCIpLA0KICAgIG5fY29scyA9IGZvcm1hdChuX2NvbHMsIGJpZy5tYXJrID0gIiwiKQ0KICApICU+JQ0KICBrbml0cjo6a2FibGUoDQogICAgY2FwdGlvbiA9ICJEaWFnbm9zdGljIHN1bW1hcnkgb2YgdGhlIG1haW4ga2VybmVscyB1c2VkIGluIHRoZSB2YXJpYW5jZS1jb21wb25lbnQgYW5hbHlzaXMgYWZ0ZXIgZXhwbGljaXQgYWxpZ25tZW50IHRvIHRoZSBwaGVub3R5cGljIG9ic2VydmF0aW9uIG9yZGVyLiIsDQogICAgYWxpZ24gPSAibGNjY2NjYyINCiAgKSAlPiUNCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygNCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwNCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsDQogICAgcG9zaXRpb24gPSAibGVmdCINCiAgKSAlPiUNCiAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjI2MHB4IikNCmBgYA0KDQojIyA0LiBEZWZpbml0aW9uIG9mIHRoZSAxOCBQcmVkaWN0aXZlIE1vZGVscw0KDQpFYWNoIG1vZGVsIGlzIGRlZmluZWQgYXMgYSBsaXN0IGluIHRoZSBmb3JtYXQgcmVxdWlyZWQgYnkgdGhlIGBFVEFgIGFyZ3VtZW50IG9mIGBCR0xSYC4gVGhlIGVudmlyb25tZW50IHRlcm0gKGBFYCkgaXMgaW5jbHVkZWQgYXMgYSBmaXhlZC1lZmZlY3QgZGVzaWduIG1hdHJpeCB3aXRoIGBtb2RlbCA9ICJCUlIiYCwgd2hpbGUgdGhlIGtlcm5lbHMgYXJlIGZpdHRlZCBhcyByYW5kb20gZWZmZWN0cyB3aXRoIGBtb2RlbCA9ICJSS0hTImAuDQoNClRoZSByZWR1Y2VkIHNldCBvZiAxOCBCYXllc2lhbiBtdWx0aS1rZXJuZWwgbW9kZWxzIGlzIGRlZmluZWQgYmVsb3cgaW4gdGhlIGZvcm0gcmVxdWlyZWQgZm9yIHRoZSB2YXJpYW5jZS1jb21wb25lbnQgd29ya2Zsb3cuDQoNCmBgYHtyIGRlZmluZS1tb2RlbHN9DQptb2RlbHMgPC0gbGlzdCgpDQoNCm1vZGVsc1tbMV1dIDwtIGxpc3QoDQogIEUgID0gbGlzdChYID0gWkUsIG1vZGVsID0gIkJSUiIpLA0KICBHICA9IGxpc3QoSyA9IFpHLCBtb2RlbCA9ICJSS0hTIiksDQogIEdFID0gbGlzdChLID0gWkdaRSwgbW9kZWwgPSAiUktIUyIpDQopDQoNCm1vZGVsc1tbMl1dIDwtIGxpc3QoDQogIEUgID0gbGlzdChYID0gWkUsIG1vZGVsID0gIkJSUiIpLA0KICBQICA9IGxpc3QoSyA9IFpQLCBtb2RlbCA9ICJSS0hTIiksDQogIFBFID0gbGlzdChLID0gWlBaRSwgbW9kZWwgPSAiUktIUyIpDQopDQoNCm1vZGVsc1tbM11dIDwtIGxpc3QoDQogIEUgICAgPSBsaXN0KFggPSBaRSwgbW9kZWwgPSAiQlJSIiksDQogIEdHSyAgPSBsaXN0KEsgPSBHR0ssIG1vZGVsID0gIlJLSFMiKSwNCiAgR0dLRSA9IGxpc3QoSyA9IEdHS0UsIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzRdXSA8LSBsaXN0KA0KICBFICAgID0gbGlzdChYID0gWkUsIG1vZGVsID0gIkJSUiIpLA0KICBQR0sgID0gbGlzdChLID0gUEdLLCBtb2RlbCA9ICJSS0hTIiksDQogIFBHS0UgPSBsaXN0KEsgPSBQR0tFLCBtb2RlbCA9ICJSS0hTIikNCikNCg0KbW9kZWxzW1s1XV0gPC0gbGlzdCgNCiAgRSAgICA9IGxpc3QoWCA9IFpFLCBtb2RlbCA9ICJCUlIiKSwNCiAgR0FLICA9IGxpc3QoSyA9IEdBSywgbW9kZWwgPSAiUktIUyIpLA0KICBHQUtFID0gbGlzdChLID0gR0FLRSwgbW9kZWwgPSAiUktIUyIpDQopDQoNCm1vZGVsc1tbNl1dIDwtIGxpc3QoDQogIEUgICAgPSBsaXN0KFggPSBaRSwgbW9kZWwgPSAiQlJSIiksDQogIFBBSyAgPSBsaXN0KEsgPSBQQUssIG1vZGVsID0gIlJLSFMiKSwNCiAgUEFLRSA9IGxpc3QoSyA9IFBBS0UsIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzddXSA8LSBsaXN0KA0KICBFICA9IGxpc3QoWCA9IFpFLCBtb2RlbCA9ICJCUlIiKSwNCiAgRyAgPSBsaXN0KEsgPSBaRywgbW9kZWwgPSAiUktIUyIpLA0KICBQICA9IGxpc3QoSyA9IFpQLCBtb2RlbCA9ICJSS0hTIiksDQogIEdFID0gbGlzdChLID0gWkdaRSwgbW9kZWwgPSAiUktIUyIpLA0KICBQRSA9IGxpc3QoSyA9IFpQWkUsIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzhdXSA8LSBsaXN0KA0KICBFICAgID0gbGlzdChYID0gWkUsIG1vZGVsID0gIkJSUiIpLA0KICBHR0sgID0gbGlzdChLID0gR0dLLCBtb2RlbCA9ICJSS0hTIiksDQogIFBHSyAgPSBsaXN0KEsgPSBQR0ssIG1vZGVsID0gIlJLSFMiKSwNCiAgR0dLRSA9IGxpc3QoSyA9IEdHS0UsIG1vZGVsID0gIlJLSFMiKSwNCiAgUEdLRSA9IGxpc3QoSyA9IFBHS0UsIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzldXSA8LSBsaXN0KA0KICBFICAgID0gbGlzdChYID0gWkUsIG1vZGVsID0gIkJSUiIpLA0KICBHQUsgID0gbGlzdChLID0gR0FLLCBtb2RlbCA9ICJSS0hTIiksDQogIFBBSyAgPSBsaXN0KEsgPSBQQUssIG1vZGVsID0gIlJLSFMiKSwNCiAgR0FLRSA9IGxpc3QoSyA9IEdBS0UsIG1vZGVsID0gIlJLSFMiKSwNCiAgUEFLRSA9IGxpc3QoSyA9IFBBS0UsIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzEwXV0gPC0gbGlzdCgNCiAgRSAgPSBsaXN0KFggPSBaRSwgbW9kZWwgPSAiQlJSIiksDQogIFcgID0gbGlzdChLID0gWlcsIG1vZGVsID0gIlJLSFMiKSwNCiAgRyAgPSBsaXN0KEsgPSBaRywgbW9kZWwgPSAiUktIUyIpLA0KICBHRSA9IGxpc3QoSyA9IFpHWkUsIG1vZGVsID0gIlJLSFMiKSwNCiAgR1cgPSBsaXN0KEsgPSBaR1pXLCBtb2RlbCA9ICJSS0hTIikNCikNCg0KbW9kZWxzW1sxMV1dIDwtIGxpc3QoDQogIEUgID0gbGlzdChYID0gWkUsIG1vZGVsID0gIkJSUiIpLA0KICBXICA9IGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksDQogIFAgID0gbGlzdChLID0gWlAsIG1vZGVsID0gIlJLSFMiKSwNCiAgUEUgPSBsaXN0KEsgPSBaUFpFLCBtb2RlbCA9ICJSS0hTIiksDQogIFBXID0gbGlzdChLID0gWlBaVywgbW9kZWwgPSAiUktIUyIpDQopDQoNCm1vZGVsc1tbMTJdXSA8LSBsaXN0KA0KICBFICA9IGxpc3QoWCA9IFpFLCBtb2RlbCA9ICJCUlIiKSwNCiAgVyAgPSBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLA0KICBHICA9IGxpc3QoSyA9IFpHLCBtb2RlbCA9ICJSS0hTIiksDQogIFAgID0gbGlzdChLID0gWlAsIG1vZGVsID0gIlJLSFMiKSwNCiAgR0UgPSBsaXN0KEsgPSBaR1pFLCBtb2RlbCA9ICJSS0hTIiksDQogIFBFID0gbGlzdChLID0gWlBaRSwgbW9kZWwgPSAiUktIUyIpLA0KICBHVyA9IGxpc3QoSyA9IFpHWlcsIG1vZGVsID0gIlJLSFMiKSwNCiAgUFcgPSBsaXN0KEsgPSBaUFpXLCBtb2RlbCA9ICJSS0hTIikNCikNCg0KbW9kZWxzW1sxM11dIDwtIGxpc3QoDQogIEUgICAgPSBsaXN0KFggPSBaRSwgbW9kZWwgPSAiQlJSIiksDQogIFcgICAgPSBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLA0KICBHR0sgID0gbGlzdChLID0gR0dLLCBtb2RlbCA9ICJSS0hTIiksDQogIEdHS0UgPSBsaXN0KEsgPSBHR0tFLCBtb2RlbCA9ICJSS0hTIiksDQogIEdHS1cgPSBsaXN0KEsgPSBHR0tXLCBtb2RlbCA9ICJSS0hTIikNCikNCg0KbW9kZWxzW1sxNF1dIDwtIGxpc3QoDQogIEUgICAgPSBsaXN0KFggPSBaRSwgbW9kZWwgPSAiQlJSIiksDQogIFcgICAgPSBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLA0KICBQR0sgID0gbGlzdChLID0gUEdLLCBtb2RlbCA9ICJSS0hTIiksDQogIFBHS0UgPSBsaXN0KEsgPSBQR0tFLCBtb2RlbCA9ICJSS0hTIiksDQogIFBHS1cgPSBsaXN0KEsgPSBQR0tXLCBtb2RlbCA9ICJSS0hTIikNCikNCg0KbW9kZWxzW1sxNV1dIDwtIGxpc3QoDQogIEUgICAgPSBsaXN0KFggPSBaRSwgbW9kZWwgPSAiQlJSIiksDQogIFcgICAgPSBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLA0KICBHR0sgID0gbGlzdChLID0gR0dLLCBtb2RlbCA9ICJSS0hTIiksDQogIFBHSyAgPSBsaXN0KEsgPSBQR0ssIG1vZGVsID0gIlJLSFMiKSwNCiAgR0dLRSA9IGxpc3QoSyA9IEdHS0UsIG1vZGVsID0gIlJLSFMiKSwNCiAgUEdLRSA9IGxpc3QoSyA9IFBHS0UsIG1vZGVsID0gIlJLSFMiKSwNCiAgR0dLVyA9IGxpc3QoSyA9IEdHS1csIG1vZGVsID0gIlJLSFMiKSwNCiAgUEdLVyA9IGxpc3QoSyA9IFBHS1csIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzE2XV0gPC0gbGlzdCgNCiAgRSAgICA9IGxpc3QoWCA9IFpFLCBtb2RlbCA9ICJCUlIiKSwNCiAgVyAgICA9IGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksDQogIEdBSyAgPSBsaXN0KEsgPSBHQUssIG1vZGVsID0gIlJLSFMiKSwNCiAgR0FLRSA9IGxpc3QoSyA9IEdBS0UsIG1vZGVsID0gIlJLSFMiKSwNCiAgR0FLVyA9IGxpc3QoSyA9IEdBS1csIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzE3XV0gPC0gbGlzdCgNCiAgRSAgICA9IGxpc3QoWCA9IFpFLCBtb2RlbCA9ICJCUlIiKSwNCiAgVyAgICA9IGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksDQogIFBBSyAgPSBsaXN0KEsgPSBQQUssIG1vZGVsID0gIlJLSFMiKSwNCiAgUEFLRSA9IGxpc3QoSyA9IFBBS0UsIG1vZGVsID0gIlJLSFMiKSwNCiAgUEFLVyA9IGxpc3QoSyA9IFBBS1csIG1vZGVsID0gIlJLSFMiKQ0KKQ0KDQptb2RlbHNbWzE4XV0gPC0gbGlzdCgNCiAgRSAgICA9IGxpc3QoWCA9IFpFLCBtb2RlbCA9ICJCUlIiKSwNCiAgVyAgICA9IGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksDQogIEdBSyAgPSBsaXN0KEsgPSBHQUssIG1vZGVsID0gIlJLSFMiKSwNCiAgUEFLICA9IGxpc3QoSyA9IFBBSywgbW9kZWwgPSAiUktIUyIpLA0KICBHQUtFID0gbGlzdChLID0gR0FLRSwgbW9kZWwgPSAiUktIUyIpLA0KICBQQUtFID0gbGlzdChLID0gUEFLRSwgbW9kZWwgPSAiUktIUyIpLA0KICBHQUtXID0gbGlzdChLID0gR0FLVywgbW9kZWwgPSAiUktIUyIpLA0KICBQQUtXID0gbGlzdChLID0gUEFLVywgbW9kZWwgPSAiUktIUyIpDQopDQoNCm5hbWVzKG1vZGVscykgPC0gc3ByaW50ZigiRXRhJTAyZCIsIHNlcV9hbG9uZyhtb2RlbHMpKQ0KYGBgDQoNCiMjIyA0LjEuIE1vZGVsIGNhdGFsb2cNCg0KVGhpcyB0YWJsZSBpcyB1c2VmdWwgYm90aCBmb3IgaW50ZXJuYWwgY2hlY2tzIGFuZCBmb3IgbGlua2luZyBmaWd1cmUgbGFiZWxzIHRvIHRoZSBmb3JtYWwgZGVzY3JpcHRpb24gb2YgZWFjaCBtb2RlbC4NCg0KVGhlIGNvZGUgYmVsb3cgYnVpbGRzIGEgY29tcGFjdCBtb2RlbCBjYXRhbG9nIGxpbmtpbmcgZWFjaCBtb2RlbCBpZGVudGlmaWVyIHRvIGl0cyBjYXRlZ29yeSBhbmQgZGVzY3JpcHRpdmUgbGFiZWwgZm9yIGxhdGVyIHN1bW1hcmllcy4NCg0KYGBge3IgbW9kZWwtY2F0YWxvZ30NCm1vZGVsX2luZm8gPC0gdGliYmxlKA0KICBNb2RlbElEID0gMToxOCwNCiAgTW9kZWwgPSBwYXN0ZTAoIkV0YSIsIDE6MTgpLA0KICBFVEEgPSBuYW1lcyhtb2RlbHMpLA0KICBDYXRlZ29yeSA9IGMoDQogICAgcmVwKCJTaW5nbGUtc291cmNlICsgRSIsIDYpLA0KICAgIHJlcCgiSW50ZWdyYXRlZCBHL1AgKyBFIiwgMyksDQogICAgcmVwKCJXZWF0aGVyLWF1Z21lbnRlZCIsIDkpDQogICksDQogIERlc2NyaXB0aW9uID0gYygNCiAgICAiRSArIEcgKyBHRSIsDQogICAgIkUgKyBQICsgUEUiLA0KICAgICJFICsgR0dLICsgR0dLRSIsDQogICAgIkUgKyBQR0sgKyBQR0tFIiwNCiAgICAiRSArIEdBSyArIEdBS0UiLA0KICAgICJFICsgUEFLICsgUEFLRSIsDQogICAgIkUgKyBHICsgUCArIEdFICsgUEUiLA0KICAgICJFICsgR0dLICsgUEdLICsgR0dLRSArIFBHS0UiLA0KICAgICJFICsgR0FLICsgUEFLICsgR0FLRSArIFBBS0UiLA0KICAgICJFICsgVyArIEcgKyBHRSArIEdXIiwNCiAgICAiRSArIFcgKyBQICsgUEUgKyBQVyIsDQogICAgIkUgKyBXICsgRyArIFAgKyBHRSArIFBFICsgR1cgKyBQVyIsDQogICAgIkUgKyBXICsgR0dLICsgR0dLRSArIEdHS1ciLA0KICAgICJFICsgVyArIFBHSyArIFBHS0UgKyBQR0tXIiwNCiAgICAiRSArIFcgKyBHR0sgKyBQR0sgKyBHR0tFICsgUEdLRSArIEdHS1cgKyBQR0tXIiwNCiAgICAiRSArIFcgKyBHQUsgKyBHQUtFICsgR0FLVyIsDQogICAgIkUgKyBXICsgUEFLICsgUEFLRSArIFBBS1ciLA0KICAgICJFICsgVyArIEdBSyArIFBBSyArIEdBS0UgKyBQQUtFICsgR0FLVyArIFBBS1ciDQogICkNCikNCg0KcmVhZHI6OndyaXRlX2Nzdihtb2RlbF9pbmZvLCBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJtb2RlbF9jYXRhbG9nLmNzdiIpKQ0KDQptb2RlbF9pbmZvICU+JQ0KICBrbml0cjo6a2FibGUoDQogICAgY2FwdGlvbiA9ICJDYXRhbG9nIG9mIHRoZSAxOCBtb2RlbHMgZXZhbHVhdGVkIGluIHRoZSB2YXJpYW5jZS1jb21wb25lbnQgYW5hbHlzaXMuIiwNCiAgICBhbGlnbiA9ICJjbGwiDQogICkgJT4lDQogIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoDQogICAgYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiwgImNvbmRlbnNlZCIsICJyZXNwb25zaXZlIiksDQogICAgZnVsbF93aWR0aCA9IEZBTFNFLA0KICAgIHBvc2l0aW9uID0gImxlZnQiDQogICkgJT4lDQogIGthYmxlRXh0cmE6OnNjcm9sbF9ib3god2lkdGggPSAiMTAwJSIsIGhlaWdodCA9ICIzNDBweCIpDQpgYGANCg0KIyMgNS4gTUNNQyBTZXR0aW5ncw0KDQpUaGVzZSB2YWx1ZXMgZGVmaW5lIHRoZSBHaWJicyBzYW1wbGVyIHVzZWQgYnkgYEJHTFJgLiBBZGp1c3QgdGhlbSBoZXJlIGlmIGEgbG9uZ2VyIGNoYWluIGlzIHJlcXVpcmVkIGZvciBjb252ZXJnZW5jZSBkaWFnbm9zdGljcyBvciBzZW5zaXRpdml0eSBhbmFseXNlcy4NCg0KVGhpcyBzZWN0aW9uIGRlZmluZXMgdGhlIE1DTUMgc2V0dGluZ3MgYW5kIGV4ZWN1dGlvbiBjb250cm9scyB0aGF0IHJlZ3VsYXRlIHRoZSBlc3RpbWF0aW9uIHN0YWdlIHdoZW4gdGhlIGhlYXZ5IGNvbXB1dGF0aW9uIGlzIHR1cm5lZCBvbi4NCg0KYGBge3IgbWNtYy1wYXJhbWV0ZXJzfQ0Kbkl0ZXIgICA8LSA1MDAwDQpidXJuSW4gIDwtIDEwMDANCnRoaW4gICAgPC0gMTANCm51bV9yZXAgPC0gMTANCnRyYWl0cyAgPC0gYygiR1kiLCAiS1ciKQ0KYGBgDQoNCiMjIDYuIFJ1bm5pbmcgQkdMUiBmb3IgRWFjaCBNb2RlbA0KDQpUaGlzIGlzIHRoZSBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIHN0ZXAuIFRoZSBjaHVuayBpcyBrZXB0IHdpdGggYGV2YWw9RkFMU0VgIHNvIHRoYXQgdGhlIHR1dG9yaWFsIGNhbiBiZSByZW5kZXJlZCB3aXRob3V0IHJlcnVubmluZyBhbGwgQmF5ZXNpYW4gbW9kZWxzIGV2ZXJ5IHRpbWUuIFdoZW4geW91IG5lZWQgdXBkYXRlZCB2YXJpYW5jZSBjb21wb25lbnRzLCBjaGFuZ2UgYGV2YWxgIHRvIGBUUlVFYCBhbmQgcnVuIHRoZSBjaHVuay4NCg0KVGhlIG5leHQgYmxvY2sgY29udGFpbnMgdGhlIGNvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUgcGFyYWxsZWwgZXN0aW1hdGlvbiBzdGFnZSB0aGF0IGZpdHMgdGhlIHZhcmlhbmNlLWNvbXBvbmVudCBtb2RlbHMsIHByZXNlcnZlcyB0aGUgaXRlcmF0aXZlIEJHTFIgYXJ0aWZhY3RzIGluIHBlcnNpc3RlbnQgZm9sZGVycywgYW5kIHNhdmVzIHRoZSByYXcgcG9zdGVyaW9yIHN1bW1hcmllcy4NCg0KYGBge3IgcGFyYWxsZWwtcnVuLCBldmFsPUZBTFNFfQ0KbnVtQ29yZXMgPC0gcGFyYWxsZWw6OmRldGVjdENvcmVzKCkNCnVzZUNvcmVzIDwtIG1heCgxLCBudW1Db3JlcyAtIDEpDQpjbCA8LSBwYXJhbGxlbDo6bWFrZUNsdXN0ZXIodXNlQ29yZXMpDQpkb1BhcmFsbGVsOjpyZWdpc3RlckRvUGFyYWxsZWwoY2wpDQoNCmNvbWJvcyA8LSBleHBhbmQuZ3JpZCgNCiAgTW9kZWwgPSAxOjE4LA0KICBSRVAgPSAxOm51bV9yZXAsDQogIFRyYWl0ID0gdHJhaXRzLA0KICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCikNCg0KcmVzdWx0c19saXN0IDwtIGZvcmVhY2goDQogIGkgPSAxOm5yb3coY29tYm9zKSwNCiAgLnBhY2thZ2VzID0gYygiQkdMUiIsICJkcGx5ciIsICJ0aWJibGUiKQ0KKSAlZG9wYXIlIHsNCiAgTW9kZWwgPC0gY29tYm9zJE1vZGVsW2ldDQogIHJlcF9udW0gPC0gY29tYm9zJFJFUFtpXQ0KICBUcmFpdCA8LSBjb21ib3MkVHJhaXRbaV0NCiAgcmVwX2xhYmVsIDwtIHNwcmludGYoInJlcCUwMmQiLCByZXBfbnVtKQ0KICBldGFfbGFiZWwgPC0gcGFzdGUwKCJFdGEiLCBNb2RlbCkNCg0KICBwaGVubyA8LSBkYXRhLmZyYW1lKFBoZW5vKQ0KICB5X2RhdGEgPC0gYXMubnVtZXJpYyhwaGVub1tbVHJhaXRdXSkNCg0KICBvdXRfZGlyIDwtIGZpbGUucGF0aChiZ2xyX3J1bnNfZGlyLCBUcmFpdCwgcmVwX2xhYmVsLCBldGFfbGFiZWwpDQogIGRpci5jcmVhdGUob3V0X2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpDQoNCiAgc2F2ZV9wcmVmaXggPC0gZmlsZS5wYXRoKA0KICAgIG91dF9kaXIsDQogICAgcGFzdGUwKCJWQ18iLCBUcmFpdCwgIl8iLCBldGFfbGFiZWwsICJfIiwgcmVwX2xhYmVsLCAiXyIpDQogICkNCg0KICBzdGFydF90aW1lIDwtIFN5cy50aW1lKCkNCiAgZml0IDwtIEJHTFIoDQogICAgeSA9IHlfZGF0YSwNCiAgICBFVEEgPSBtb2RlbHNbW01vZGVsXV0sDQogICAgbkl0ZXIgPSBuSXRlciwNCiAgICBidXJuSW4gPSBidXJuSW4sDQogICAgdGhpbiA9IHRoaW4sDQogICAgc2F2ZUF0ID0gc2F2ZV9wcmVmaXgNCiAgKQ0KICBlbmRfdGltZSA8LSBTeXMudGltZSgpDQoNCiAgZWZmZWN0X25hbWVzIDwtIG5hbWVzKG1vZGVsc1tbTW9kZWxdXSkNCiAgZWZmZWN0X21vZGVscyA8LSB2YXBwbHkobW9kZWxzW1tNb2RlbF1dLCBmdW5jdGlvbih4KSB4JG1vZGVsLCBjaGFyYWN0ZXIoMSkpDQogIGVmZmVjdF9sYWJlbHMgPC0gcGFzdGUwKGVmZmVjdF9uYW1lcywgIl8iLCBlZmZlY3RfbW9kZWxzKQ0KDQogIGdlbmVyYXRlZF9maWxlcyA8LSBsaXN0LmZpbGVzKA0KICAgIG91dF9kaXIsDQogICAgcGF0dGVybiA9IHBhc3RlMCgiXiIsIGJhc2VuYW1lKHNhdmVfcHJlZml4KSksDQogICAgZnVsbC5uYW1lcyA9IFRSVUUNCiAgKQ0KDQogIGlmIChsZW5ndGgoZ2VuZXJhdGVkX2ZpbGVzKSA+IDApIHsNCiAgICBmb3IgKGN1cnJlbnRfZmlsZSBpbiBnZW5lcmF0ZWRfZmlsZXMpIHsNCiAgICAgIGN1cnJlbnRfbmFtZSA8LSBiYXNlbmFtZShjdXJyZW50X2ZpbGUpDQogICAgICByZW5hbWVkX25hbWUgPC0gY3VycmVudF9uYW1lDQoNCiAgICAgIGZvciAoaiBpbiBzZXFfYWxvbmcoZWZmZWN0X2xhYmVscykpIHsNCiAgICAgICAgcmVuYW1lZF9uYW1lIDwtIHN1YigNCiAgICAgICAgICBwYXN0ZTAoIkVUQV8iLCBqKSwNCiAgICAgICAgICBlZmZlY3RfbGFiZWxzW2pdLA0KICAgICAgICAgIHJlbmFtZWRfbmFtZSwNCiAgICAgICAgICBmaXhlZCA9IFRSVUUNCiAgICAgICAgKQ0KICAgICAgfQ0KDQogICAgICBpZiAoIWlkZW50aWNhbChjdXJyZW50X25hbWUsIHJlbmFtZWRfbmFtZSkpIHsNCiAgICAgICAgZmlsZS5yZW5hbWUoY3VycmVudF9maWxlLCBmaWxlLnBhdGgob3V0X2RpciwgcmVuYW1lZF9uYW1lKSkNCiAgICAgIH0NCiAgICB9DQogIH0NCg0KICB2YXJpYW5jZV9zYW1wbGVzIDwtIGxpc3QoKQ0KDQogIGlmICghaXMubnVsbChmaXQkRVRBKSkgew0KICAgIGlmIChpcy5udWxsKGVmZmVjdF9uYW1lcykpIHsNCiAgICAgIGVmZmVjdF9uYW1lcyA8LSBwYXN0ZTAoIkVmZmVjdF8iLCBzZXFfYWxvbmcoZml0JEVUQSkpDQogICAgfQ0KDQogICAgZm9yIChqIGluIHNlcV9hbG9uZyhmaXQkRVRBKSkgew0KICAgICAgcGFyYW1ldGVyX25hbWVzIDwtIG5hbWVzKGZpdCRFVEFbW2pdXSkNCiAgICAgIHZhcl9wYXJhbWV0ZXIgPC0gcGFyYW1ldGVyX25hbWVzW2dyZXBsKCJedmFyIiwgcGFyYW1ldGVyX25hbWVzKV0NCg0KICAgICAgaWYgKGxlbmd0aCh2YXJfcGFyYW1ldGVyKSA+IDApIHsNCiAgICAgICAgdmFyaWFuY2Vfc2FtcGxlc1tbZWZmZWN0X25hbWVzW2pdXV0gPC0gZml0JEVUQVtbal1dW1t2YXJfcGFyYW1ldGVyWzFdXV0NCiAgICAgIH0NCiAgICB9DQogIH0NCg0KICBtZXRyaWNzX29iamVjdCA8LSBsaXN0KA0KICAgIHZhckVfc2FtcGxlcyA9IGZpdCR2YXJFLA0KICAgIHZhcmlhbmNlc19zYW1wbGVzID0gdmFyaWFuY2Vfc2FtcGxlcywNCiAgICBESUMgPSBmaXQkZml0JERJQywNCiAgICBydW50aW1lX3NlYyA9IGFzLm51bWVyaWMoZGlmZnRpbWUoZW5kX3RpbWUsIHN0YXJ0X3RpbWUsIHVuaXRzID0gInNlY3MiKSkNCiAgKQ0KDQogIHNhdmVSRFMobWV0cmljc19vYmplY3QsIGZpbGUucGF0aChvdXRfZGlyLCAidmFyaWFuY2VfbWV0cmljcy5yZHMiKSkNCg0KICByZXN1bHRfdGFibGUgPC0gdGliYmxlKA0KICAgIENvbXBvbmVudCA9ICJSZXNpZHVhbCIsDQogICAgTWVhblZhciA9IG1lYW4obWV0cmljc19vYmplY3QkdmFyRV9zYW1wbGVzLCBuYS5ybSA9IFRSVUUpDQogICkNCg0KICBpZiAobGVuZ3RoKG1ldHJpY3Nfb2JqZWN0JHZhcmlhbmNlc19zYW1wbGVzKSA+IDApIHsNCiAgICBmb3IgKG5tIGluIG5hbWVzKG1ldHJpY3Nfb2JqZWN0JHZhcmlhbmNlc19zYW1wbGVzKSkgew0KICAgICAgcmVzdWx0X3RhYmxlIDwtIGJpbmRfcm93cygNCiAgICAgICAgcmVzdWx0X3RhYmxlLA0KICAgICAgICB0aWJibGUoDQogICAgICAgICAgQ29tcG9uZW50ID0gbm0sDQogICAgICAgICAgTWVhblZhciA9IG1lYW4obWV0cmljc19vYmplY3QkdmFyaWFuY2VzX3NhbXBsZXNbW25tXV0sIG5hLnJtID0gVFJVRSkNCiAgICAgICAgKQ0KICAgICAgKQ0KICAgIH0NCiAgfQ0KDQogIHJtKGZpdCkNCiAgZ2MoKQ0KDQogIHJlc3VsdF90YWJsZSAlPiUNCiAgICBtdXRhdGUoTW9kZWwgPSBNb2RlbCwgUmVwID0gcmVwX251bSwgVHJhaXQgPSBUcmFpdCkNCn0NCg0Kc3RvcENsdXN0ZXIoY2wpDQoNCmFsbF92YXJpYW5jZSA8LSBiaW5kX3Jvd3MocmVzdWx0c19saXN0KQ0Kd3JpdGVfY3N2KGFsbF92YXJpYW5jZSwgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAidmFyaWFuY2VfY29tcG9uZW50c19yYXcuY3N2IikpDQpzYXZlUkRTKGFsbF92YXJpYW5jZSwgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAidmFyaWFuY2VfY29tcG9uZW50c19yYXcucmRzIikpDQpgYGANCg0KIyMgNy4gQ29uc29saWRhdGluZyBTYXZlZCBQb3N0ZXJpb3IgU3VtbWFyaWVzDQoNCkluIHRoZSBjdXJyZW50IHZlcnNpb24gb2YgdGhlIHdvcmtmbG93LCB0aGlzIGlzIHRoZSBzZWN0aW9uIHVzZWQgbW9zdCBvZnRlbi4gSW5zdGVhZCBvZiByZWZpdHRpbmcgYWxsIG1vZGVscyBldmVyeSB0aW1lLCB0aGUgc2NyaXB0IHJlYWRzIHRoZSBwcmV2aW91c2x5IHNhdmVkIGB2YXJpYW5jZV9tZXRyaWNzLnJkc2AgZmlsZXMgZnJvbSBgb3V0cHV0L3ZhcmlhbmNlX2NvbXBvbmVudHMvYmdscl9ydW5zL2AgYW5kIGNvbnNvbGlkYXRlcyB0aGVtIGludG8gb25lIG9iamVjdC4gVGhpcyBrZWVwcyB0aGUgdHV0b3JpYWwgcHJhY3RpY2FsIGFuZCByZXByb2R1Y2libGUsIHdoaWxlIG1ha2luZyBpdCBjbGVhciB0aGF0IHRoaXMgcGFnZSBpcyBtYWlubHkgYSAqKnJlc3VsdC1jb25zb2xpZGF0aW9uIHN0ZXAqKiB1bmxlc3MgdGhlIHVzZXIgaW50ZW50aW9uYWxseSByZXJ1bnMgdGhlIGZ1bGwgQkdMUiBlc3RpbWF0aW9uIHN0YWdlLg0KDQpUaGUgc2F2ZWQgb3V0cHV0cyBwcm9kdWNlZCBieSB0aGUgZXN0aW1hdGlvbiBzdGFnZSBhcmUgY29uc29saWRhdGVkIGJlbG93IGludG8gYSBzaW5nbGUgc3RydWN0dXJlIHRoYXQgY2FuIGJlIHByb2Nlc3NlZCBtb3JlIGVhc2lseSBsYXRlci4NCg0KYGBge3IgY29uc29saWRhdGUtc2F2ZWQtcmVzdWx0c30NCnJkc19maWxlcyA8LSBsaXN0LmZpbGVzKA0KICBwYXRoID0gYmdscl9ydW5zX2RpciwNCiAgcGF0dGVybiA9ICIodmFyaWFuY2VfbWV0cmljc3xfKG1ldHJpY3N8bWV0cmljYXMpKVxcLnJkcyQiLA0KICByZWN1cnNpdmUgPSBUUlVFLA0KICBmdWxsLm5hbWVzID0gVFJVRQ0KKQ0KDQppZiAobGVuZ3RoKHJkc19maWxlcykgPiAwKSB7DQogIGFsbF90YWJsZXMgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoKHJkc19maWxlcykpDQoNCiAgZm9yIChpIGluIHNlcV9hbG9uZyhyZHNfZmlsZXMpKSB7DQogICAgY3VycmVudF9maWxlIDwtIG5vcm1hbGl6ZVBhdGgocmRzX2ZpbGVzW2ldLCB3aW5zbGFzaCA9ICIvIiwgbXVzdFdvcmsgPSBGQUxTRSkNCiAgICBwYXRoX3BhcnRzIDwtIHN0cl9zcGxpdChjdXJyZW50X2ZpbGUsICIvIilbWzFdXQ0KDQogICAgcmVwX3BhcnQgPC0gcGF0aF9wYXJ0c1tzdHJfZGV0ZWN0KHBhdGhfcGFydHMsICJecmVwWzAtOV0rJHxecmVwX1swLTldKyQiKV1bMV0NCiAgICBldGFfcGFydCA8LSBwYXRoX3BhcnRzW3N0cl9kZXRlY3QocGF0aF9wYXJ0cywgIl5FdGEiKV1bMV0NCiAgICB0cmFpdF9wYXJ0IDwtIHBhdGhfcGFydHNbcGF0aF9wYXJ0cyAlaW4lIHRyYWl0c11bMV0NCg0KICAgIHJlcF92YWx1ZSA8LSBhcy5pbnRlZ2VyKHN0cl9yZW1vdmUocmVwX3BhcnQsICJecmVwXz8wKiIpKQ0KICAgIG1vZGVsX3ZhbHVlIDwtIGFzLmludGVnZXIoc3RyX3JlbW92ZShldGFfcGFydCwgIl5FdGEwKiIpKQ0KICAgIHRyYWl0X3ZhbHVlIDwtIHRyYWl0X3BhcnQNCg0KICAgIGN1cnJlbnRfb2JqZWN0IDwtIHJlYWRSRFMoY3VycmVudF9maWxlKQ0KDQogICAgY3VycmVudF90YWJsZSA8LSB0aWJibGUoDQogICAgICBSZXAgPSByZXBfdmFsdWUsDQogICAgICBNb2RlbCA9IG1vZGVsX3ZhbHVlLA0KICAgICAgVHJhaXQgPSB0cmFpdF92YWx1ZSwNCiAgICAgIENvbXBvbmVudCA9ICJSZXNpZHVhbCIsDQogICAgICBNZWFuVmFyID0gbWVhbihjdXJyZW50X29iamVjdCR2YXJFX3NhbXBsZXMsIG5hLnJtID0gVFJVRSksDQogICAgICBESUMgPSBjdXJyZW50X29iamVjdCRESUMsDQogICAgICBydW50aW1lX3NlYyA9IGN1cnJlbnRfb2JqZWN0JHJ1bnRpbWVfc2VjDQogICAgKQ0KDQogICAgaWYgKGxlbmd0aChjdXJyZW50X29iamVjdCR2YXJpYW5jZXNfc2FtcGxlcykgPiAwKSB7DQogICAgICBmb3IgKG5tIGluIG5hbWVzKGN1cnJlbnRfb2JqZWN0JHZhcmlhbmNlc19zYW1wbGVzKSkgew0KICAgICAgICBjdXJyZW50X3RhYmxlIDwtIGJpbmRfcm93cygNCiAgICAgICAgICBjdXJyZW50X3RhYmxlLA0KICAgICAgICAgIHRpYmJsZSgNCiAgICAgICAgICAgIFJlcCA9IHJlcF92YWx1ZSwNCiAgICAgICAgICAgIE1vZGVsID0gbW9kZWxfdmFsdWUsDQogICAgICAgICAgICBUcmFpdCA9IHRyYWl0X3ZhbHVlLA0KICAgICAgICAgICAgQ29tcG9uZW50ID0gbm9ybWFsaXplX2NvbXBvbmVudF9uYW1lKG5tLCBtb2RlbF92YWx1ZSA9IG1vZGVsX3ZhbHVlKSwNCiAgICAgICAgICAgIE1lYW5WYXIgPSBtZWFuKGN1cnJlbnRfb2JqZWN0JHZhcmlhbmNlc19zYW1wbGVzW1tubV1dLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgRElDID0gY3VycmVudF9vYmplY3QkRElDLA0KICAgICAgICAgICAgcnVudGltZV9zZWMgPSBjdXJyZW50X29iamVjdCRydW50aW1lX3NlYw0KICAgICAgICAgICkNCiAgICAgICAgKQ0KICAgICAgfQ0KICAgIH0NCg0KICAgIGFsbF90YWJsZXNbW2ldXSA8LSBjdXJyZW50X3RhYmxlDQogIH0NCg0KICBhbGxfdmFyaWFuY2UgPC0gYmluZF9yb3dzKGFsbF90YWJsZXMpDQoNCiAgc2F2ZVJEUyhhbGxfdmFyaWFuY2UsIGZpbGUucGF0aChyZXN1bHRzX2RpciwgInZhcmlhbmNlX2NvbXBvbmVudHNfYWxsLnJkcyIpKQ0KICB3cml0ZV9jc3YoYWxsX3ZhcmlhbmNlLCBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJ2YXJpYW5jZV9jb21wb25lbnRzX3Jhdy5jc3YiKSkNCn0NCmBgYA0KDQojIyA4LiBMb2FkaW5nIHRoZSBDb25zb2xpZGF0ZWQgVmFyaWFuY2UtQ29tcG9uZW50IE9iamVjdA0KDQpUaGlzIHNlY3Rpb24gbG9hZHMgdGhlIGNvbnNvbGlkYXRlZCByZXN1bHRzIGFuZCBwcmVwYXJlcyB0aGVtIGZvciBzdW1tYXJpemF0aW9uLiBJZiB0aGUgZmlsZSBkb2VzIG5vdCBleGlzdCwgdGhlIHNjcmlwdCBzdG9wcyBhbmQgaW5zdHJ1Y3RzIHRoZSB1c2VyIHRvIGV4ZWN1dGUgdGhlIG1vZGVsLWZpdHRpbmcgc3RhZ2UgZmlyc3QuDQoNClRoZSBkb2N1bWVudCBub3cgbG9hZHMgdGhlIGFscmVhZHkgY29uc29saWRhdGVkIHZhcmlhbmNlLWNvbXBvbmVudCByZXN1bHRzIHNvIHRoYXQgaXQgY2FuIHN1bW1hcml6ZSBhbmQgdmlzdWFsaXplIHRoZW0gd2l0aG91dCByZXBlYXRpbmcgdGhlIGV4cGVuc2l2ZSBlc3RpbWF0aW9uLg0KDQpgYGB7ciBsb2FkLXJlc3VsdHMsIGVycm9yPVRSVUV9DQpyYXdfZmlsZSA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJ2YXJpYW5jZV9jb21wb25lbnRzX2FsbC5yZHMiKQ0KDQppZiAoIWZpbGUuZXhpc3RzKHJhd19maWxlKSkgew0KICBzdG9wKA0KICAgICJUaGUgZmlsZSAnIiwgcmF3X2ZpbGUsICInIHdhcyBub3QgZm91bmQuICIsDQogICAgIlJ1biB0aGUgQmF5ZXNpYW4gZml0dGluZyBzdGFnZSBmaXJzdCBvciBrZWVwIHRoZSBwcmV2aW91c2x5IHNhdmVkIF9tZXRyaWNzLnJkcyBmaWxlcyBpbiBvdXRwdXQvdmFyaWFuY2VfY29tcG9uZW50cy8uIg0KICApDQp9DQoNCmFsbF92YXJpYW5jZSA8LSByZWFkUkRTKHJhd19maWxlKQ0KDQphbGxfdmFyaWFuY2UgPC0gYWxsX3ZhcmlhbmNlICU+JQ0KICBtdXRhdGUoDQogICAgVHJhaXQgPSBzdHJfZXh0cmFjdChUcmFpdCwgIkdZfEtXIiksDQogICAgVHJhaXQgPSBpZmVsc2UoaXMubmEoVHJhaXQpLCBUcmFpdCwgVHJhaXQpLA0KICAgIE1vZGVsID0gYXMuaW50ZWdlcihNb2RlbCkNCiAgKQ0KDQpjYXQoIk51bWJlciBvZiByb3dzIGluIGNvbnNvbGlkYXRlZCB2YXJpYW5jZSBvYmplY3Q6IiwgbnJvdyhhbGxfdmFyaWFuY2UpLCAiXG4iKQ0KYGBgDQoNCiMjIDkuIFN1bW1hcml6aW5nIHRoZSBWYXJpYW5jZSBDb21wb25lbnRzDQoNCk5vdyB3ZSBhdmVyYWdlIHRoZSBwb3N0ZXJpb3IgbWVhbnMgYWNyb3NzIHJlcGV0aXRpb25zIGZvciBlYWNoIG1vZGVsLCB0cmFpdCwgYW5kIGNvbXBvbmVudC4gVGhlbiB3ZSBjb252ZXJ0IHRoZSByZXN1bHRzIHRvICoqcGVyY2VudGFnZXMgb2YgdG90YWwgdmFyaWFuY2UqKiwgd2hpY2ggbWFrZXMgdGhlIG1vZGVscyBkaXJlY3RseSBjb21wYXJhYmxlLg0KDQpUaGUgbG9hZGVkIHJlc3VsdHMgYXJlIHRoZW4gcmVzaGFwZWQgYW5kIGFubm90YXRlZCBzbyB0aGF0IHBvc3RlcmlvciBtZWFucyBjYW4gYmUgY29tcGFyZWQgYWNyb3NzIHRyYWl0cywgbW9kZWxzLCBhbmQgY29tcG9uZW50IGNhdGVnb3JpZXMuDQoNCmBgYHtyIHByb2Nlc3MtdmFyaWFuY2UtY29tcG9uZW50c30NCnZhcmlhbmNlX3N1bW1hcnkgPC0gYWxsX3ZhcmlhbmNlICU+JQ0KICBncm91cF9ieShNb2RlbCwgVHJhaXQsIENvbXBvbmVudCkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBBdmdWYXIgPSBtZWFuKE1lYW5WYXIsIG5hLnJtID0gVFJVRSksDQogICAgTWVhbkRJQyA9IG1lYW4oRElDLCBuYS5ybSA9IFRSVUUpLA0KICAgIE1lYW5SdW50aW1lU2VjID0gbWVhbihydW50aW1lX3NlYywgbmEucm0gPSBUUlVFKSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIGZpbHRlcighaXMubmEoQXZnVmFyKSwgQXZnVmFyID4gMCkgJT4lDQogIGdyb3VwX2J5KE1vZGVsLCBUcmFpdCkgJT4lDQogIG11dGF0ZSgNCiAgICBUb3RhbFZhciA9IHN1bShBdmdWYXIpLA0KICAgIFBlcmNlbnRhZ2UgPSBBdmdWYXIgLyBUb3RhbFZhcg0KICApICU+JQ0KICB1bmdyb3VwKCkgJT4lDQogIGxlZnRfam9pbihtb2RlbF9pbmZvLCBieSA9IGMoIk1vZGVsIiA9ICJNb2RlbElEIikpICU+JQ0KICBtdXRhdGUoDQogICAgTW9kZWxOdW1iZXIgPSBNb2RlbCwNCiAgICBNb2RlbCA9IGZhY3RvcihwYXN0ZTAoIkV0YSIsIE1vZGVsTnVtYmVyKSwgbGV2ZWxzID0gcGFzdGUwKCJFdGEiLCAxOjE4KSksDQogICAgQ29tcG9uZW50ID0gbm9ybWFsaXplX2NvbXBvbmVudF9uYW1lKENvbXBvbmVudCwgbW9kZWxfdmFsdWUgPSBNb2RlbE51bWJlciksDQogICAgQ29tcG9uZW50TGFiZWwgPSBkcGx5cjo6cmVjb2RlKENvbXBvbmVudCwgISEhY29tcG9uZW50X2Rpc3BsYXkpLA0KICAgIENvbXBvbmVudExhYmVsID0gZmFjdG9yKENvbXBvbmVudExhYmVsLCBsZXZlbHMgPSBjb21wb25lbnRfb3JkZXIpLA0KICAgIFRyYWl0ID0gZmFjdG9yKFRyYWl0LCBsZXZlbHMgPSBjKCJHWSIsICJLVyIpKQ0KICApICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1Nb2RlbE51bWJlcikNCg0KcmVhZHI6OndyaXRlX2Nzdih2YXJpYW5jZV9zdW1tYXJ5LCBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJ2YXJpYW5jZV9jb21wb25lbnRzX3Byb2Nlc3NlZC5jc3YiKSkNCmBgYA0KDQojIyMgOS4xLiBXaWRlLWZvcm1hdCBzdW1tYXJ5IHRhYmxlDQoNClRoaXMgdGFibGUgaXMgaGVscGZ1bCB3aGVuIHlvdSBuZWVkIGEgY29tcGFjdCBudW1lcmljYWwgc3VtbWFyeSBmb3IgdGhlIG1hbnVzY3JpcHQgb3Igc3VwcGxlbWVudGFyeSBtYXRlcmlhbC4NCg0KVGhlIG5leHQgdGFibGUgc3VtbWFyaXplcyB0aGUgcmVsYXRpdmUgY29udHJpYnV0aW9uIG9mIGVhY2ggdmFyaWFuY2UgY29tcG9uZW50IGFjcm9zcyBtb2RlbHMuDQoNCmBgYHtyIHN1bW1hcnktdGFibGV9DQp2YXJpYW5jZV90YWJsZSA8LSB2YXJpYW5jZV9zdW1tYXJ5ICU+JQ0KICBkcGx5cjo6c2VsZWN0KE1vZGVsLCBUcmFpdCwgQ29tcG9uZW50TGFiZWwsIFBlcmNlbnRhZ2UpICU+JQ0KICBkcGx5cjo6bXV0YXRlKFBlcmNlbnRhZ2UgPSByb3VuZCgxMDAgKiBQZXJjZW50YWdlLCAyKSkgJT4lDQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ29tcG9uZW50TGFiZWwsIHZhbHVlc19mcm9tID0gUGVyY2VudGFnZSkNCg0KcmVhZHI6OndyaXRlX2Nzdih2YXJpYW5jZV90YWJsZSwgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAidmFyaWFuY2VfY29tcG9uZW50c190YWJsZV9wZXJjZW50LmNzdiIpKQ0KDQpoZWFkKHZhcmlhbmNlX3RhYmxlLCAxMikgJT4lDQogIGtuaXRyOjprYWJsZSgNCiAgICBjYXB0aW9uID0gIlByZXZpZXcgb2YgdGhlIHByb2Nlc3NlZCB2YXJpYW5jZS1jb21wb25lbnQgdGFibGUgKHBlcmNlbnQgb2YgdG90YWwgdmFyaWFuY2UpLiIsDQogICAgYWxpZ24gPSAibGNjY2NjY2NjY2NjYyINCiAgKSAlPiUNCiAga2FibGVFeHRyYTo6a2FibGVfc3R5bGluZygNCiAgICBib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIiwgInJlc3BvbnNpdmUiKSwNCiAgICBmdWxsX3dpZHRoID0gRkFMU0UsDQogICAgcG9zaXRpb24gPSAibGVmdCINCiAgKSAlPiUNCiAga2FibGVFeHRyYTo6c2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gIjMyMHB4IikNCmBgYA0KDQojIyAxMC4gRmlndXJlIGZvciB0aGUgQXJ0aWNsZQ0KDQpUaGUgbmV4dCBmaWd1cmUgc3VtbWFyaXplcyB0aGUgcGVyY2VudGFnZSBjb250cmlidXRpb24gb2YgZWFjaCB2YXJpYW5jZSBjb21wb25lbnQgYWNyb3NzIHRoZSAxOCBtb2RlbHMgZm9yIGJvdGggdHJhaXRzLiBUbyBrZWVwIHRoZSB4LWF4aXMgcmVhZGFibGUsIHRoZSBtb2RlbHMgYXJlIGRpc3BsYXllZCBhcyBgRXRhMWAgdG8gYEV0YTE4YCwgYW5kIHRoZSBkZXRhaWxlZCBkZXNjcmlwdGlvbnMgcmVtYWluIGF2YWlsYWJsZSBpbiB0aGUgbW9kZWwgY2F0YWxvZyBzYXZlZCBhYm92ZS4NCg0KVGhpcyBmaWd1cmUgc2hvdWxkIGJlIHJlYWQgYXMgYSBzdHJ1Y3R1cmFsIHN1bW1hcnkgb2YgdGhlIGZpdHRlZCBtb2RlbHMsIG5vdCBhcyBkaXJlY3QgcHJvb2YgdGhhdCBjb21iaW5pbmcgbW9yZSBkYXRhIHNvdXJjZXMgYXV0b21hdGljYWxseSBsZWFkcyB0byBzdHJvbmdlciBiaW9sb2dpY2FsIGNvbXBsZW1lbnRhcml0eSBvciBiZXR0ZXIgcHJlZGljdGl2ZSBwZXJmb3JtYW5jZS4NCg0KVGhlIGZpbmFsIGZpZ3VyZSB0dXJucyB0aGUgcHJvY2Vzc2VkIHRhYmxlIGludG8gYSBjb21wYWN0IHZpc3VhbCBzdW1tYXJ5IGZvciBpbnRlcnByZXRhdGlvbi4NCg0KYGBge3IgdmFyaWFuY2UtZmlndXJlLCBmaWcuY2FwPSJGaWd1cmUgMy4gUG9zdGVyaW9yIG1lYW4gcGVyY2VudGFnZSBvZiB0b3RhbCB2YXJpYW5jZSBhdHRyaWJ1dGVkIHRvIGVhY2ggbW9kZWwgY29tcG9uZW50IGFjcm9zcyB0aGUgMTggcHJlZGljdGl2ZSBtb2RlbHMgZm9yIGdyYWluIHlpZWxkIChHWSkgYW5kIGtlcm5lbCB3ZWlnaHQgKEtXKS4gRWFjaCBzdGFja2VkIGJhciByZXByZXNlbnRzIG9uZSBtb2RlbCAoRXRhMS1FdGExOCksIGF2ZXJhZ2VkIGFjcm9zcyByZXBldGl0aW9ucywgYW5kIGNvbG9ycyBpZGVudGlmeSB0aGUgdmFyaWFuY2UgY29tcG9uZW50cyBpbmNsdWRlZCBpbiB0aGUgbW9kZWwgc3RydWN0dXJlLiBUaGUgd2VhdGhlciBrZXJuZWwgKFcpIGFuZCBpdHMgYXNzb2NpYXRlZCBpbnRlcmFjdGlvbiB0ZXJtcyBhcHBlYXIgbWFpbmx5IGluIG1vZGVscyBFdGExMC1FdGExOC4gVmFyaWFuY2UgZGVjb21wb3NpdGlvbiBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgYXMgYSBkZXNjcmlwdGlvbiBvZiBtb2RlbCBzdHJ1Y3R1cmUgYW5kIG5vdCBhdXRvbWF0aWNhbGx5IGFzIGV2aWRlbmNlIG9mIHN0cm9uZ2VyIGJpb2xvZ2ljYWwgY29tcGxlbWVudGFyaXR5IGFtb25nIGluZm9ybWF0aW9uIHNvdXJjZXMuIiwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEzfQ0KdHJhaXRfbGFiZWxzIDwtIGMoDQogIEdZID0gIkEuIEdyYWluIHlpZWxkIChHWSkiLA0KICBLVyA9ICJCLiBLZXJuZWwgd2VpZ2h0IChLVykiDQopDQoNCnZhcmlhbmNlX3Bsb3QgPC0gdmFyaWFuY2Vfc3VtbWFyeSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gTW9kZWwsIHkgPSBQZXJjZW50YWdlLCBmaWxsID0gQ29tcG9uZW50TGFiZWwpKSArDQogIGdlb21fY29sKGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC4xNSwgd2lkdGggPSAwLjgyKSArDQogIGZhY2V0X3dyYXAofiBUcmFpdCwgbmNvbCA9IDEsIGxhYmVsbGVyID0gYXNfbGFiZWxsZXIodHJhaXRfbGFiZWxzKSkgKw0KICBnZ3RoZW1lczo6c2NhbGVfZmlsbF9nZG9jcyhuYW1lID0gIlZhcmlhbmNlIGNvbXBvbmVudCIsIGRyb3AgPSBGQUxTRSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50X2Zvcm1hdChhY2N1cmFjeSA9IDEpLA0KICAgIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjAyKSkNCiAgKSArDQogIGxhYnMoDQogICAgeCA9ICJNb2RlbCBJRCIsDQogICAgeSA9ICJQcm9wb3J0aW9uIG9mIHRvdGFsIHZhcmlhbmNlIg0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMSkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMywgaGp1c3QgPSAwLjUpLA0KICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDExKSwNCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgdmp1c3QgPSAxLCBzaXplID0gOCksDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDkpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsDQogICAgbGVnZW5kLmJveCA9ICJ2ZXJ0aWNhbCIsDQogICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIHNpemUgPSAxMCksDQogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLA0KICAgIGxlZ2VuZC5rZXkuaGVpZ2h0ID0gdW5pdCgwLjQ1LCAiY20iKSwNCiAgICBsZWdlbmQua2V5LndpZHRoID0gdW5pdCgwLjU1LCAiY20iKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQ0KICApICsNCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobmNvbCA9IDIsIGJ5cm93ID0gVFJVRSkpDQoNCnByaW50KHZhcmlhbmNlX3Bsb3QpDQoNCmdnc2F2ZSgNCiAgZmlsZW5hbWUgPSBmaWxlLnBhdGgoZmlndXJlX2RpciwgInZhcmlhbmNlX2NvbXBvbmVudHNfYXJ0aWNsZS50aWZmIiksDQogIHBsb3QgPSB2YXJpYW5jZV9wbG90LA0KICB3aWR0aCA9IDE0LA0KICBoZWlnaHQgPSAxMCwNCiAgZHBpID0gMzAwLA0KICBjb21wcmVzc2lvbiA9ICJsenciLA0KICBiZyA9ICJ3aGl0ZSINCikNCmBgYA0KDQojIyAxMS4gSW50ZXJwcmV0YXRpb24gR3VpZGUNCg0KVGhpcyBmaW5hbCBzZWN0aW9uIGNsYXJpZmllcyBob3cgdGhlIGZpZ3VyZSBzaG91bGQgYmUgaW50ZXJwcmV0ZWQuDQoNCi0gQSBsYXJnZSAqKkVudmlyb25tZW50IChFKSoqIHNlZ21lbnQgaW5kaWNhdGVzIHRoYXQgdGhlIGNhdGVnb3JpY2FsIG1hY3JvLWVudmlyb25tZW50IGVmZmVjdCBkb21pbmF0ZXMgdGhlIHZhcmlhbmNlIHBhcnRpdGlvbi4NCi0gTGFyZ2UgZ2Vub21pYyB0ZXJtcyBzdWNoIGFzICoqRyoqLCAqKkdHSyoqLCBvciAqKkdBSyoqIGluZGljYXRlIHRoYXQgZ2Vub21pYyBzaW1pbGFyaXR5IGNhcHR1cmVzIGFuIGltcG9ydGFudCBzaGFyZSBvZiB0aGUgdHJhaXQgdmFyaWFiaWxpdHkuDQotIExhcmdlIHBoZW5vbWljIHRlcm1zIHN1Y2ggYXMgKipQKiosICoqUEdLKiosIG9yICoqUEFLKiogaW5kaWNhdGUgc3Ryb25nZXIgY29udHJpYnV0aW9uIG9mIHRoZSBOSVJTLWRlcml2ZWQgc2ltaWxhcml0eSwgYnV0IHRoZXNlIHRlcm1zIHNob3VsZCBiZSBpbnRlcnByZXRlZCB3aXRoIGNhdXRpb24gYmVjYXVzZSBOSVJTIG1heSBhbHNvIHJlZmxlY3Qgc3Ryb25nIHBoeXNpb2xvZ2ljYWwgcmVzcG9uc2UgdG8gdGhlIGVudmlyb25tZW50Lg0KLSBMYXJnZSB3ZWF0aGVyLXJlbGF0ZWQgdGVybXMgc3VjaCBhcyAqKlcqKiwgKipHIMOXIFcqKiwgb3IgKipQIMOXIFcqKiBpbmRpY2F0ZSB0aGF0IHdlYXRoZXItZGVyaXZlZCBjb3ZhcmlhdGVzIGV4cGxhaW4gYWRkaXRpb25hbCBzdHJ1Y3R1cmUuIFRoZXNlIHRlcm1zIGFwcGVhciBtYWlubHkgaW4gbW9kZWxzICoqRXRhMTAtRXRhMTgqKiBhbmQgc2hvdWxkIGJlIGludGVycHJldGVkIGFzIGNvbXBsZW1lbnRhcnkgdG8gdGhlIGNhdGVnb3JpY2FsIGVudmlyb25tZW50IGVmZmVjdCwgbm90IGFzIGEgcmVwbGFjZW1lbnQgZm9yICoqRSoqLg0KLSBBIGxhcmdlICoqUmVzaWR1YWwqKiBzZWdtZW50IGluZGljYXRlcyB0aGF0IGltcG9ydGFudCBzb3VyY2VzIG9mIHZhcmlhYmlsaXR5IHJlbWFpbiB1bmV4cGxhaW5lZCBieSB0aGUgZml0dGVkIG1vZGVsLg0KDQpJbiB0aGUgY29udGV4dCBvZiB0aGlzIGFydGljbGUsIHRoZXNlIHZhcmlhbmNlIGRlY29tcG9zaXRpb25zIGNvbXBsZW1lbnQgdGhlIHByZWRpY3RpdmUgYW5hbHlzZXMgYnkgc2hvd2luZyAqKndoaWNoIGluZm9ybWF0aW9uIHNvdXJjZXMgY29udHJpYnV0ZSB0byBtb2RlbCBzdHJ1Y3R1cmUqKi4gSG93ZXZlciwgYSBsYXJnZSB2YXJpYW5jZSBzaGFyZSBmb3IgYSBjb21wb25lbnQgc2hvdWxkIG5vdCBiZSByZWFkIGF1dG9tYXRpY2FsbHkgYXMgZXZpZGVuY2Ugb2Ygc3Ryb25nZXIgYmlvbG9naWNhbCBjb21wbGVtZW50YXJpdHkgb3Igb2YgYmV0dGVyIHByZWRpY3RpdmUgYWJpbGl0eS4NCg0KIyMgU3RhZ2UgbmF2aWdhdGlvbg0KDQoqKlByZXZpb3VzIHN0YWdlOioqIFtNYXRyaWNlc10obWF0cml6ZXMuaHRtbCkgIA0KKipOZXh0IHN0YWdlOioqIFtQcmVkaWN0aW9uIOKAlCBQYXJ0IEldKGFuYWx5c2lzX3ByZWRpY3Rpb24uaHRtbCk=