Last updated: 2026-04-08

Checks: 7 0

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.


Great! Since the R Markdown file has been committed to the Git repository, you know the exact version of the code that produced these results.

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 ee9b0ba. 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:    analysis/script_tabelas_resultados_pipeline_v2.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
    Ignored:    output/variance_components/bglr_runs/
    Ignored:    output/variance_components/figures/

Untracked files:
    Untracked:  output/results/

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/analysis_prediction_run_outputs.Rmd) and HTML (docs/analysis_prediction_run_outputs.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 d7caa48 WevertonGomesCosta 2026-04-07 update
Rmd bfbf03c WevertonGomesCosta 2026-04-07 update
Rmd e1fdde0 WevertonGomesCosta 2026-04-07 update
Rmd 0b2db18 WevertonGomesCosta 2026-04-07 update
Rmd 2d2b88e WevertonGomesCosta 2026-04-06 update
Rmd 5aac46d WevertonGomesCosta 2026-04-06 update
Rmd d1beaba WevertonGomesCosta 2026-04-06 update
html d1beaba WevertonGomesCosta 2026-04-06 update
Rmd 04efebb WevertonGomesCosta 2026-04-05 update
html 6b32571 WevertonGomesCosta 2026-04-05 update
Rmd 0a6059c WevertonGomesCosta 2026-04-03 update
html f6d5560 WevertonGomesCosta 2026-04-03 update files
Rmd 3c8baec WevertonGomesCosta 2026-04-03 update
Rmd 262e87a WevertonGomesCosta 2026-04-02 update
Rmd 9e064c5 WevertonGomesCosta 2026-04-02 update
Rmd 5581b38 WevertonGomesCosta 2026-04-02 update
Rmd cda6f85 WevertonGomesCosta 2026-04-02 update
html cda6f85 WevertonGomesCosta 2026-04-02 update
Rmd 00ef3ef WevertonGomesCosta 2026-04-01 update
Rmd 731cac2 WevertonGomesCosta 2026-04-01 update

Language / Idioma: English | Português

Module navigation / Navegação do módulo: Part I: setup, models and CV design

1. Introduction

This submodule is the execution counterpart of analysis_prediction.Rmd. Its role is to run the prediction jobs for the reduced catalog of 18 models, save the CSV files under output/results/, and inventory the outputs that will later be consumed by the visualization stage.

The execution contract used here has four operational rules:

  • CV1 and CV2 are derived from the same fit within each fold;
  • CV0 and CV00 are derived from the same fit for each held-out environment within each fold;
  • the fold partition is deterministic for each MODEL × REP × TRAIT task and is reused in both CV blocks;
  • output files are organized explicitly by validation scheme, environment when applicable, trait, repetition, and fold, with the fold represented both in the directory tree and in the file name.

The full fitting stage is preserved here for completeness. When the prediction stage needs to be rerun, the code shown below is the operational version used by the pipeline.

2. Packages and project paths

The execution module needs both the matrix objects created upstream and the packages used by the parallel fitting layer. The path logic below keeps the file runnable from either the project root or the analysis/ directory.

rm(list = ls())
options(scipen = 999)

library(readr)
Warning: pacote 'readr' foi compilado no R versão 4.5.2
library(dplyr)
Warning: pacote 'dplyr' foi compilado no R versão 4.5.2

Anexando pacote: 'dplyr'
Os seguintes objetos são mascarados por 'package:stats':

    filter, lag
Os seguintes objetos são mascarados por 'package:base':

    intersect, setdiff, setequal, union
library(stringr)
library(knitr)
library(BGLR)
library(foreach)
Warning: pacote 'foreach' foi compilado no R versão 4.5.2
library(doParallel)
Warning: pacote 'doParallel' foi compilado no R versão 4.5.2
Carregando pacotes exigidos: iterators
Carregando pacotes exigidos: parallel
library(parallel)
library(tidyr)
Warning: pacote 'tidyr' foi compilado no R versão 4.5.2
project_root <- getwd()

if (!dir.exists(file.path(project_root, "output")) &&
    dir.exists(file.path(project_root, "..", "output"))) {
  project_root <- normalizePath(file.path(project_root, ".."))
}

if (!dir.exists(file.path(project_root, "output"))) {
  stop("Could not find the folder 'output/'. Render this file from the project root or from analysis/.")
}

results_dir <- file.path(project_root, "output", "results")
analysis_tables_dir <- file.path(project_root, "output", "tables", "analysis_prediction")
bglr_runs_dir <- file.path(results_dir, "bglr_runs")

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

3. Reload the execution objects from Part I

This section rebuilds the execution environment when the file is rendered on its own. It follows the contract of analysis_prediction.Rmd and recreates the reduced set of 18 ETA objects exactly as defined in Part I.

matrices_dir <- file.path(project_root, "output", "Matrizes")

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

required_matrix_paths <- file.path(matrices_dir, required_matrix_files)

if (!all(file.exists(required_matrix_paths))) {
  missing_matrix_files <- required_matrix_files[!file.exists(required_matrix_paths)]
  stop(
    paste0(
      "The execution objects required by analysis_prediction_run_outputs were not found in output/Matrizes/. Missing files: ",
      paste(missing_matrix_files, collapse = ", ")
    )
  )
}

Pheno <- readRDS(file.path(matrices_dir, "Pheno.rds"))
ZG   <- readRDS(file.path(matrices_dir, "ZG.rds"))
ZP   <- readRDS(file.path(matrices_dir, "ZP.rds"))
ZE   <- readRDS(file.path(matrices_dir, "ZE.rds"))
ZW   <- readRDS(file.path(matrices_dir, "ZW.rds"))
ZGZE <- readRDS(file.path(matrices_dir, "ZGZE.rds"))
ZPZE <- readRDS(file.path(matrices_dir, "ZPZE.rds"))
ZGZW <- readRDS(file.path(matrices_dir, "ZGZW.rds"))
ZPZW <- readRDS(file.path(matrices_dir, "ZPZW.rds"))
GGK  <- readRDS(file.path(matrices_dir, "GGK.rds"))
PGK  <- readRDS(file.path(matrices_dir, "PGK.rds"))
GAK  <- readRDS(file.path(matrices_dir, "GAK.rds"))
PAK  <- readRDS(file.path(matrices_dir, "PAK.rds"))
GGKE <- readRDS(file.path(matrices_dir, "GGKE.rds"))
PGKE <- readRDS(file.path(matrices_dir, "PGKE.rds"))
GAKE <- readRDS(file.path(matrices_dir, "GAKE.rds"))
PAKE <- readRDS(file.path(matrices_dir, "PAKE.rds"))
GGKW <- readRDS(file.path(matrices_dir, "GGKW.rds"))
PGKW <- readRDS(file.path(matrices_dir, "PGKW.rds"))
GAKW <- readRDS(file.path(matrices_dir, "GAKW.rds"))
PAKW <- readRDS(file.path(matrices_dir, "PAKW.rds"))

if (!exists("Pedigree")) Pedigree <- unique(Pheno$Pedigree)

candidate_traits <- c("GY", "KW")
traits_to_run <- candidate_traits[candidate_traits %in% names(Pheno)]
if (length(traits_to_run) == 0) {
  stop("None of the expected traits (GY, KW) were found in Pheno.")
}

if (!exists("nIter"))   nIter   <- 5000
if (!exists("burnIn"))  burnIn  <- 1000
if (!exists("thin"))    thin    <- 10
if (!exists("num_rep")) num_rep <- 10
if (!exists("n_cores")) n_cores <- max(1L, parallel::detectCores() - 1L)

model_catalog <- data.frame(
  ModelNumber = 1:18,
  Model = paste0("Eta", 1:18),
  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"
  ),
  stringsAsFactors = FALSE
)

env_design <- model.matrix(~ Pheno$Env - 1)

Eta_list <- vector("list", 18)
Eta_list[[1]]  <- list(list(X = env_design, model = "BRR"), list(K = ZG, model = "RKHS"), list(K = ZGZE, model = "RKHS"))
Eta_list[[2]]  <- list(list(X = env_design, model = "BRR"), list(K = ZP, model = "RKHS"), list(K = ZPZE, model = "RKHS"))
Eta_list[[3]]  <- list(list(X = env_design, model = "BRR"), list(K = GGK, model = "RKHS"), list(K = GGKE, model = "RKHS"))
Eta_list[[4]]  <- list(list(X = env_design, model = "BRR"), list(K = PGK, model = "RKHS"), list(K = PGKE, model = "RKHS"))
Eta_list[[5]]  <- list(list(X = env_design, model = "BRR"), list(K = GAK, model = "RKHS"), list(K = GAKE, model = "RKHS"))
Eta_list[[6]]  <- list(list(X = env_design, model = "BRR"), list(K = PAK, model = "RKHS"), list(K = PAKE, model = "RKHS"))
Eta_list[[7]]  <- list(list(X = env_design, model = "BRR"), list(K = ZG, model = "RKHS"), list(K = ZP, model = "RKHS"), list(K = ZGZE, model = "RKHS"), list(K = ZPZE, model = "RKHS"))
Eta_list[[8]]  <- list(list(X = env_design, model = "BRR"), list(K = GGK, model = "RKHS"), list(K = PGK, model = "RKHS"), list(K = GGKE, model = "RKHS"), list(K = PGKE, model = "RKHS"))
Eta_list[[9]]  <- list(list(X = env_design, model = "BRR"), list(K = GAK, model = "RKHS"), list(K = PAK, model = "RKHS"), list(K = GAKE, model = "RKHS"), list(K = PAKE, model = "RKHS"))
Eta_list[[10]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = ZG, model = "RKHS"), list(K = ZGZE, model = "RKHS"), list(K = ZGZW, model = "RKHS"))
Eta_list[[11]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = ZP, model = "RKHS"), list(K = ZPZE, model = "RKHS"), list(K = ZPZW, model = "RKHS"))
Eta_list[[12]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = ZG, model = "RKHS"), list(K = ZP, model = "RKHS"), list(K = ZGZE, model = "RKHS"), list(K = ZPZE, model = "RKHS"), list(K = ZGZW, model = "RKHS"), list(K = ZPZW, model = "RKHS"))
Eta_list[[13]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = GGK, model = "RKHS"), list(K = GGKE, model = "RKHS"), list(K = GGKW, model = "RKHS"))
Eta_list[[14]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = PGK, model = "RKHS"), list(K = PGKE, model = "RKHS"), list(K = PGKW, model = "RKHS"))
Eta_list[[15]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = GGK, model = "RKHS"), list(K = PGK, model = "RKHS"), list(K = GGKE, model = "RKHS"), list(K = PGKE, model = "RKHS"), list(K = GGKW, model = "RKHS"), list(K = PGKW, model = "RKHS"))
Eta_list[[16]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = GAK, model = "RKHS"), list(K = GAKE, model = "RKHS"), list(K = GAKW, model = "RKHS"))
Eta_list[[17]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = PAK, model = "RKHS"), list(K = PAKE, model = "RKHS"), list(K = PAKW, model = "RKHS"))
Eta_list[[18]] <- list(list(X = env_design, model = "BRR"), list(K = ZW, model = "RKHS"), list(K = GAK, model = "RKHS"), list(K = PAK, model = "RKHS"), list(K = GAKE, model = "RKHS"), list(K = PAKE, model = "RKHS"), list(K = GAKW, model = "RKHS"), list(K = PAKW, model = "RKHS"))

4. Define the execution contract

This block restores the operational objects used by the current workflow: the fixed leave-one-environment-out set, the number of folds, and the MODEL × REP × TRAIT task grid for the 18-model catalog. The fitting and summarization logic are written directly inside the CV blocks so that the execution path stays explicit.

envs_leaveout <- c("CS11_WS", "CS11_WW", "CS12_WS", "CS12_WW")
envs_leaveout <- envs_leaveout[envs_leaveout %in% unique(Pheno$Env)]

num_fold <- 5L
n_hybrids <- length(Pedigree)
combos <- expand.grid(
  MODEL = seq_along(Eta_list),
  REP = seq_len(num_rep),
  TRAIT = traits_to_run,
  stringsAsFactors = FALSE
)

5. Start the parallel backend

The backend below mirrors the original workflow: one worker receives one combination of model, repetition, and trait.

useCores <- max(1L, n_cores)
cl <- parallel::makeCluster(useCores)
doParallel::registerDoParallel(cl)

parallel::clusterEvalQ(cl, {
  library(BGLR)
  library(dplyr)
  library(plyr)
  NULL
})

cat("Running on ", useCores, " cores.")

6. Run prediction analyses for CV1 and CV2

This block implements genotype-based validation for the 18-model workflow. The deterministic fold map, the BGLR fit, and the environment-wise summary are all written directly inside the loop so that each step stays explicit. One fit is run per fold, and the resulting predictions are split into CV1 (held-out genotypes) and CV2 (training genotypes under the same fit). In the current standard, both the final CSV summaries and the BGLR fitting artifacts are stored in a rep/fold hierarchy.

foreach(i = seq_len(nrow(combos)),
        .packages = c("BGLR", "dplyr", "plyr")) %dopar% {
  MODEL <- combos$MODEL[i]
  rep_num <- combos$REP[i]
  TRAIT <- as.character(combos$TRAIT[i])

  pheno <- data.frame(Pheno)

  trait_id <- match(TRAIT, trait_reference_order)
  if (is.na(trait_id)) {
    trait_id <- 1L
  }

  seed_value <- as.integer(100000L * MODEL + 100L * rep_num + trait_id)
  old_seed_exists <- exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
  if (old_seed_exists) {
    old_seed <- get(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
  }

  set.seed(seed_value)
  obs_per_fold <- ceiling(length(Pedigree) / num_fold)
  folds <- sample(rep(seq_len(num_fold), each = obs_per_fold, length.out = length(Pedigree)))

  if (old_seed_exists) {
    assign(".Random.seed", old_seed, envir = .GlobalEnv)
  } else if (exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)) {
    rm(".Random.seed", envir = .GlobalEnv)
  }

  folds_df <- data.frame(Pedigree = Pedigree, fold = folds, stringsAsFactors = FALSE)

  for (fold_num in seq_len(num_fold)) {
    rep_label <- sprintf("rep%02d", rep_num)
    fold_label <- sprintf("fold%02d", fold_num)

    cv1_dir <- file.path(results_dir, "CV1", TRAIT, rep_label, fold_label)
    cv2_dir <- file.path(results_dir, "CV2", TRAIT, rep_label, fold_label)
    dir.create(cv1_dir, recursive = TRUE, showWarnings = FALSE)
    dir.create(cv2_dir, recursive = TRUE, showWarnings = FALSE)

    cv1_file <- file.path(cv1_dir, paste0("CV1_", TRAIT, "_Eta", MODEL, "_", rep_label, "_", fold_label, ".csv"))
    cv2_file <- file.path(cv2_dir, paste0("CV2_", TRAIT, "_Eta", MODEL, "_", rep_label, "_", fold_label, ".csv"))

    if (file.exists(cv1_file) && file.exists(cv2_file)) {
      next
    }

    train_geno <- folds_df$Pedigree[folds_df$fold != fold_num]

    yield <- pheno
    yield$Y2 <- NA
    yield$Y2[yield$Pedigree %in% train_geno] <- yield[[TRAIT]][yield$Pedigree %in% train_geno]

    run_dir <- file.path(bglr_runs_dir, "CV1", TRAIT, rep_label, fold_label, paste0("Eta", MODEL))
    dir.create(run_dir, recursive = TRUE, showWarnings = FALSE)

    save_prefix_full <- file.path(run_dir, paste0("CV1_", TRAIT, "_Eta", MODEL, "_", rep_label, "_", fold_label, "_"))

    y_t <- as.numeric(yield$Y2)
    start_time <- Sys.time()
    fit <- BGLR(y = y_t, ETA = Eta_list[[MODEL]], nIter = nIter, burnIn = burnIn, thin = thin, saveAt = save_prefix_full)
    end_time <- Sys.time()
    runtime_full <- as.numeric(difftime(end_time, start_time, units = "secs"))

    yield$yhat <- fit$yHat
    rm(fit)
    gc()

    df_test <- yield[!yield$Pedigree %in% train_geno, ]
    df_train <- yield[yield$Pedigree %in% train_geno, ]

    tmp1 <- df_test %>%
      dplyr::group_by(Env) %>%
      dplyr::summarize(cor = cor(.data[[TRAIT]], yhat, use = "complete.obs"), .groups = "drop") %>%
      as.data.frame()

    tmp2 <- df_train %>%
      dplyr::group_by(Env) %>%
      dplyr::summarize(cor = cor(.data[[TRAIT]], yhat, use = "complete.obs"), .groups = "drop") %>%
      as.data.frame()

    tmp1$Runtime_sec <- runtime_full
    tmp2$Runtime_sec <- runtime_full
    tmp1$Fold <- fold_label
    tmp2$Fold <- fold_label

    write.csv(tmp1, file = cv1_file, row.names = FALSE)
    write.csv(tmp2, file = cv2_file, row.names = FALSE)
  }

  NULL
}

7. Run prediction analyses for CV0 and CV00

This block implements leave-one-environment-out validation for the same 18-model workflow. For each task, the deterministic fold map is rebuilt with the same rule used in CV1/CV2, one fit is run for each env_leave × fold combination, and the predictions are split into CV0 (training genotypes under the environment mask) and CV00 (held-out genotypes under the same mask). As in the genotype-based block, the fold construction, the BGLR fit, and the environment-wise summary are all written directly inside the loop so that the execution path remains explicit. The outputs are written in a rep/fold hierarchy and the BGLR artifacts are preserved in matching persistent folders.

foreach(i = seq_len(nrow(combos)),
        .packages = c("BGLR", "dplyr", "plyr")) %dopar% {
  MODEL <- combos$MODEL[i]
  rep_num <- combos$REP[i]
  TRAIT <- as.character(combos$TRAIT[i])

  pheno <- data.frame(Pheno)

  trait_id <- match(TRAIT, trait_reference_order)
  if (is.na(trait_id)) {
    trait_id <- 1L
  }

  seed_value <- as.integer(100000L * MODEL + 100L * rep_num + trait_id)
  old_seed_exists <- exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
  if (old_seed_exists) {
    old_seed <- get(".Random.seed", envir = .GlobalEnv, inherits = FALSE)
  }

  set.seed(seed_value)
  obs_per_fold <- ceiling(length(Pedigree) / num_fold)
  folds <- sample(rep(seq_len(num_fold), each = obs_per_fold, length.out = length(Pedigree)))

  if (old_seed_exists) {
    assign(".Random.seed", old_seed, envir = .GlobalEnv)
  } else if (exists(".Random.seed", envir = .GlobalEnv, inherits = FALSE)) {
    rm(".Random.seed", envir = .GlobalEnv)
  }

  folds_df <- data.frame(Pedigree = Pedigree, fold = folds, stringsAsFactors = FALSE)

  for (fold_num in seq_len(num_fold)) {
    train_geno <- folds_df$Pedigree[folds_df$fold != fold_num]
    rep_label <- sprintf("rep%02d", rep_num)
    fold_label <- sprintf("fold%02d", fold_num)

    for (env_idx in seq_along(envs_leaveout)) {
      env_name <- envs_leaveout[env_idx]

      cv00_dir <- file.path(results_dir, "CV00", env_name, TRAIT, rep_label, fold_label)
      cv0_dir <- file.path(results_dir, "CV0", env_name, TRAIT, rep_label, fold_label)
      dir.create(cv00_dir, recursive = TRUE, showWarnings = FALSE)
      dir.create(cv0_dir, recursive = TRUE, showWarnings = FALSE)

      cv00_file <- file.path(cv00_dir, paste0("CV00_", TRAIT, "_", env_name, "_Eta", MODEL, "_", rep_label, "_", fold_label, ".csv"))
      cv0_file <- file.path(cv0_dir, paste0("CV0_", TRAIT, "_", env_name, "_Eta", MODEL, "_", rep_label, "_", fold_label, ".csv"))

      if (file.exists(cv00_file) && file.exists(cv0_file)) {
        next
      }

      yield2 <- pheno
      yield2$Y2 <- NA
      yield2$Y2[yield2$Pedigree %in% train_geno] <- yield2[[TRAIT]][yield2$Pedigree %in% train_geno]
      yield2$Y2[yield2$Env == env_name] <- NA

      run_dir <- file.path(bglr_runs_dir, "CV0", env_name, TRAIT, rep_label, fold_label, paste0("Eta", MODEL))
      dir.create(run_dir, recursive = TRUE, showWarnings = FALSE)

      save_prefix_env <- file.path(run_dir, paste0("CV0_", env_name, "_", TRAIT, "_Eta", MODEL, "_", rep_label, "_", fold_label, "_"))

      y_t <- as.numeric(yield2$Y2)
      start_time <- Sys.time()
      fit <- BGLR(y = y_t, ETA = Eta_list[[MODEL]], nIter = nIter, burnIn = burnIn, thin = thin, saveAt = save_prefix_env)
      end_time <- Sys.time()
      runtime_env <- as.numeric(difftime(end_time, start_time, units = "secs"))

      yield2$yhat <- fit$yHat
      rm(fit)
      gc()

      df_test2 <- yield2[!yield2$Pedigree %in% train_geno & yield2$Env == env_name, , drop = FALSE]
      df_train2 <- yield2[yield2$Pedigree %in% train_geno & yield2$Env == env_name, , drop = FALSE]

      tmp00 <- df_test2 %>%
        dplyr::group_by(Env) %>%
        dplyr::summarize(cor = cor(.data[[TRAIT]], yhat, use = "complete.obs"), .groups = "drop") %>%
        as.data.frame()

      tmp0 <- df_train2 %>%
        dplyr::group_by(Env) %>%
        dplyr::summarize(cor = cor(.data[[TRAIT]], yhat, use = "complete.obs"), .groups = "drop") %>%
        as.data.frame()

      tmp00$Runtime_sec <- runtime_env
      tmp0$Runtime_sec <- runtime_env
      tmp00$Fold <- fold_label
      tmp0$Fold <- fold_label

      write.csv(tmp00, file = cv00_file, row.names = FALSE)
      write.csv(tmp0, file = cv0_file, row.names = FALSE)
    }
  }

  NULL
}

8. Stop the parallel backend

stopCluster(cl)
foreach::registerDoSEQ()

9. Inventory of the prediction output files

The inventories below summarize the CSV files produced by the execution stage using the current rep/fold folder contract for the 18-model workflow.

prediction_files <- character(0)

if (dir.exists(results_dir)) {
  prediction_files <- list.files(results_dir, pattern = "\\.csv$", full.names = TRUE, recursive = TRUE)
}

inventory_cv12 <- data.frame()
inventory_cvloo <- data.frame()

if (length(prediction_files) > 0) {
  results_root <- normalizePath(results_dir, winslash = "/")

  for (current_file in prediction_files) {
    current_norm <- normalizePath(current_file, winslash = "/")
    rel_path <- substring(current_norm, nchar(results_root) + 2)
    path_parts <- strsplit(rel_path, "/", fixed = TRUE)[[1]]
    current_name <- basename(current_file)
    current_name_no_ext <- sub("\\.csv$", "", current_name)
    current_parts <- strsplit(current_name_no_ext, "_")[[1]]

    if (length(path_parts) >= 5 && current_parts[1] %in% c("CV1", "CV2")) {
      inventory_cv12 <- dplyr::bind_rows(
        inventory_cv12,
        data.frame(
          relative_path = rel_path,
          file_name = current_name,
          CV = current_parts[1],
          Trait = path_parts[2],
          Rep = path_parts[3],
          Fold = path_parts[4],
          Model = current_parts[3],
          stringsAsFactors = FALSE
        )
      )
    }

    if (length(path_parts) >= 6 && current_parts[1] %in% c("CV0", "CV00")) {
      inventory_cvloo <- dplyr::bind_rows(
        inventory_cvloo,
        data.frame(
          relative_path = rel_path,
          file_name = current_name,
          CV = current_parts[1],
          Env_leave = path_parts[2],
          Trait = path_parts[3],
          Rep = path_parts[4],
          Fold = path_parts[5],
          Model = current_parts[length(current_parts) - 2],
          stringsAsFactors = FALSE
        )
      )
    }
  }
}

write_csv(inventory_cv12, file.path(analysis_tables_dir, "analysis_prediction_inventory_cv12.csv"))
write_csv(inventory_cvloo, file.path(analysis_tables_dir, "analysis_prediction_inventory_cvloo.csv"))

10. Counts of files by validation scheme and trait

counts_by_cv <- data.frame()
counts_by_trait <- data.frame()

if (nrow(inventory_cv12) > 0 || nrow(inventory_cvloo) > 0) {
  combined_inventory <- bind_rows(
    inventory_cv12 %>% mutate(Env_leave = NA_character_),
    inventory_cvloo
  )

  counts_by_cv <- combined_inventory %>%
    count(CV, name = "n_files") %>%
    arrange(CV)

  counts_by_trait <- combined_inventory %>%
    count(CV, Trait, name = "n_files") %>%
    arrange(CV, Trait)
}

write_csv(counts_by_cv, file.path(analysis_tables_dir, "analysis_prediction_counts_by_cv.csv"))
write_csv(counts_by_trait, file.path(analysis_tables_dir, "analysis_prediction_counts_by_trait.csv"))

if (nrow(counts_by_cv) > 0) {
  knitr::kable(counts_by_cv, caption = "Number of prediction files by validation scheme.", align = "cc")
} else {
  cat("Prediction counts by validation scheme are not available yet.
")
}
Number of prediction files by validation scheme.
CV n_files
CV0 7200
CV00 7200
CV1 1800
CV2 1800

11. Examples of the output path pattern

output_examples <- bind_rows(
  tibble::tibble(
    ExampleType = "CV1/CV2",
    relative_path = c(
      "CV1/GY/rep01/fold01/CV1_GY_Eta1_rep01_fold01.csv",
      "CV2/KW/rep10/fold05/CV2_KW_Eta18_rep10_fold05.csv"
    ),
    bglr_example = c(
      "bglr_runs/CV1/GY/rep01/fold01/Eta1/CV1_GY_Eta1_rep01_fold01_GE_RKHS_varU.dat",
      "bglr_runs/CV2/KW/rep10/fold05/Eta18/CV2_KW_Eta18_rep10_fold05_PAKW_RKHS_varU.dat"
    ),
    Meaning = c(
      "Prediction accuracy for CV1, trait GY, model Eta1, repetition 1, fold 1.",
      "Prediction accuracy for CV2, trait KW, model Eta18, repetition 10, fold 5."
    )
  ),
  tibble::tibble(
    ExampleType = "CV0/CV00",
    relative_path = c(
      "CV0/CS11_WS/GY/rep01/fold01/CV0_GY_CS11_WS_Eta1_rep01_fold01.csv",
      "CV00/CS12_WW/KW/rep10/fold02/CV00_KW_CS12_WW_Eta18_rep10_fold02.csv"
    ),
    bglr_example = c(
      "bglr_runs/CV0/CS11_WS/GY/rep01/fold01/Eta1/CV0_CS11_WS_GY_Eta1_rep01_fold01_GW_RKHS_varU.dat",
      "bglr_runs/CV00/CS12_WW/KW/rep10/fold02/Eta18/CV00_CS12_WW_KW_Eta18_rep10_fold02_PAKW_RKHS_varU.dat"
    ),
    Meaning = c(
      "Prediction accuracy for CV0, trait GY, held-out environment CS11_WS, model Eta1, repetition 1, fold 1.",
      "Prediction accuracy for CV00, trait KW, held-out environment CS12_WW, model Eta18, repetition 10, fold 2."
    )
  )
)

write_csv(output_examples, file.path(analysis_tables_dir, "analysis_prediction_output_examples.csv"))
knitr::kable(output_examples, caption = "Examples of output files stored under the current rep/fold layout, together with the scenario-specific prefixes used for the persistent BGLR artifacts.", align = "c")
Examples of output files stored under the current rep/fold layout, together with the scenario-specific prefixes used for the persistent BGLR artifacts.
ExampleType relative_path bglr_example Meaning
CV1/CV2 CV1/GY/rep01/fold01/CV1_GY_Eta1_rep01_fold01.csv bglr_runs/CV1/GY/rep01/fold01/Eta1/CV1_GY_Eta1_rep01_fold01_GE_RKHS_varU.dat Prediction accuracy for CV1, trait GY, model Eta1, repetition 1, fold 1.
CV1/CV2 CV2/KW/rep10/fold05/CV2_KW_Eta18_rep10_fold05.csv bglr_runs/CV2/KW/rep10/fold05/Eta18/CV2_KW_Eta18_rep10_fold05_PAKW_RKHS_varU.dat Prediction accuracy for CV2, trait KW, model Eta18, repetition 10, fold 5.
CV0/CV00 CV0/CS11_WS/GY/rep01/fold01/CV0_GY_CS11_WS_Eta1_rep01_fold01.csv bglr_runs/CV0/CS11_WS/GY/rep01/fold01/Eta1/CV0_CS11_WS_GY_Eta1_rep01_fold01_GW_RKHS_varU.dat Prediction accuracy for CV0, trait GY, held-out environment CS11_WS, model Eta1, repetition 1, fold 1.
CV0/CV00 CV00/CS12_WW/KW/rep10/fold02/CV00_KW_CS12_WW_Eta18_rep10_fold02.csv bglr_runs/CV00/CS12_WW/KW/rep10/fold02/Eta18/CV00_CS12_WW_KW_Eta18_rep10_fold02_PAKW_RKHS_varU.dat Prediction accuracy for CV00, trait KW, held-out environment CS12_WW, model Eta18, repetition 10, fold 2.

12. Note on temporary BGLR artifacts

In the current 18-model workflow, persistent saveAt artifacts are written under output/results/bglr_runs/ during each fit and preserved for later convergence diagnostics. The scenario-specific paths now separate CV1, CV2, CV0, and CV00, and the persisted filenames use the scenario prefix together with explicit effect labels instead of generic ETA_1, ETA_2, and similar placeholders. Therefore, this execution module inventories both the final CSV outputs stored under output/results/ and the hierarchical paths that locate the corresponding BGLR run folders.

13. Final note

This file closes the execution layer of the prediction stage while preserving consistency with the reduced 18-model preprocessing module documented in analysis_prediction.Rmd and with the current rep/fold output contract used by the downstream summaries.

Stage navigation

Previous stage: Prediction — Part I
Next stage: Visualization


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] parallel  stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
[1] tidyr_1.3.2       doParallel_1.0.17 iterators_1.0.14  foreach_1.5.2    
[5] BGLR_1.1.4        knitr_1.50        stringr_1.6.0     dplyr_1.2.0      
[9] readr_2.2.0      

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

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

LS0tCnRpdGxlOiAiUHJlZGljdGlvbiBhbmFseXNlcyAtIHJ1biBwcmVkaWN0aW9uIGFuZCBvdXRwdXRzIgphdXRob3I6CiAgLSBDb3N0YSwgVy4gRy5eW1dldmVydG9uIEdvbWVzIGRhIENvc3RhLCBQb3N0ZG9jdG9yYWwgUmVzZWFyY2hlciwgRGVwYXJ0bWVudCBvZiBTdGF0aXN0aWNzIC0gVW5pdmVyc2lkYWRlIEZlZGVyYWwgZGUgVmnDp29zYSwgd2V2ZXJ0b251ZnZAZ21haWwuY29tXQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCnNpdGU6IHdvcmtmbG93cjo6d2Zsb3dfc2l0ZQp1cmw6IGh0dHBzOi8vd2V2ZXJ0b25nb21lc2Nvc3RhLmdpdGh1Yi5pby9JbnRlZ3JhdGluZy1uaXItZ2Vub21pYy1rZXJuZWwvCm91dHB1dDoKICB3b3JrZmxvd3I6OndmbG93X2h0bWw6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBjb25zb2xlCmdpdGh1Yi1yZXBvOiBXZXZlcnRvbkdvbWVzQ29zdGEvSW50ZWdyYXRpbmctbmlyLWdlbm9taWMta2VybmVsCi0tLQoKKipMYW5ndWFnZSAvIElkaW9tYToqKiBbRW5nbGlzaF0oYW5hbHlzaXNfcHJlZGljdGlvbl9ydW5fb3V0cHV0cy5odG1sKSB8IFtQb3J0dWd1w6pzXShhbmFseXNpc19wcmVkaWN0aW9uX3J1bl9vdXRwdXRzX3B0Lmh0bWwpCgoqKk1vZHVsZSBuYXZpZ2F0aW9uIC8gTmF2ZWdhw6fDo28gZG8gbcOzZHVsbzoqKiBbUGFydCBJOiBzZXR1cCwgbW9kZWxzIGFuZCBDViBkZXNpZ25dKGFuYWx5c2lzX3ByZWRpY3Rpb24uaHRtbCkKCiMjIDEuIEludHJvZHVjdGlvbgoKVGhpcyBzdWJtb2R1bGUgaXMgdGhlIGV4ZWN1dGlvbiBjb3VudGVycGFydCBvZiBgYW5hbHlzaXNfcHJlZGljdGlvbi5SbWRgLiBJdHMgcm9sZSBpcyB0byBydW4gdGhlIHByZWRpY3Rpb24gam9icyBmb3IgdGhlIHJlZHVjZWQgY2F0YWxvZyBvZiAxOCBtb2RlbHMsIHNhdmUgdGhlIENTViBmaWxlcyB1bmRlciBgb3V0cHV0L3Jlc3VsdHMvYCwgYW5kIGludmVudG9yeSB0aGUgb3V0cHV0cyB0aGF0IHdpbGwgbGF0ZXIgYmUgY29uc3VtZWQgYnkgdGhlIHZpc3VhbGl6YXRpb24gc3RhZ2UuCgpUaGUgZXhlY3V0aW9uIGNvbnRyYWN0IHVzZWQgaGVyZSBoYXMgZm91ciBvcGVyYXRpb25hbCBydWxlczoKCi0gYENWMWAgYW5kIGBDVjJgIGFyZSBkZXJpdmVkIGZyb20gdGhlIHNhbWUgZml0IHdpdGhpbiBlYWNoIGZvbGQ7Ci0gYENWMGAgYW5kIGBDVjAwYCBhcmUgZGVyaXZlZCBmcm9tIHRoZSBzYW1lIGZpdCBmb3IgZWFjaCBoZWxkLW91dCBlbnZpcm9ubWVudCB3aXRoaW4gZWFjaCBmb2xkOwotIHRoZSBmb2xkIHBhcnRpdGlvbiBpcyBkZXRlcm1pbmlzdGljIGZvciBlYWNoIGBNT0RFTCDDlyBSRVAgw5cgVFJBSVRgIHRhc2sgYW5kIGlzIHJldXNlZCBpbiBib3RoIENWIGJsb2NrczsKLSBvdXRwdXQgZmlsZXMgYXJlIG9yZ2FuaXplZCBleHBsaWNpdGx5IGJ5IHZhbGlkYXRpb24gc2NoZW1lLCBlbnZpcm9ubWVudCB3aGVuIGFwcGxpY2FibGUsIHRyYWl0LCByZXBldGl0aW9uLCBhbmQgZm9sZCwgd2l0aCB0aGUgZm9sZCByZXByZXNlbnRlZCBib3RoIGluIHRoZSBkaXJlY3RvcnkgdHJlZSBhbmQgaW4gdGhlIGZpbGUgbmFtZS4KClRoZSBmdWxsIGZpdHRpbmcgc3RhZ2UgaXMgcHJlc2VydmVkIGhlcmUgZm9yIGNvbXBsZXRlbmVzcy4gV2hlbiB0aGUgcHJlZGljdGlvbiBzdGFnZSBuZWVkcyB0byBiZSByZXJ1biwgdGhlIGNvZGUgc2hvd24gYmVsb3cgaXMgdGhlIG9wZXJhdGlvbmFsIHZlcnNpb24gdXNlZCBieSB0aGUgcGlwZWxpbmUuCgojIyAyLiBQYWNrYWdlcyBhbmQgcHJvamVjdCBwYXRocwoKVGhlIGV4ZWN1dGlvbiBtb2R1bGUgbmVlZHMgYm90aCB0aGUgbWF0cml4IG9iamVjdHMgY3JlYXRlZCB1cHN0cmVhbSBhbmQgdGhlIHBhY2thZ2VzIHVzZWQgYnkgdGhlIHBhcmFsbGVsIGZpdHRpbmcgbGF5ZXIuIFRoZSBwYXRoIGxvZ2ljIGJlbG93IGtlZXBzIHRoZSBmaWxlIHJ1bm5hYmxlIGZyb20gZWl0aGVyIHRoZSBwcm9qZWN0IHJvb3Qgb3IgdGhlIGBhbmFseXNpcy9gIGRpcmVjdG9yeS4KCgpgYGB7ciBzZXR1cH0Kcm0obGlzdCA9IGxzKCkpCm9wdGlvbnMoc2NpcGVuID0gOTk5KQoKbGlicmFyeShyZWFkcikKbGlicmFyeShkcGx5cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KEJHTFIpCmxpYnJhcnkoZm9yZWFjaCkKbGlicmFyeShkb1BhcmFsbGVsKQpsaWJyYXJ5KHBhcmFsbGVsKQpsaWJyYXJ5KHRpZHlyKQoKcHJvamVjdF9yb290IDwtIGdldHdkKCkKCmlmICghZGlyLmV4aXN0cyhmaWxlLnBhdGgocHJvamVjdF9yb290LCAib3V0cHV0IikpICYmCiAgICBkaXIuZXhpc3RzKGZpbGUucGF0aChwcm9qZWN0X3Jvb3QsICIuLiIsICJvdXRwdXQiKSkpIHsKICBwcm9qZWN0X3Jvb3QgPC0gbm9ybWFsaXplUGF0aChmaWxlLnBhdGgocHJvamVjdF9yb290LCAiLi4iKSkKfQoKaWYgKCFkaXIuZXhpc3RzKGZpbGUucGF0aChwcm9qZWN0X3Jvb3QsICJvdXRwdXQiKSkpIHsKICBzdG9wKCJDb3VsZCBub3QgZmluZCB0aGUgZm9sZGVyICdvdXRwdXQvJy4gUmVuZGVyIHRoaXMgZmlsZSBmcm9tIHRoZSBwcm9qZWN0IHJvb3Qgb3IgZnJvbSBhbmFseXNpcy8uIikKfQoKcmVzdWx0c19kaXIgPC0gZmlsZS5wYXRoKHByb2plY3Rfcm9vdCwgIm91dHB1dCIsICJyZXN1bHRzIikKYW5hbHlzaXNfdGFibGVzX2RpciA8LSBmaWxlLnBhdGgocHJvamVjdF9yb290LCAib3V0cHV0IiwgInRhYmxlcyIsICJhbmFseXNpc19wcmVkaWN0aW9uIikKYmdscl9ydW5zX2RpciA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJiZ2xyX3J1bnMiKQoKZGlyLmNyZWF0ZShyZXN1bHRzX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmRpci5jcmVhdGUoYW5hbHlzaXNfdGFibGVzX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmRpci5jcmVhdGUoYmdscl9ydW5zX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCmBgYAoKCiMjIDMuIFJlbG9hZCB0aGUgZXhlY3V0aW9uIG9iamVjdHMgZnJvbSBQYXJ0IEkKClRoaXMgc2VjdGlvbiByZWJ1aWxkcyB0aGUgZXhlY3V0aW9uIGVudmlyb25tZW50IHdoZW4gdGhlIGZpbGUgaXMgcmVuZGVyZWQgb24gaXRzIG93bi4gSXQgZm9sbG93cyB0aGUgY29udHJhY3Qgb2YgYGFuYWx5c2lzX3ByZWRpY3Rpb24uUm1kYCBhbmQgcmVjcmVhdGVzIHRoZSByZWR1Y2VkIHNldCBvZiAxOCBFVEEgb2JqZWN0cyBleGFjdGx5IGFzIGRlZmluZWQgaW4gUGFydCBJLgoKYGBge3IgbG9hZC1yZXF1aXJlZC1vYmplY3RzLCBldmFsPUZBTFNFfQptYXRyaWNlc19kaXIgPC0gZmlsZS5wYXRoKHByb2plY3Rfcm9vdCwgIm91dHB1dCIsICJNYXRyaXplcyIpCgpyZXF1aXJlZF9tYXRyaXhfZmlsZXMgPC0gYygKICAiUGhlbm8ucmRzIiwKICAiWkcucmRzIiwgIlpQLnJkcyIsICJaRS5yZHMiLCAiWlcucmRzIiwKICAiWkdaRS5yZHMiLCAiWlBaRS5yZHMiLCAiWkdaVy5yZHMiLCAiWlBaVy5yZHMiLAogICJHR0sucmRzIiwgIlBHSy5yZHMiLCAiR0FLLnJkcyIsICJQQUsucmRzIiwKICAiR0dLRS5yZHMiLCAiUEdLRS5yZHMiLCAiR0FLRS5yZHMiLCAiUEFLRS5yZHMiLAogICJHR0tXLnJkcyIsICJQR0tXLnJkcyIsICJHQUtXLnJkcyIsICJQQUtXLnJkcyIKKQoKcmVxdWlyZWRfbWF0cml4X3BhdGhzIDwtIGZpbGUucGF0aChtYXRyaWNlc19kaXIsIHJlcXVpcmVkX21hdHJpeF9maWxlcykKCmlmICghYWxsKGZpbGUuZXhpc3RzKHJlcXVpcmVkX21hdHJpeF9wYXRocykpKSB7CiAgbWlzc2luZ19tYXRyaXhfZmlsZXMgPC0gcmVxdWlyZWRfbWF0cml4X2ZpbGVzWyFmaWxlLmV4aXN0cyhyZXF1aXJlZF9tYXRyaXhfcGF0aHMpXQogIHN0b3AoCiAgICBwYXN0ZTAoCiAgICAgICJUaGUgZXhlY3V0aW9uIG9iamVjdHMgcmVxdWlyZWQgYnkgYW5hbHlzaXNfcHJlZGljdGlvbl9ydW5fb3V0cHV0cyB3ZXJlIG5vdCBmb3VuZCBpbiBvdXRwdXQvTWF0cml6ZXMvLiBNaXNzaW5nIGZpbGVzOiAiLAogICAgICBwYXN0ZShtaXNzaW5nX21hdHJpeF9maWxlcywgY29sbGFwc2UgPSAiLCAiKQogICAgKQogICkKfQoKUGhlbm8gPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiUGhlbm8ucmRzIikpClpHICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiWkcucmRzIikpClpQICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiWlAucmRzIikpClpFICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiWkUucmRzIikpClpXICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiWlcucmRzIikpClpHWkUgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiWkdaRS5yZHMiKSkKWlBaRSA8LSByZWFkUkRTKGZpbGUucGF0aChtYXRyaWNlc19kaXIsICJaUFpFLnJkcyIpKQpaR1pXIDwtIHJlYWRSRFMoZmlsZS5wYXRoKG1hdHJpY2VzX2RpciwgIlpHWlcucmRzIikpClpQWlcgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiWlBaVy5yZHMiKSkKR0dLICA8LSByZWFkUkRTKGZpbGUucGF0aChtYXRyaWNlc19kaXIsICJHR0sucmRzIikpClBHSyAgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiUEdLLnJkcyIpKQpHQUsgIDwtIHJlYWRSRFMoZmlsZS5wYXRoKG1hdHJpY2VzX2RpciwgIkdBSy5yZHMiKSkKUEFLICA8LSByZWFkUkRTKGZpbGUucGF0aChtYXRyaWNlc19kaXIsICJQQUsucmRzIikpCkdHS0UgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiR0dLRS5yZHMiKSkKUEdLRSA8LSByZWFkUkRTKGZpbGUucGF0aChtYXRyaWNlc19kaXIsICJQR0tFLnJkcyIpKQpHQUtFIDwtIHJlYWRSRFMoZmlsZS5wYXRoKG1hdHJpY2VzX2RpciwgIkdBS0UucmRzIikpClBBS0UgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiUEFLRS5yZHMiKSkKR0dLVyA8LSByZWFkUkRTKGZpbGUucGF0aChtYXRyaWNlc19kaXIsICJHR0tXLnJkcyIpKQpQR0tXIDwtIHJlYWRSRFMoZmlsZS5wYXRoKG1hdHJpY2VzX2RpciwgIlBHS1cucmRzIikpCkdBS1cgPC0gcmVhZFJEUyhmaWxlLnBhdGgobWF0cmljZXNfZGlyLCAiR0FLVy5yZHMiKSkKUEFLVyA8LSByZWFkUkRTKGZpbGUucGF0aChtYXRyaWNlc19kaXIsICJQQUtXLnJkcyIpKQoKaWYgKCFleGlzdHMoIlBlZGlncmVlIikpIFBlZGlncmVlIDwtIHVuaXF1ZShQaGVubyRQZWRpZ3JlZSkKCmNhbmRpZGF0ZV90cmFpdHMgPC0gYygiR1kiLCAiS1ciKQp0cmFpdHNfdG9fcnVuIDwtIGNhbmRpZGF0ZV90cmFpdHNbY2FuZGlkYXRlX3RyYWl0cyAlaW4lIG5hbWVzKFBoZW5vKV0KaWYgKGxlbmd0aCh0cmFpdHNfdG9fcnVuKSA9PSAwKSB7CiAgc3RvcCgiTm9uZSBvZiB0aGUgZXhwZWN0ZWQgdHJhaXRzIChHWSwgS1cpIHdlcmUgZm91bmQgaW4gUGhlbm8uIikKfQoKaWYgKCFleGlzdHMoIm5JdGVyIikpICAgbkl0ZXIgICA8LSA1MDAwCmlmICghZXhpc3RzKCJidXJuSW4iKSkgIGJ1cm5JbiAgPC0gMTAwMAppZiAoIWV4aXN0cygidGhpbiIpKSAgICB0aGluICAgIDwtIDEwCmlmICghZXhpc3RzKCJudW1fcmVwIikpIG51bV9yZXAgPC0gMTAKaWYgKCFleGlzdHMoIm5fY29yZXMiKSkgbl9jb3JlcyA8LSBtYXgoMUwsIHBhcmFsbGVsOjpkZXRlY3RDb3JlcygpIC0gMUwpCgptb2RlbF9jYXRhbG9nIDwtIGRhdGEuZnJhbWUoCiAgTW9kZWxOdW1iZXIgPSAxOjE4LAogIE1vZGVsID0gcGFzdGUwKCJFdGEiLCAxOjE4KSwKICBEZXNjcmlwdGlvbiA9IGMoCiAgICAiRSArIEcgKyBHRSIsCiAgICAiRSArIFAgKyBQRSIsCiAgICAiRSArIEdHSyArIEdHS0UiLAogICAgIkUgKyBQR0sgKyBQR0tFIiwKICAgICJFICsgR0FLICsgR0FLRSIsCiAgICAiRSArIFBBSyArIFBBS0UiLAogICAgIkUgKyBHICsgUCArIEdFICsgUEUiLAogICAgIkUgKyBHR0sgKyBQR0sgKyBHR0tFICsgUEdLRSIsCiAgICAiRSArIEdBSyArIFBBSyArIEdBS0UgKyBQQUtFIiwKICAgICJFICsgVyArIEcgKyBHRSArIEdXIiwKICAgICJFICsgVyArIFAgKyBQRSArIFBXIiwKICAgICJFICsgVyArIEcgKyBQICsgR0UgKyBQRSArIEdXICsgUFciLAogICAgIkUgKyBXICsgR0dLICsgR0dLRSArIEdHS1ciLAogICAgIkUgKyBXICsgUEdLICsgUEdLRSArIFBHS1ciLAogICAgIkUgKyBXICsgR0dLICsgUEdLICsgR0dLRSArIFBHS0UgKyBHR0tXICsgUEdLVyIsCiAgICAiRSArIFcgKyBHQUsgKyBHQUtFICsgR0FLVyIsCiAgICAiRSArIFcgKyBQQUsgKyBQQUtFICsgUEFLVyIsCiAgICAiRSArIFcgKyBHQUsgKyBQQUsgKyBHQUtFICsgUEFLRSArIEdBS1cgKyBQQUtXIgogICksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCmVudl9kZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gUGhlbm8kRW52IC0gMSkKCkV0YV9saXN0IDwtIHZlY3RvcigibGlzdCIsIDE4KQpFdGFfbGlzdFtbMV1dICA8LSBsaXN0KGxpc3QoWCA9IGVudl9kZXNpZ24sIG1vZGVsID0gIkJSUiIpLCBsaXN0KEsgPSBaRywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBaR1pFLCBtb2RlbCA9ICJSS0hTIikpCkV0YV9saXN0W1syXV0gIDwtIGxpc3QobGlzdChYID0gZW52X2Rlc2lnbiwgbW9kZWwgPSAiQlJSIiksIGxpc3QoSyA9IFpQLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFpQWkUsIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzNdXSAgPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gR0dLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IEdHS0UsIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzRdXSAgPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gUEdLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBHS0UsIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzVdXSAgPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gR0FLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IEdBS0UsIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzZdXSAgPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gUEFLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBBS0UsIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzddXSAgPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gWkcsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWlAsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWkdaRSwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBaUFpFLCBtb2RlbCA9ICJSS0hTIikpCkV0YV9saXN0W1s4XV0gIDwtIGxpc3QobGlzdChYID0gZW52X2Rlc2lnbiwgbW9kZWwgPSAiQlJSIiksIGxpc3QoSyA9IEdHSywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBQR0ssIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gR0dLRSwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBQR0tFLCBtb2RlbCA9ICJSS0hTIikpCkV0YV9saXN0W1s5XV0gIDwtIGxpc3QobGlzdChYID0gZW52X2Rlc2lnbiwgbW9kZWwgPSAiQlJSIiksIGxpc3QoSyA9IEdBSywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBQQUssIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gR0FLRSwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBQQUtFLCBtb2RlbCA9ICJSS0hTIikpCkV0YV9saXN0W1sxMF1dIDwtIGxpc3QobGlzdChYID0gZW52X2Rlc2lnbiwgbW9kZWwgPSAiQlJSIiksIGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFpHLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFpHWkUsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWkdaVywgbW9kZWwgPSAiUktIUyIpKQpFdGFfbGlzdFtbMTFdXSA8LSBsaXN0KGxpc3QoWCA9IGVudl9kZXNpZ24sIG1vZGVsID0gIkJSUiIpLCBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBaUCwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBaUFpFLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFpQWlcsIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzEyXV0gPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gWlcsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWkcsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWlAsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWkdaRSwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBaUFpFLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFpHWlcsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gWlBaVywgbW9kZWwgPSAiUktIUyIpKQpFdGFfbGlzdFtbMTNdXSA8LSBsaXN0KGxpc3QoWCA9IGVudl9kZXNpZ24sIG1vZGVsID0gIkJSUiIpLCBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBHR0ssIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gR0dLRSwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBHR0tXLCBtb2RlbCA9ICJSS0hTIikpCkV0YV9saXN0W1sxNF1dIDwtIGxpc3QobGlzdChYID0gZW52X2Rlc2lnbiwgbW9kZWwgPSAiQlJSIiksIGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBHSywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBQR0tFLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBHS1csIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzE1XV0gPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gWlcsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gR0dLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBHSywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBHR0tFLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBHS0UsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gR0dLVywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBQR0tXLCBtb2RlbCA9ICJSS0hTIikpCkV0YV9saXN0W1sxNl1dIDwtIGxpc3QobGlzdChYID0gZW52X2Rlc2lnbiwgbW9kZWwgPSAiQlJSIiksIGxpc3QoSyA9IFpXLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IEdBSywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBHQUtFLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IEdBS1csIG1vZGVsID0gIlJLSFMiKSkKRXRhX2xpc3RbWzE3XV0gPC0gbGlzdChsaXN0KFggPSBlbnZfZGVzaWduLCBtb2RlbCA9ICJCUlIiKSwgbGlzdChLID0gWlcsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gUEFLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBBS0UsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gUEFLVywgbW9kZWwgPSAiUktIUyIpKQpFdGFfbGlzdFtbMThdXSA8LSBsaXN0KGxpc3QoWCA9IGVudl9kZXNpZ24sIG1vZGVsID0gIkJSUiIpLCBsaXN0KEsgPSBaVywgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBHQUssIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gUEFLLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IEdBS0UsIG1vZGVsID0gIlJLSFMiKSwgbGlzdChLID0gUEFLRSwgbW9kZWwgPSAiUktIUyIpLCBsaXN0KEsgPSBHQUtXLCBtb2RlbCA9ICJSS0hTIiksIGxpc3QoSyA9IFBBS1csIG1vZGVsID0gIlJLSFMiKSkKCmBgYAoKCiMjIDQuIERlZmluZSB0aGUgZXhlY3V0aW9uIGNvbnRyYWN0CgpUaGlzIGJsb2NrIHJlc3RvcmVzIHRoZSBvcGVyYXRpb25hbCBvYmplY3RzIHVzZWQgYnkgdGhlIGN1cnJlbnQgd29ya2Zsb3c6IHRoZSBmaXhlZCBsZWF2ZS1vbmUtZW52aXJvbm1lbnQtb3V0IHNldCwgdGhlIG51bWJlciBvZiBmb2xkcywgYW5kIHRoZSBgTU9ERUwgw5cgUkVQIMOXIFRSQUlUYCB0YXNrIGdyaWQgZm9yIHRoZSAxOC1tb2RlbCBjYXRhbG9nLiBUaGUgZml0dGluZyBhbmQgc3VtbWFyaXphdGlvbiBsb2dpYyBhcmUgd3JpdHRlbiBkaXJlY3RseSBpbnNpZGUgdGhlIENWIGJsb2NrcyBzbyB0aGF0IHRoZSBleGVjdXRpb24gcGF0aCBzdGF5cyBleHBsaWNpdC4KCmBgYHtyIGRlZmluZS1ydW4tb2JqZWN0cywgZXZhbD1GQUxTRX0KZW52c19sZWF2ZW91dCA8LSBjKCJDUzExX1dTIiwgIkNTMTFfV1ciLCAiQ1MxMl9XUyIsICJDUzEyX1dXIikKZW52c19sZWF2ZW91dCA8LSBlbnZzX2xlYXZlb3V0W2VudnNfbGVhdmVvdXQgJWluJSB1bmlxdWUoUGhlbm8kRW52KV0KCm51bV9mb2xkIDwtIDVMCm5faHlicmlkcyA8LSBsZW5ndGgoUGVkaWdyZWUpCmNvbWJvcyA8LSBleHBhbmQuZ3JpZCgKICBNT0RFTCA9IHNlcV9hbG9uZyhFdGFfbGlzdCksCiAgUkVQID0gc2VxX2xlbihudW1fcmVwKSwKICBUUkFJVCA9IHRyYWl0c190b19ydW4sCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKYGBgCgojIyA1LiBTdGFydCB0aGUgcGFyYWxsZWwgYmFja2VuZAoKVGhlIGJhY2tlbmQgYmVsb3cgbWlycm9ycyB0aGUgb3JpZ2luYWwgd29ya2Zsb3c6IG9uZSB3b3JrZXIgcmVjZWl2ZXMgb25lIGNvbWJpbmF0aW9uIG9mIG1vZGVsLCByZXBldGl0aW9uLCBhbmQgdHJhaXQuCgpgYGB7ciBzdGFydC1wYXJhbGxlbC1iYWNrZW5kLCBldmFsPUZBTFNFfQp1c2VDb3JlcyA8LSBtYXgoMUwsIG5fY29yZXMpCmNsIDwtIHBhcmFsbGVsOjptYWtlQ2x1c3Rlcih1c2VDb3JlcykKZG9QYXJhbGxlbDo6cmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQoKcGFyYWxsZWw6OmNsdXN0ZXJFdmFsUShjbCwgewogIGxpYnJhcnkoQkdMUikKICBsaWJyYXJ5KGRwbHlyKQogIGxpYnJhcnkocGx5cikKICBOVUxMCn0pCgpjYXQoIlJ1bm5pbmcgb24gIiwgdXNlQ29yZXMsICIgY29yZXMuIikKYGBgCgoKIyMgNi4gUnVuIHByZWRpY3Rpb24gYW5hbHlzZXMgZm9yIENWMSBhbmQgQ1YyCgpUaGlzIGJsb2NrIGltcGxlbWVudHMgZ2Vub3R5cGUtYmFzZWQgdmFsaWRhdGlvbiBmb3IgdGhlIDE4LW1vZGVsIHdvcmtmbG93LiBUaGUgZGV0ZXJtaW5pc3RpYyBmb2xkIG1hcCwgdGhlIEJHTFIgZml0LCBhbmQgdGhlIGVudmlyb25tZW50LXdpc2Ugc3VtbWFyeSBhcmUgYWxsIHdyaXR0ZW4gZGlyZWN0bHkgaW5zaWRlIHRoZSBsb29wIHNvIHRoYXQgZWFjaCBzdGVwIHN0YXlzIGV4cGxpY2l0LiBPbmUgZml0IGlzIHJ1biBwZXIgZm9sZCwgYW5kIHRoZSByZXN1bHRpbmcgcHJlZGljdGlvbnMgYXJlIHNwbGl0IGludG8gYENWMWAgKGhlbGQtb3V0IGdlbm90eXBlcykgYW5kIGBDVjJgICh0cmFpbmluZyBnZW5vdHlwZXMgdW5kZXIgdGhlIHNhbWUgZml0KS4gSW4gdGhlIGN1cnJlbnQgc3RhbmRhcmQsIGJvdGggdGhlIGZpbmFsIENTViBzdW1tYXJpZXMgYW5kIHRoZSBCR0xSIGZpdHRpbmcgYXJ0aWZhY3RzIGFyZSBzdG9yZWQgaW4gYSByZXAvZm9sZCBoaWVyYXJjaHkuCgpgYGB7ciBydW4tcHJlZGljdGlvbnMtY3YxMiwgZXZhbD1GQUxTRX0KZm9yZWFjaChpID0gc2VxX2xlbihucm93KGNvbWJvcykpLAogICAgICAgIC5wYWNrYWdlcyA9IGMoIkJHTFIiLCAiZHBseXIiLCAicGx5ciIpKSAlZG9wYXIlIHsKICBNT0RFTCA8LSBjb21ib3MkTU9ERUxbaV0KICByZXBfbnVtIDwtIGNvbWJvcyRSRVBbaV0KICBUUkFJVCA8LSBhcy5jaGFyYWN0ZXIoY29tYm9zJFRSQUlUW2ldKQoKICBwaGVubyA8LSBkYXRhLmZyYW1lKFBoZW5vKQoKICB0cmFpdF9pZCA8LSBtYXRjaChUUkFJVCwgdHJhaXRfcmVmZXJlbmNlX29yZGVyKQogIGlmIChpcy5uYSh0cmFpdF9pZCkpIHsKICAgIHRyYWl0X2lkIDwtIDFMCiAgfQoKICBzZWVkX3ZhbHVlIDwtIGFzLmludGVnZXIoMTAwMDAwTCAqIE1PREVMICsgMTAwTCAqIHJlcF9udW0gKyB0cmFpdF9pZCkKICBvbGRfc2VlZF9leGlzdHMgPC0gZXhpc3RzKCIuUmFuZG9tLnNlZWQiLCBlbnZpciA9IC5HbG9iYWxFbnYsIGluaGVyaXRzID0gRkFMU0UpCiAgaWYgKG9sZF9zZWVkX2V4aXN0cykgewogICAgb2xkX3NlZWQgPC0gZ2V0KCIuUmFuZG9tLnNlZWQiLCBlbnZpciA9IC5HbG9iYWxFbnYsIGluaGVyaXRzID0gRkFMU0UpCiAgfQoKICBzZXQuc2VlZChzZWVkX3ZhbHVlKQogIG9ic19wZXJfZm9sZCA8LSBjZWlsaW5nKGxlbmd0aChQZWRpZ3JlZSkgLyBudW1fZm9sZCkKICBmb2xkcyA8LSBzYW1wbGUocmVwKHNlcV9sZW4obnVtX2ZvbGQpLCBlYWNoID0gb2JzX3Blcl9mb2xkLCBsZW5ndGgub3V0ID0gbGVuZ3RoKFBlZGlncmVlKSkpCgogIGlmIChvbGRfc2VlZF9leGlzdHMpIHsKICAgIGFzc2lnbigiLlJhbmRvbS5zZWVkIiwgb2xkX3NlZWQsIGVudmlyID0gLkdsb2JhbEVudikKICB9IGVsc2UgaWYgKGV4aXN0cygiLlJhbmRvbS5zZWVkIiwgZW52aXIgPSAuR2xvYmFsRW52LCBpbmhlcml0cyA9IEZBTFNFKSkgewogICAgcm0oIi5SYW5kb20uc2VlZCIsIGVudmlyID0gLkdsb2JhbEVudikKICB9CgogIGZvbGRzX2RmIDwtIGRhdGEuZnJhbWUoUGVkaWdyZWUgPSBQZWRpZ3JlZSwgZm9sZCA9IGZvbGRzLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgogIGZvciAoZm9sZF9udW0gaW4gc2VxX2xlbihudW1fZm9sZCkpIHsKICAgIHJlcF9sYWJlbCA8LSBzcHJpbnRmKCJyZXAlMDJkIiwgcmVwX251bSkKICAgIGZvbGRfbGFiZWwgPC0gc3ByaW50ZigiZm9sZCUwMmQiLCBmb2xkX251bSkKCiAgICBjdjFfZGlyIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwgIkNWMSIsIFRSQUlULCByZXBfbGFiZWwsIGZvbGRfbGFiZWwpCiAgICBjdjJfZGlyIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwgIkNWMiIsIFRSQUlULCByZXBfbGFiZWwsIGZvbGRfbGFiZWwpCiAgICBkaXIuY3JlYXRlKGN2MV9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUsIHNob3dXYXJuaW5ncyA9IEZBTFNFKQogICAgZGlyLmNyZWF0ZShjdjJfZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiAgICBjdjFfZmlsZSA8LSBmaWxlLnBhdGgoY3YxX2RpciwgcGFzdGUwKCJDVjFfIiwgVFJBSVQsICJfRXRhIiwgTU9ERUwsICJfIiwgcmVwX2xhYmVsLCAiXyIsIGZvbGRfbGFiZWwsICIuY3N2IikpCiAgICBjdjJfZmlsZSA8LSBmaWxlLnBhdGgoY3YyX2RpciwgcGFzdGUwKCJDVjJfIiwgVFJBSVQsICJfRXRhIiwgTU9ERUwsICJfIiwgcmVwX2xhYmVsLCAiXyIsIGZvbGRfbGFiZWwsICIuY3N2IikpCgogICAgaWYgKGZpbGUuZXhpc3RzKGN2MV9maWxlKSAmJiBmaWxlLmV4aXN0cyhjdjJfZmlsZSkpIHsKICAgICAgbmV4dAogICAgfQoKICAgIHRyYWluX2dlbm8gPC0gZm9sZHNfZGYkUGVkaWdyZWVbZm9sZHNfZGYkZm9sZCAhPSBmb2xkX251bV0KCiAgICB5aWVsZCA8LSBwaGVubwogICAgeWllbGQkWTIgPC0gTkEKICAgIHlpZWxkJFkyW3lpZWxkJFBlZGlncmVlICVpbiUgdHJhaW5fZ2Vub10gPC0geWllbGRbW1RSQUlUXV1beWllbGQkUGVkaWdyZWUgJWluJSB0cmFpbl9nZW5vXQoKICAgIHJ1bl9kaXIgPC0gZmlsZS5wYXRoKGJnbHJfcnVuc19kaXIsICJDVjEiLCBUUkFJVCwgcmVwX2xhYmVsLCBmb2xkX2xhYmVsLCBwYXN0ZTAoIkV0YSIsIE1PREVMKSkKICAgIGRpci5jcmVhdGUocnVuX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCgogICAgc2F2ZV9wcmVmaXhfZnVsbCA8LSBmaWxlLnBhdGgocnVuX2RpciwgcGFzdGUwKCJDVjFfIiwgVFJBSVQsICJfRXRhIiwgTU9ERUwsICJfIiwgcmVwX2xhYmVsLCAiXyIsIGZvbGRfbGFiZWwsICJfIikpCgogICAgeV90IDwtIGFzLm51bWVyaWMoeWllbGQkWTIpCiAgICBzdGFydF90aW1lIDwtIFN5cy50aW1lKCkKICAgIGZpdCA8LSBCR0xSKHkgPSB5X3QsIEVUQSA9IEV0YV9saXN0W1tNT0RFTF1dLCBuSXRlciA9IG5JdGVyLCBidXJuSW4gPSBidXJuSW4sIHRoaW4gPSB0aGluLCBzYXZlQXQgPSBzYXZlX3ByZWZpeF9mdWxsKQogICAgZW5kX3RpbWUgPC0gU3lzLnRpbWUoKQogICAgcnVudGltZV9mdWxsIDwtIGFzLm51bWVyaWMoZGlmZnRpbWUoZW5kX3RpbWUsIHN0YXJ0X3RpbWUsIHVuaXRzID0gInNlY3MiKSkKCiAgICB5aWVsZCR5aGF0IDwtIGZpdCR5SGF0CiAgICBybShmaXQpCiAgICBnYygpCgogICAgZGZfdGVzdCA8LSB5aWVsZFsheWllbGQkUGVkaWdyZWUgJWluJSB0cmFpbl9nZW5vLCBdCiAgICBkZl90cmFpbiA8LSB5aWVsZFt5aWVsZCRQZWRpZ3JlZSAlaW4lIHRyYWluX2dlbm8sIF0KCiAgICB0bXAxIDwtIGRmX3Rlc3QgJT4lCiAgICAgIGRwbHlyOjpncm91cF9ieShFbnYpICU+JQogICAgICBkcGx5cjo6c3VtbWFyaXplKGNvciA9IGNvciguZGF0YVtbVFJBSVRdXSwgeWhhdCwgdXNlID0gImNvbXBsZXRlLm9icyIpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICAgICAgYXMuZGF0YS5mcmFtZSgpCgogICAgdG1wMiA8LSBkZl90cmFpbiAlPiUKICAgICAgZHBseXI6Omdyb3VwX2J5KEVudikgJT4lCiAgICAgIGRwbHlyOjpzdW1tYXJpemUoY29yID0gY29yKC5kYXRhW1tUUkFJVF1dLCB5aGF0LCB1c2UgPSAiY29tcGxldGUub2JzIiksIC5ncm91cHMgPSAiZHJvcCIpICU+JQogICAgICBhcy5kYXRhLmZyYW1lKCkKCiAgICB0bXAxJFJ1bnRpbWVfc2VjIDwtIHJ1bnRpbWVfZnVsbAogICAgdG1wMiRSdW50aW1lX3NlYyA8LSBydW50aW1lX2Z1bGwKICAgIHRtcDEkRm9sZCA8LSBmb2xkX2xhYmVsCiAgICB0bXAyJEZvbGQgPC0gZm9sZF9sYWJlbAoKICAgIHdyaXRlLmNzdih0bXAxLCBmaWxlID0gY3YxX2ZpbGUsIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgd3JpdGUuY3N2KHRtcDIsIGZpbGUgPSBjdjJfZmlsZSwgcm93Lm5hbWVzID0gRkFMU0UpCiAgfQoKICBOVUxMCn0KYGBgCiMjIDcuIFJ1biBwcmVkaWN0aW9uIGFuYWx5c2VzIGZvciBDVjAgYW5kIENWMDAKClRoaXMgYmxvY2sgaW1wbGVtZW50cyBsZWF2ZS1vbmUtZW52aXJvbm1lbnQtb3V0IHZhbGlkYXRpb24gZm9yIHRoZSBzYW1lIDE4LW1vZGVsIHdvcmtmbG93LiBGb3IgZWFjaCB0YXNrLCB0aGUgZGV0ZXJtaW5pc3RpYyBmb2xkIG1hcCBpcyByZWJ1aWx0IHdpdGggdGhlIHNhbWUgcnVsZSB1c2VkIGluIGBDVjEvQ1YyYCwgb25lIGZpdCBpcyBydW4gZm9yIGVhY2ggYGVudl9sZWF2ZSDDlyBmb2xkYCBjb21iaW5hdGlvbiwgYW5kIHRoZSBwcmVkaWN0aW9ucyBhcmUgc3BsaXQgaW50byBgQ1YwYCAodHJhaW5pbmcgZ2Vub3R5cGVzIHVuZGVyIHRoZSBlbnZpcm9ubWVudCBtYXNrKSBhbmQgYENWMDBgIChoZWxkLW91dCBnZW5vdHlwZXMgdW5kZXIgdGhlIHNhbWUgbWFzaykuIEFzIGluIHRoZSBnZW5vdHlwZS1iYXNlZCBibG9jaywgdGhlIGZvbGQgY29uc3RydWN0aW9uLCB0aGUgQkdMUiBmaXQsIGFuZCB0aGUgZW52aXJvbm1lbnQtd2lzZSBzdW1tYXJ5IGFyZSBhbGwgd3JpdHRlbiBkaXJlY3RseSBpbnNpZGUgdGhlIGxvb3Agc28gdGhhdCB0aGUgZXhlY3V0aW9uIHBhdGggcmVtYWlucyBleHBsaWNpdC4gVGhlIG91dHB1dHMgYXJlIHdyaXR0ZW4gaW4gYSByZXAvZm9sZCBoaWVyYXJjaHkgYW5kIHRoZSBCR0xSIGFydGlmYWN0cyBhcmUgcHJlc2VydmVkIGluIG1hdGNoaW5nIHBlcnNpc3RlbnQgZm9sZGVycy4KCmBgYHtyIHJ1bi1wcmVkaWN0aW9ucy1jdjAtY3YwMCwgZXZhbD1GQUxTRX0KZm9yZWFjaChpID0gc2VxX2xlbihucm93KGNvbWJvcykpLAogICAgICAgIC5wYWNrYWdlcyA9IGMoIkJHTFIiLCAiZHBseXIiLCAicGx5ciIpKSAlZG9wYXIlIHsKICBNT0RFTCA8LSBjb21ib3MkTU9ERUxbaV0KICByZXBfbnVtIDwtIGNvbWJvcyRSRVBbaV0KICBUUkFJVCA8LSBhcy5jaGFyYWN0ZXIoY29tYm9zJFRSQUlUW2ldKQoKICBwaGVubyA8LSBkYXRhLmZyYW1lKFBoZW5vKQoKICB0cmFpdF9pZCA8LSBtYXRjaChUUkFJVCwgdHJhaXRfcmVmZXJlbmNlX29yZGVyKQogIGlmIChpcy5uYSh0cmFpdF9pZCkpIHsKICAgIHRyYWl0X2lkIDwtIDFMCiAgfQoKICBzZWVkX3ZhbHVlIDwtIGFzLmludGVnZXIoMTAwMDAwTCAqIE1PREVMICsgMTAwTCAqIHJlcF9udW0gKyB0cmFpdF9pZCkKICBvbGRfc2VlZF9leGlzdHMgPC0gZXhpc3RzKCIuUmFuZG9tLnNlZWQiLCBlbnZpciA9IC5HbG9iYWxFbnYsIGluaGVyaXRzID0gRkFMU0UpCiAgaWYgKG9sZF9zZWVkX2V4aXN0cykgewogICAgb2xkX3NlZWQgPC0gZ2V0KCIuUmFuZG9tLnNlZWQiLCBlbnZpciA9IC5HbG9iYWxFbnYsIGluaGVyaXRzID0gRkFMU0UpCiAgfQoKICBzZXQuc2VlZChzZWVkX3ZhbHVlKQogIG9ic19wZXJfZm9sZCA8LSBjZWlsaW5nKGxlbmd0aChQZWRpZ3JlZSkgLyBudW1fZm9sZCkKICBmb2xkcyA8LSBzYW1wbGUocmVwKHNlcV9sZW4obnVtX2ZvbGQpLCBlYWNoID0gb2JzX3Blcl9mb2xkLCBsZW5ndGgub3V0ID0gbGVuZ3RoKFBlZGlncmVlKSkpCgogIGlmIChvbGRfc2VlZF9leGlzdHMpIHsKICAgIGFzc2lnbigiLlJhbmRvbS5zZWVkIiwgb2xkX3NlZWQsIGVudmlyID0gLkdsb2JhbEVudikKICB9IGVsc2UgaWYgKGV4aXN0cygiLlJhbmRvbS5zZWVkIiwgZW52aXIgPSAuR2xvYmFsRW52LCBpbmhlcml0cyA9IEZBTFNFKSkgewogICAgcm0oIi5SYW5kb20uc2VlZCIsIGVudmlyID0gLkdsb2JhbEVudikKICB9CgogIGZvbGRzX2RmIDwtIGRhdGEuZnJhbWUoUGVkaWdyZWUgPSBQZWRpZ3JlZSwgZm9sZCA9IGZvbGRzLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCgogIGZvciAoZm9sZF9udW0gaW4gc2VxX2xlbihudW1fZm9sZCkpIHsKICAgIHRyYWluX2dlbm8gPC0gZm9sZHNfZGYkUGVkaWdyZWVbZm9sZHNfZGYkZm9sZCAhPSBmb2xkX251bV0KICAgIHJlcF9sYWJlbCA8LSBzcHJpbnRmKCJyZXAlMDJkIiwgcmVwX251bSkKICAgIGZvbGRfbGFiZWwgPC0gc3ByaW50ZigiZm9sZCUwMmQiLCBmb2xkX251bSkKCiAgICBmb3IgKGVudl9pZHggaW4gc2VxX2Fsb25nKGVudnNfbGVhdmVvdXQpKSB7CiAgICAgIGVudl9uYW1lIDwtIGVudnNfbGVhdmVvdXRbZW52X2lkeF0KCiAgICAgIGN2MDBfZGlyIDwtIGZpbGUucGF0aChyZXN1bHRzX2RpciwgIkNWMDAiLCBlbnZfbmFtZSwgVFJBSVQsIHJlcF9sYWJlbCwgZm9sZF9sYWJlbCkKICAgICAgY3YwX2RpciA8LSBmaWxlLnBhdGgocmVzdWx0c19kaXIsICJDVjAiLCBlbnZfbmFtZSwgVFJBSVQsIHJlcF9sYWJlbCwgZm9sZF9sYWJlbCkKICAgICAgZGlyLmNyZWF0ZShjdjAwX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCiAgICAgIGRpci5jcmVhdGUoY3YwX2RpciwgcmVjdXJzaXZlID0gVFJVRSwgc2hvd1dhcm5pbmdzID0gRkFMU0UpCgogICAgICBjdjAwX2ZpbGUgPC0gZmlsZS5wYXRoKGN2MDBfZGlyLCBwYXN0ZTAoIkNWMDBfIiwgVFJBSVQsICJfIiwgZW52X25hbWUsICJfRXRhIiwgTU9ERUwsICJfIiwgcmVwX2xhYmVsLCAiXyIsIGZvbGRfbGFiZWwsICIuY3N2IikpCiAgICAgIGN2MF9maWxlIDwtIGZpbGUucGF0aChjdjBfZGlyLCBwYXN0ZTAoIkNWMF8iLCBUUkFJVCwgIl8iLCBlbnZfbmFtZSwgIl9FdGEiLCBNT0RFTCwgIl8iLCByZXBfbGFiZWwsICJfIiwgZm9sZF9sYWJlbCwgIi5jc3YiKSkKCiAgICAgIGlmIChmaWxlLmV4aXN0cyhjdjAwX2ZpbGUpICYmIGZpbGUuZXhpc3RzKGN2MF9maWxlKSkgewogICAgICAgIG5leHQKICAgICAgfQoKICAgICAgeWllbGQyIDwtIHBoZW5vCiAgICAgIHlpZWxkMiRZMiA8LSBOQQogICAgICB5aWVsZDIkWTJbeWllbGQyJFBlZGlncmVlICVpbiUgdHJhaW5fZ2Vub10gPC0geWllbGQyW1tUUkFJVF1dW3lpZWxkMiRQZWRpZ3JlZSAlaW4lIHRyYWluX2dlbm9dCiAgICAgIHlpZWxkMiRZMlt5aWVsZDIkRW52ID09IGVudl9uYW1lXSA8LSBOQQoKICAgICAgcnVuX2RpciA8LSBmaWxlLnBhdGgoYmdscl9ydW5zX2RpciwgIkNWMCIsIGVudl9uYW1lLCBUUkFJVCwgcmVwX2xhYmVsLCBmb2xkX2xhYmVsLCBwYXN0ZTAoIkV0YSIsIE1PREVMKSkKICAgICAgZGlyLmNyZWF0ZShydW5fZGlyLCByZWN1cnNpdmUgPSBUUlVFLCBzaG93V2FybmluZ3MgPSBGQUxTRSkKCiAgICAgIHNhdmVfcHJlZml4X2VudiA8LSBmaWxlLnBhdGgocnVuX2RpciwgcGFzdGUwKCJDVjBfIiwgZW52X25hbWUsICJfIiwgVFJBSVQsICJfRXRhIiwgTU9ERUwsICJfIiwgcmVwX2xhYmVsLCAiXyIsIGZvbGRfbGFiZWwsICJfIikpCgogICAgICB5X3QgPC0gYXMubnVtZXJpYyh5aWVsZDIkWTIpCiAgICAgIHN0YXJ0X3RpbWUgPC0gU3lzLnRpbWUoKQogICAgICBmaXQgPC0gQkdMUih5ID0geV90LCBFVEEgPSBFdGFfbGlzdFtbTU9ERUxdXSwgbkl0ZXIgPSBuSXRlciwgYnVybkluID0gYnVybkluLCB0aGluID0gdGhpbiwgc2F2ZUF0ID0gc2F2ZV9wcmVmaXhfZW52KQogICAgICBlbmRfdGltZSA8LSBTeXMudGltZSgpCiAgICAgIHJ1bnRpbWVfZW52IDwtIGFzLm51bWVyaWMoZGlmZnRpbWUoZW5kX3RpbWUsIHN0YXJ0X3RpbWUsIHVuaXRzID0gInNlY3MiKSkKCiAgICAgIHlpZWxkMiR5aGF0IDwtIGZpdCR5SGF0CiAgICAgIHJtKGZpdCkKICAgICAgZ2MoKQoKICAgICAgZGZfdGVzdDIgPC0geWllbGQyWyF5aWVsZDIkUGVkaWdyZWUgJWluJSB0cmFpbl9nZW5vICYgeWllbGQyJEVudiA9PSBlbnZfbmFtZSwgLCBkcm9wID0gRkFMU0VdCiAgICAgIGRmX3RyYWluMiA8LSB5aWVsZDJbeWllbGQyJFBlZGlncmVlICVpbiUgdHJhaW5fZ2VubyAmIHlpZWxkMiRFbnYgPT0gZW52X25hbWUsICwgZHJvcCA9IEZBTFNFXQoKICAgICAgdG1wMDAgPC0gZGZfdGVzdDIgJT4lCiAgICAgICAgZHBseXI6Omdyb3VwX2J5KEVudikgJT4lCiAgICAgICAgZHBseXI6OnN1bW1hcml6ZShjb3IgPSBjb3IoLmRhdGFbW1RSQUlUXV0sIHloYXQsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgICAgICAgYXMuZGF0YS5mcmFtZSgpCgogICAgICB0bXAwIDwtIGRmX3RyYWluMiAlPiUKICAgICAgICBkcGx5cjo6Z3JvdXBfYnkoRW52KSAlPiUKICAgICAgICBkcGx5cjo6c3VtbWFyaXplKGNvciA9IGNvciguZGF0YVtbVFJBSVRdXSwgeWhhdCwgdXNlID0gImNvbXBsZXRlLm9icyIpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUKICAgICAgICBhcy5kYXRhLmZyYW1lKCkKCiAgICAgIHRtcDAwJFJ1bnRpbWVfc2VjIDwtIHJ1bnRpbWVfZW52CiAgICAgIHRtcDAkUnVudGltZV9zZWMgPC0gcnVudGltZV9lbnYKICAgICAgdG1wMDAkRm9sZCA8LSBmb2xkX2xhYmVsCiAgICAgIHRtcDAkRm9sZCA8LSBmb2xkX2xhYmVsCgogICAgICB3cml0ZS5jc3YodG1wMDAsIGZpbGUgPSBjdjAwX2ZpbGUsIHJvdy5uYW1lcyA9IEZBTFNFKQogICAgICB3cml0ZS5jc3YodG1wMCwgZmlsZSA9IGN2MF9maWxlLCByb3cubmFtZXMgPSBGQUxTRSkKICAgIH0KICB9CgogIE5VTEwKfQpgYGAKIyMgOC4gU3RvcCB0aGUgcGFyYWxsZWwgYmFja2VuZAoKYGBge3Igc3RvcC1wYXJhbGxlbC1iYWNrZW5kLCBldmFsPUZBTFNFfQpzdG9wQ2x1c3RlcihjbCkKZm9yZWFjaDo6cmVnaXN0ZXJEb1NFUSgpCmBgYAoKCiMjIDkuIEludmVudG9yeSBvZiB0aGUgcHJlZGljdGlvbiBvdXRwdXQgZmlsZXMKClRoZSBpbnZlbnRvcmllcyBiZWxvdyBzdW1tYXJpemUgdGhlIENTViBmaWxlcyBwcm9kdWNlZCBieSB0aGUgZXhlY3V0aW9uIHN0YWdlIHVzaW5nIHRoZSBjdXJyZW50IHJlcC9mb2xkIGZvbGRlciBjb250cmFjdCBmb3IgdGhlIDE4LW1vZGVsIHdvcmtmbG93LgoKYGBge3IgcHJlZGljdGlvbi1vdXRwdXQtaW52ZW50b3J5fQpwcmVkaWN0aW9uX2ZpbGVzIDwtIGNoYXJhY3RlcigwKQoKaWYgKGRpci5leGlzdHMocmVzdWx0c19kaXIpKSB7CiAgcHJlZGljdGlvbl9maWxlcyA8LSBsaXN0LmZpbGVzKHJlc3VsdHNfZGlyLCBwYXR0ZXJuID0gIlxcLmNzdiQiLCBmdWxsLm5hbWVzID0gVFJVRSwgcmVjdXJzaXZlID0gVFJVRSkKfQoKaW52ZW50b3J5X2N2MTIgPC0gZGF0YS5mcmFtZSgpCmludmVudG9yeV9jdmxvbyA8LSBkYXRhLmZyYW1lKCkKCmlmIChsZW5ndGgocHJlZGljdGlvbl9maWxlcykgPiAwKSB7CiAgcmVzdWx0c19yb290IDwtIG5vcm1hbGl6ZVBhdGgocmVzdWx0c19kaXIsIHdpbnNsYXNoID0gIi8iKQoKICBmb3IgKGN1cnJlbnRfZmlsZSBpbiBwcmVkaWN0aW9uX2ZpbGVzKSB7CiAgICBjdXJyZW50X25vcm0gPC0gbm9ybWFsaXplUGF0aChjdXJyZW50X2ZpbGUsIHdpbnNsYXNoID0gIi8iKQogICAgcmVsX3BhdGggPC0gc3Vic3RyaW5nKGN1cnJlbnRfbm9ybSwgbmNoYXIocmVzdWx0c19yb290KSArIDIpCiAgICBwYXRoX3BhcnRzIDwtIHN0cnNwbGl0KHJlbF9wYXRoLCAiLyIsIGZpeGVkID0gVFJVRSlbWzFdXQogICAgY3VycmVudF9uYW1lIDwtIGJhc2VuYW1lKGN1cnJlbnRfZmlsZSkKICAgIGN1cnJlbnRfbmFtZV9ub19leHQgPC0gc3ViKCJcXC5jc3YkIiwgIiIsIGN1cnJlbnRfbmFtZSkKICAgIGN1cnJlbnRfcGFydHMgPC0gc3Ryc3BsaXQoY3VycmVudF9uYW1lX25vX2V4dCwgIl8iKVtbMV1dCgogICAgaWYgKGxlbmd0aChwYXRoX3BhcnRzKSA+PSA1ICYmIGN1cnJlbnRfcGFydHNbMV0gJWluJSBjKCJDVjEiLCAiQ1YyIikpIHsKICAgICAgaW52ZW50b3J5X2N2MTIgPC0gZHBseXI6OmJpbmRfcm93cygKICAgICAgICBpbnZlbnRvcnlfY3YxMiwKICAgICAgICBkYXRhLmZyYW1lKAogICAgICAgICAgcmVsYXRpdmVfcGF0aCA9IHJlbF9wYXRoLAogICAgICAgICAgZmlsZV9uYW1lID0gY3VycmVudF9uYW1lLAogICAgICAgICAgQ1YgPSBjdXJyZW50X3BhcnRzWzFdLAogICAgICAgICAgVHJhaXQgPSBwYXRoX3BhcnRzWzJdLAogICAgICAgICAgUmVwID0gcGF0aF9wYXJ0c1szXSwKICAgICAgICAgIEZvbGQgPSBwYXRoX3BhcnRzWzRdLAogICAgICAgICAgTW9kZWwgPSBjdXJyZW50X3BhcnRzWzNdLAogICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICAgICAgKQogICAgICApCiAgICB9CgogICAgaWYgKGxlbmd0aChwYXRoX3BhcnRzKSA+PSA2ICYmIGN1cnJlbnRfcGFydHNbMV0gJWluJSBjKCJDVjAiLCAiQ1YwMCIpKSB7CiAgICAgIGludmVudG9yeV9jdmxvbyA8LSBkcGx5cjo6YmluZF9yb3dzKAogICAgICAgIGludmVudG9yeV9jdmxvbywKICAgICAgICBkYXRhLmZyYW1lKAogICAgICAgICAgcmVsYXRpdmVfcGF0aCA9IHJlbF9wYXRoLAogICAgICAgICAgZmlsZV9uYW1lID0gY3VycmVudF9uYW1lLAogICAgICAgICAgQ1YgPSBjdXJyZW50X3BhcnRzWzFdLAogICAgICAgICAgRW52X2xlYXZlID0gcGF0aF9wYXJ0c1syXSwKICAgICAgICAgIFRyYWl0ID0gcGF0aF9wYXJ0c1szXSwKICAgICAgICAgIFJlcCA9IHBhdGhfcGFydHNbNF0sCiAgICAgICAgICBGb2xkID0gcGF0aF9wYXJ0c1s1XSwKICAgICAgICAgIE1vZGVsID0gY3VycmVudF9wYXJ0c1tsZW5ndGgoY3VycmVudF9wYXJ0cykgLSAyXSwKICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICkKICAgICAgKQogICAgfQogIH0KfQoKd3JpdGVfY3N2KGludmVudG9yeV9jdjEyLCBmaWxlLnBhdGgoYW5hbHlzaXNfdGFibGVzX2RpciwgImFuYWx5c2lzX3ByZWRpY3Rpb25faW52ZW50b3J5X2N2MTIuY3N2IikpCndyaXRlX2NzdihpbnZlbnRvcnlfY3Zsb28sIGZpbGUucGF0aChhbmFseXNpc190YWJsZXNfZGlyLCAiYW5hbHlzaXNfcHJlZGljdGlvbl9pbnZlbnRvcnlfY3Zsb28uY3N2IikpCmBgYAojIyAxMC4gQ291bnRzIG9mIGZpbGVzIGJ5IHZhbGlkYXRpb24gc2NoZW1lIGFuZCB0cmFpdAoKYGBge3IgcHJlZGljdGlvbi1vdXRwdXQtY291bnRzfQpjb3VudHNfYnlfY3YgPC0gZGF0YS5mcmFtZSgpCmNvdW50c19ieV90cmFpdCA8LSBkYXRhLmZyYW1lKCkKCmlmIChucm93KGludmVudG9yeV9jdjEyKSA+IDAgfHwgbnJvdyhpbnZlbnRvcnlfY3Zsb28pID4gMCkgewogIGNvbWJpbmVkX2ludmVudG9yeSA8LSBiaW5kX3Jvd3MoCiAgICBpbnZlbnRvcnlfY3YxMiAlPiUgbXV0YXRlKEVudl9sZWF2ZSA9IE5BX2NoYXJhY3Rlcl8pLAogICAgaW52ZW50b3J5X2N2bG9vCiAgKQoKICBjb3VudHNfYnlfY3YgPC0gY29tYmluZWRfaW52ZW50b3J5ICU+JQogICAgY291bnQoQ1YsIG5hbWUgPSAibl9maWxlcyIpICU+JQogICAgYXJyYW5nZShDVikKCiAgY291bnRzX2J5X3RyYWl0IDwtIGNvbWJpbmVkX2ludmVudG9yeSAlPiUKICAgIGNvdW50KENWLCBUcmFpdCwgbmFtZSA9ICJuX2ZpbGVzIikgJT4lCiAgICBhcnJhbmdlKENWLCBUcmFpdCkKfQoKd3JpdGVfY3N2KGNvdW50c19ieV9jdiwgZmlsZS5wYXRoKGFuYWx5c2lzX3RhYmxlc19kaXIsICJhbmFseXNpc19wcmVkaWN0aW9uX2NvdW50c19ieV9jdi5jc3YiKSkKd3JpdGVfY3N2KGNvdW50c19ieV90cmFpdCwgZmlsZS5wYXRoKGFuYWx5c2lzX3RhYmxlc19kaXIsICJhbmFseXNpc19wcmVkaWN0aW9uX2NvdW50c19ieV90cmFpdC5jc3YiKSkKCmlmIChucm93KGNvdW50c19ieV9jdikgPiAwKSB7CiAga25pdHI6OmthYmxlKGNvdW50c19ieV9jdiwgY2FwdGlvbiA9ICJOdW1iZXIgb2YgcHJlZGljdGlvbiBmaWxlcyBieSB2YWxpZGF0aW9uIHNjaGVtZS4iLCBhbGlnbiA9ICJjYyIpCn0gZWxzZSB7CiAgY2F0KCJQcmVkaWN0aW9uIGNvdW50cyBieSB2YWxpZGF0aW9uIHNjaGVtZSBhcmUgbm90IGF2YWlsYWJsZSB5ZXQuCiIpCn0KYGBgCgoKIyMgMTEuIEV4YW1wbGVzIG9mIHRoZSBvdXRwdXQgcGF0aCBwYXR0ZXJuCgpgYGB7ciBwcmVkaWN0aW9uLW91dHB1dC1leGFtcGxlc30Kb3V0cHV0X2V4YW1wbGVzIDwtIGJpbmRfcm93cygKICB0aWJibGU6OnRpYmJsZSgKICAgIEV4YW1wbGVUeXBlID0gIkNWMS9DVjIiLAogICAgcmVsYXRpdmVfcGF0aCA9IGMoCiAgICAgICJDVjEvR1kvcmVwMDEvZm9sZDAxL0NWMV9HWV9FdGExX3JlcDAxX2ZvbGQwMS5jc3YiLAogICAgICAiQ1YyL0tXL3JlcDEwL2ZvbGQwNS9DVjJfS1dfRXRhMThfcmVwMTBfZm9sZDA1LmNzdiIKICAgICksCiAgICBiZ2xyX2V4YW1wbGUgPSBjKAogICAgICAiYmdscl9ydW5zL0NWMS9HWS9yZXAwMS9mb2xkMDEvRXRhMS9DVjFfR1lfRXRhMV9yZXAwMV9mb2xkMDFfR0VfUktIU192YXJVLmRhdCIsCiAgICAgICJiZ2xyX3J1bnMvQ1YyL0tXL3JlcDEwL2ZvbGQwNS9FdGExOC9DVjJfS1dfRXRhMThfcmVwMTBfZm9sZDA1X1BBS1dfUktIU192YXJVLmRhdCIKICAgICksCiAgICBNZWFuaW5nID0gYygKICAgICAgIlByZWRpY3Rpb24gYWNjdXJhY3kgZm9yIENWMSwgdHJhaXQgR1ksIG1vZGVsIEV0YTEsIHJlcGV0aXRpb24gMSwgZm9sZCAxLiIsCiAgICAgICJQcmVkaWN0aW9uIGFjY3VyYWN5IGZvciBDVjIsIHRyYWl0IEtXLCBtb2RlbCBFdGExOCwgcmVwZXRpdGlvbiAxMCwgZm9sZCA1LiIKICAgICkKICApLAogIHRpYmJsZTo6dGliYmxlKAogICAgRXhhbXBsZVR5cGUgPSAiQ1YwL0NWMDAiLAogICAgcmVsYXRpdmVfcGF0aCA9IGMoCiAgICAgICJDVjAvQ1MxMV9XUy9HWS9yZXAwMS9mb2xkMDEvQ1YwX0dZX0NTMTFfV1NfRXRhMV9yZXAwMV9mb2xkMDEuY3N2IiwKICAgICAgIkNWMDAvQ1MxMl9XVy9LVy9yZXAxMC9mb2xkMDIvQ1YwMF9LV19DUzEyX1dXX0V0YTE4X3JlcDEwX2ZvbGQwMi5jc3YiCiAgICApLAogICAgYmdscl9leGFtcGxlID0gYygKICAgICAgImJnbHJfcnVucy9DVjAvQ1MxMV9XUy9HWS9yZXAwMS9mb2xkMDEvRXRhMS9DVjBfQ1MxMV9XU19HWV9FdGExX3JlcDAxX2ZvbGQwMV9HV19SS0hTX3ZhclUuZGF0IiwKICAgICAgImJnbHJfcnVucy9DVjAwL0NTMTJfV1cvS1cvcmVwMTAvZm9sZDAyL0V0YTE4L0NWMDBfQ1MxMl9XV19LV19FdGExOF9yZXAxMF9mb2xkMDJfUEFLV19SS0hTX3ZhclUuZGF0IgogICAgKSwKICAgIE1lYW5pbmcgPSBjKAogICAgICAiUHJlZGljdGlvbiBhY2N1cmFjeSBmb3IgQ1YwLCB0cmFpdCBHWSwgaGVsZC1vdXQgZW52aXJvbm1lbnQgQ1MxMV9XUywgbW9kZWwgRXRhMSwgcmVwZXRpdGlvbiAxLCBmb2xkIDEuIiwKICAgICAgIlByZWRpY3Rpb24gYWNjdXJhY3kgZm9yIENWMDAsIHRyYWl0IEtXLCBoZWxkLW91dCBlbnZpcm9ubWVudCBDUzEyX1dXLCBtb2RlbCBFdGExOCwgcmVwZXRpdGlvbiAxMCwgZm9sZCAyLiIKICAgICkKICApCikKCndyaXRlX2NzdihvdXRwdXRfZXhhbXBsZXMsIGZpbGUucGF0aChhbmFseXNpc190YWJsZXNfZGlyLCAiYW5hbHlzaXNfcHJlZGljdGlvbl9vdXRwdXRfZXhhbXBsZXMuY3N2IikpCmtuaXRyOjprYWJsZShvdXRwdXRfZXhhbXBsZXMsIGNhcHRpb24gPSAiRXhhbXBsZXMgb2Ygb3V0cHV0IGZpbGVzIHN0b3JlZCB1bmRlciB0aGUgY3VycmVudCByZXAvZm9sZCBsYXlvdXQsIHRvZ2V0aGVyIHdpdGggdGhlIHNjZW5hcmlvLXNwZWNpZmljIHByZWZpeGVzIHVzZWQgZm9yIHRoZSBwZXJzaXN0ZW50IEJHTFIgYXJ0aWZhY3RzLiIsIGFsaWduID0gImMiKQpgYGAKIyMgMTIuIE5vdGUgb24gdGVtcG9yYXJ5IGBCR0xSYCBhcnRpZmFjdHMKCkluIHRoZSBjdXJyZW50IDE4LW1vZGVsIHdvcmtmbG93LCBwZXJzaXN0ZW50IGBzYXZlQXRgIGFydGlmYWN0cyBhcmUgd3JpdHRlbiB1bmRlciBgb3V0cHV0L3Jlc3VsdHMvYmdscl9ydW5zL2AgZHVyaW5nIGVhY2ggZml0IGFuZCBwcmVzZXJ2ZWQgZm9yIGxhdGVyIGNvbnZlcmdlbmNlIGRpYWdub3N0aWNzLiBUaGUgc2NlbmFyaW8tc3BlY2lmaWMgcGF0aHMgbm93IHNlcGFyYXRlIGBDVjFgLCBgQ1YyYCwgYENWMGAsIGFuZCBgQ1YwMGAsIGFuZCB0aGUgcGVyc2lzdGVkIGZpbGVuYW1lcyB1c2UgdGhlIHNjZW5hcmlvIHByZWZpeCB0b2dldGhlciB3aXRoIGV4cGxpY2l0IGVmZmVjdCBsYWJlbHMgaW5zdGVhZCBvZiBnZW5lcmljIGBFVEFfMWAsIGBFVEFfMmAsIGFuZCBzaW1pbGFyIHBsYWNlaG9sZGVycy4gVGhlcmVmb3JlLCB0aGlzIGV4ZWN1dGlvbiBtb2R1bGUgaW52ZW50b3JpZXMgYm90aCB0aGUgZmluYWwgQ1NWIG91dHB1dHMgc3RvcmVkIHVuZGVyIGBvdXRwdXQvcmVzdWx0cy9gIGFuZCB0aGUgaGllcmFyY2hpY2FsIHBhdGhzIHRoYXQgbG9jYXRlIHRoZSBjb3JyZXNwb25kaW5nIEJHTFIgcnVuIGZvbGRlcnMuCgojIyAxMy4gRmluYWwgbm90ZQoKVGhpcyBmaWxlIGNsb3NlcyB0aGUgZXhlY3V0aW9uIGxheWVyIG9mIHRoZSBwcmVkaWN0aW9uIHN0YWdlIHdoaWxlIHByZXNlcnZpbmcgY29uc2lzdGVuY3kgd2l0aCB0aGUgcmVkdWNlZCAxOC1tb2RlbCBwcmVwcm9jZXNzaW5nIG1vZHVsZSBkb2N1bWVudGVkIGluIGBhbmFseXNpc19wcmVkaWN0aW9uLlJtZGAgYW5kIHdpdGggdGhlIGN1cnJlbnQgcmVwL2ZvbGQgb3V0cHV0IGNvbnRyYWN0IHVzZWQgYnkgdGhlIGRvd25zdHJlYW0gc3VtbWFyaWVzLgoKIyMgU3RhZ2UgbmF2aWdhdGlvbgoKKipQcmV2aW91cyBzdGFnZToqKiBbUHJlZGljdGlvbiDigJQgUGFydCBJXShhbmFseXNpc19wcmVkaWN0aW9uLmh0bWwpICAKKipOZXh0IHN0YWdlOioqIFtWaXN1YWxpemF0aW9uXSh2aXN1YWxpemF0aW9uLmh0bWwpCgo=