License: CC BY 4.0

📕 README

SCRIPT PART 2 of 3 This script “field-vs-lab-doses-r-2” is the second of three used for this project. The second script, “field-vs-lab-doses-r-2”, deals with the analysis. The first script, “field-vs-lab-doses-r-1”, tidies the input databases and combines them into a single data frame for analysis, the third deals with a specific sub-set Quality Assurance and Quality Control (QA/QC) analysis. If you download the data files for this project from the Open Science Framework (https://osf.io/h6cde/, DOI 10.17605/OSF.IO/H6CDE), you can run this script independently.

ARTICLE This R script is associated with Martin et al (2025) “Aligning Behavioural Ecotoxicology Tests with Real-World Water Concentrations: Current Minimum Tested Levels Far Exceed Environmental Reality” DOI: <to be added>

AUTHORS
Jake M. Martin 1,2,3*
Erin S. McCallum 1
Jack A. Band 2,4

AFFILIATIONS
(1) School of Life and Environmental Sciences, Deakin University, Geelong, Australia
(2) Department of Wildlife, Fish, and Environmental Studies, Swedish University of Agricultural Sciences, Umeå, Sweden
(3) School of Biological Sciences, Monash University, Melbourne, Australia
(4) Institute of Zoology, Zoological Society of London, London, United Kingdom
(*) Corresponding author

AIM
The aim of this study is two-fold: (1) identify whether tested concentrations in behavioural ecotoxicology studies reflect those that are detected in surface waters and/or waste water; (2) to compare the target pharmaceuticals tested in behavioural ecotoxicology to what have been detected in the environment, highlighting potential targets for future investigation.

METHODS
To achieve these aims, this study capitalises on four extensive open access databases, one of which focuses on peer-review research on the behavioural ecotoxicology of pharmaceuticals1, and the remaining focus on pharmaceutical environmental occurrence and concentrations2,3,4.

  1. The Evidence of the Impacts of Pharmaceuticals on Aquatic Animal Behaviour (EIPAAB) database (Martin et al. 2025)

📥 EIPAAB Database

  1. The Umwelt Bundesamt Pharmaceuticals in the environment (PHARMS-UBA) database

📥 PHARMS-UBA Database

  1. Pharmaceutical pollution of the world’s rivers (Wilkinson et al 2022), specifically “Dataset S4. Database of pharmaceutical concentrations at all the sampling locations monitored in this project”

📥 Wilkinson Database

  1. The NORMAN EMPODAT Database - Chemical Occurrence Data.

📥 NORMAN EMPODAT Database

SCRIPT
This is an R markdown script written in R studio (2023.09.0+463 “Desert Sunflower” Release). The ‘field-vs-lab-doses’ git repository hosts this script, and if downloaded (or pulled) it will reproduce all data tidy/filter, analysis, and visualisations used in Martin et al (2025). The GitHub repository includes all raw input data from the four open access databases, as well as all output data and figures. I have tried to structure this code with Open, Reliable, and Transparent (ORT) coding practices in mind. Feel free to reach out if anything is unclear.

The R script first cleans and filters each databases to make them comparable. In the gernalsit sensce In terms of filtering, we target pharmaceutical compounds, surface waters/waste water, and environmental/lab samples measured in units of mass per volume of water (e.g. ug/L).

We then summarise occurrence and concentrations between EIPAAB the three environmental databases (UBA, Wilkson and NORMAN) separately, to highlight the consistency of different environmental databases.

Lastly we combined all environmental datasets for an overall assesses of occurrence and concentrations between EIPAAB and environmental data.

GitHub

DISCLAIMER
I (Jake Martin) am dyslexic. I have made an effort to review the script for grammatical errors, but some will likely remain. I apologise. Please reach out via the contact details below if anything is unclear.

📧 Contact

🐟 Jake M. Martin

📧 Email: jake.martin@deakin.edu.au

📧 Alt Email: jake.martin.research@gmail.com

🌐 Web: jakemartin.org

🐙 GitHub: JakeMartinResearch

📑 Sharing/accessing and citing

  1. Licenses/restrictions placed on the data: CC-BY 4.0

  2. Link to the associated publication:
    🚧 To be added 🚧

  3. Recommended citation for this data:
    🚧 To be added 🚧

⚙️ Set-up and packages

Here we define our Knit settings, to make the output more user friendly, and to cache output for faster knitting.

#kniter setting
knitr::opts_chunk$set(
message = FALSE,
warning = FALSE, # no warnings
cache = TRUE,# Cacheing to save time when kniting
cache.lazy = FALSE, # I am running into issues with cache because the files are very large
tidy = TRUE
)

# ---- Install pacman if it's not already installed ----
if (!requireNamespace("pacman", quietly = TRUE)) install.packages("pacman")

# ---- List of required packages ----
pkgs <- c(
  # ----- Data Visualisation -----
  "ggthemes", "bayesplot", "gt", "gtsummary", "plotly", "qqplotr", "ggrepel",
  "colorspace", "flextable", "officer", "scales",
  
  # ----- Tidy Data and Wrangling -----
  "tidyverse", "janitor", "readxl", "broom.mixed", "data.table", "devtools", "scales", "glue",
  
  # ----- Modelling and Statistical Analysis -----
  "brms", "rstan", "cmdstanr", "marginaleffects", "performance", "emmeans",
  "tidybayes","future", "coda"
)

# ---- Install and load all packages using pacman ----
suppressPackageStartupMessages(
  pacman::p_load(char = pkgs, install = TRUE)
)

Here’s a list of the package names

pkgs
##  [1] "ggthemes"        "bayesplot"       "gt"              "gtsummary"      
##  [5] "plotly"          "qqplotr"         "ggrepel"         "colorspace"     
##  [9] "flextable"       "officer"         "scales"          "tidyverse"      
## [13] "janitor"         "readxl"          "broom.mixed"     "data.table"     
## [17] "devtools"        "scales"          "glue"            "brms"           
## [21] "rstan"           "cmdstanr"        "marginaleffects" "performance"    
## [25] "emmeans"         "tidybayes"       "future"          "coda"

🔧 Custom functions

Here are some custom function used within this script.

sentence_case() Changes a string into sentence case.

sentence_case <- function(text) {
    text <- tolower(text)  # Convert the entire text to lowercase
    text <- sub("^(\\s*\\w)", "\\U\\1", text, perl = TRUE)  # Capitalise the first letter
    return(text)
}

📂 Directories

Here we define the directories for the project. We also make the output directories if they don’t already exist.

Input

These are the input directors for the databases used in this project.

📥 data_wd: Directory for the input data.

wd <- getwd()  # getwd tells us what the current wd is, we are using this to drop it in a variable called wd
data_wd <- paste0(wd, "./data")  # creates a variable with the name of the wd we want to use

Output

fig_wd: Directory for the output figures.

fig_wd <- paste0(wd, "./fig")
if (!dir.exists("fig")) {
    dir.create("fig")
}

mods_wd: Directory for the output models.

mods_wd <- paste0(wd, "./mods")
if (!dir.exists("mods")) {
    dir.create("mods")
}

💿 Databases

This is the database made in part one of this script martin-field-vs-lab-all-databases.csv. It can be downloaded from the Open Science Frame Work (OSF) project page “Aligning Behavioural Ecotoxicology with Real-World Water Concentrations” by Jake M. Martin, Jack A Brand, and Erin McCallum [LINK https://osf.io/h6cde/]

You can cite the databases as

Martin JM, Brand JA, & McCallum E (2025). Aligning Behavioural Ecotoxicology with Real-World Water Concentrations [martin-field-vs-lab-all-databases.csv]. https://doi.org/10.17605/OSF.IO/H6CDE

all_databases <- fread(paste0(data_wd, "./martin-field-vs-lab-all-databases.csv"))

If you use the collated database, please also cite:

Wilkinson JL, Boxall ABA, Kolpin DW, Leung KMY, Lai RWS, Wong D, et al. Pharmaceutical pollution of the world’s rivers. Proceedings of the National Academy of Sciences. 2022;119:1–10. DOI:10.1073/pnas.2113947119.

Network N. NORMAN EMPODAT Database - Chemical Occurrence Data [Internet]. Available from: https://www.norman-network.com/nds/empodat

aus der Beek T, Weber FA, Bergmann A, Hickmann S, Ebert I, Hein A, et al. Pharmaceuticals in the environment-Global occurrences and perspectives. Environ Toxicol Chem. 2016;35:823–35.DOI:10.1002/etc.3339.

🧮 Analysis

Samples/data

Let’s see how much data is present in the filtered and combined databases.

total_compounds_eipaab <- all_databases %>%
    dplyr::filter(source == "eipaab") %>%
    nrow(.)

total_water_samples <- all_databases %>%
    dplyr::filter(source != "eipaab") %>%
    dplyr::reframe(n = sum(n_units_est, na.rm = TRUE)) %>%
    dplyr::pull(n)

glue::glue("There are {total_compounds_eipaab}, behavioural ecotoxicology exposures (compounds by species) and {total_water_samples}, water samples.")
## There are 767, behavioural ecotoxicology exposures (compounds by species) and 10009385, water samples.
all_databases %>%
    dplyr::filter(value < 0)
## Empty data.table (0 rows and 31 cols): compound_cas_corrected,compound_name_corrected,source,compound_cas,compound_name,concentration_type...

How many water samples are surface water vs waste water

all_databases %>%
    dplyr::filter(source != "eipaab") %>%
    dplyr::group_by(matrix_group) %>%
    dplyr::reframe(n = sum(n_units_est, na.rm = TRUE)) %>%
    flextable() %>%
    font(fontname = "Arial", part = "all")

matrix_group

n

effluent

281,752

surfacewater

9,727,633

Table 1

A breakdown for data contributed by each data source.

In the manuscript this will be called Table 1

Table 1: The number of pharmaceutical compounds within each of the four dataset (EIPAAB, NORMAN, PHARMS-UBA, and Wilksom) used for this investigation, and number of individual data rows within each of the database (i.e. testsa for EIPAAB, and water samplesb for the environmental occurrence databases).

soruce_data <- all_databases %>%
    dplyr::group_by(source) %>%
    dplyr::reframe(Data = sum(n_units_est, na.rm = TRUE), `Number of compounds` = length(unique(compound_name_corrected))) %>%
    dplyr::rename(Source = source) %>%
    dplyr::mutate(Source = case_when(Source == "eipaab" ~ "EIPAAB", Source == "norman" ~
        "NORMAN‡", Source == "uba" ~ "PHARMS-UBA", Source == "wilkson" ~ "Wilkson et al.",
        ))

total_data <- all_databases %>%
    dplyr::reframe(Data = sum(n_units_est, na.rm = TRUE), `Number of compounds` = length(unique(compound_name_corrected))) %>%
    dplyr::mutate(Source = "Total")

n_summary <- rbind(soruce_data, total_data)


table_1 <- n_summary %>%
    mutate(Data = round(Data, 0)) %>%
    flextable() %>%
    align(align = "center", part = "all") %>%
    autofit()


table_1

Source

Data

Number of compounds

EIPAAB

767

184

NORMAN‡

9,382,388

1,379

PHARMS-UBA

562,865

911

Wilkson et al.

64,132

61

Total

10,010,152

1,760

Note: (a) each row of the EIPAAB database represents a unique species by compound exposure; (b) each row within the NORMAN and PHARMS-UBA represents one of more samples, as a row can be a measure of central tendency (e.g. mean or median) or a single value, all rows in the Wilkson database represent a single sample; ‡ NORMAN database was filtered to remove data from the German Environment Agency (UBA), and restricted to 2014-2022, as the PHARMS-UBA also included data from NORMAN prior to 2014. Thus, this number is not a true total number of pharmaceutical samples present in the NORMAN database.

Save the table as a word document Table_1.docx

save_as_docx(table_1, path = paste0(fig_wd, "./table_1.docx"))

Number of aggregate values that had missing sample size.

all_data <- all_databases %>%
    dplyr::filter(source != "eipaab") %>%
    dplyr::reframe(total_data_n = length(compound_cas)) %>%
    dplyr::pull(total_data_n)

source_units_nas <- all_databases %>%
    dplyr::filter(concentration_type == "central") %>%
    dplyr::group_by(source) %>%
    dplyr::reframe(total_central = length(compound_cas), total_missing_units = sum(is.na(n_units)),
        all_data_n = all_data, percent_aggregate = round((total_central/all_data_n) *
            100, 2), percent_missing = round((total_missing_units/all_data_n) * 100,
            2)) %>%
    dplyr::rename(Source = source, `Total central data` = total_central, `Total missing units` = total_missing_units,
        `All data` = all_data_n, `Percent aggregate` = percent_aggregate, `Percent missing` = percent_missing) %>%
    dplyr::mutate(Source = case_when(Source == "eipaab" ~ "EIPAAB", Source == "norman" ~
        "NORMAN‡", Source == "uba" ~ "PHARMS-UBA", Source == "wilkson" ~ "Wilkson et al.",
        ))

source_units_nas %>%
    flextable() %>%
    autofit()

Source

Total central data

Total missing units

All data

Percent aggregate

Percent missing

PHARMS-UBA

31,725

4,023

9,569,449

0.33

0.04

Cross-over

Checking the cross-over between the compounds assessed in behavioural test against all environmental occurrence data.

eipaab_compounds_list <- all_databases %>%
    dplyr::filter(source == "eipaab") %>%
    dplyr::distinct(compound_name_corrected) %>%
    dplyr::pull(compound_name_corrected)

n_eipaab_compounds <- length(eipaab_compounds_list)

enviro__cross_over_compounds_list <- all_databases %>%
    dplyr::filter(source != "eipaab" & compound_name_corrected %in% eipaab_compounds_list) %>%
    dplyr::distinct(compound_name_corrected) %>%
    dplyr::pull(compound_name_corrected)

n_cross_over_compounds <- length(enviro__cross_over_compounds_list)

percent_cross_over = round(n_cross_over_compounds/n_eipaab_compounds * 100, 2)

glue::glue("{n_cross_over_compounds} of the {n_eipaab_compounds} pharmaceuticals tested in the EIPAAB database (with an environmental motivation) were present in the environmental databases (i.e. {percent_cross_over}%).")
## 167 of the 184 pharmaceuticals tested in the EIPAAB database (with an environmental motivation) were present in the environmental databases (i.e. 90.76%).

Concentrations

For questions around concentration, I will filtered for only cases where the levels are above zero or above the limit of detection (i.e. positive detections or above LOD). We will do this for two reasons, the first being that when researchers are trying to estimate the risk of environmental exposure, we assume that they are emulating potential exposure events at a contaminated site, not trying to replicate a theoretical global surface water average. The second is, by removing zero values and values below LODs, we are trying to be more conservative with the relative comparison between tested doses in behavioural ecotoxicology and observed environmental doses (as opposed to using some value substitute/estimated value for samples below detection limit). We are also using both single values and summary values of central tendency (means, median, ect). Fig

In this section we are calculating summary metrics for concentrations used in the behavioural test and detected in surface waters and wastewater in the enviromental data. We will do this separately for wastewater and surface waters samples. We are calculating, the mean, median, min, max, range, n (number of contributing data points), standard deviation (sd), standard error (se), and the upper and lower 95% credible intervals (ci_upper, ci_lower) using empirical quantiles.

conc_summary <- all_databases %>%
    dplyr::filter(value > 0) %>%
    dplyr::group_by(matrix_group, compound_name_corrected) %>%
    dplyr::reframe(mean = mean(value, na.rm = TRUE), median = median(value, na.rm = TRUE),
        min = min(value, na.rm = TRUE), max = max(value, na.rm = TRUE), range = max -
            min, n = length(unique_row_id), sd = sd(value, na.rm = TRUE), ci_lower = quantile(value,
            0.025, na.rm = TRUE), ci_upper = quantile(value, 0.975, na.rm = TRUE)) %>%
    dplyr::mutate(ci_lower = if_else(is.na(ci_lower), NA, ci_lower), ci_upper = if_else(is.na(ci_upper),
        NA, ci_upper)) %>%
    tidyr::complete(tidyr::expand(nesting(matrix_group, compound_name_corrected)))

Making a more use friendly table with the concentration of each compound that has a positive detection for wastewater and or surf waters.

cas_number <- all_databases %>%
    dplyr::filter(value > 0) %>%
    dplyr::group_by(compound_cas_corrected) %>%
    dplyr::slice(1) %>%
    dplyr::ungroup() %>%
    dplyr::select(compound_cas_corrected, compound_name_corrected)


conc_summary_df <- conc_summary %>%
    dplyr::left_join(., cas_number, by = "compound_name_corrected") %>%
    dplyr::select(compound_name_corrected, compound_cas_corrected, n, everything()) %>%
    dplyr::rename(compound_name = compound_name_corrected, compound_cas = compound_cas_corrected,
        matrix = matrix_group, cri_95_lower = ci_lower, cri_95_upper = ci_upper)

Saving as a date frame

write.csv(conc_summary_df, paste0(data_wd, "./martin-concentration-summary-table.csv"),
    row.names = FALSE)

For the Supplementary martial, we will make a smaller table.

eff_table <- conc_summary_df %>%
    dplyr::filter(matrix == "effluent") %>%
    dplyr::mutate(across(where(is.double), ~formatC(.x, format = "f", digits = 4))) %>%
    dplyr::mutate(Compound = paste0(compound_name, " (", compound_cas, ")"), `Wastewater 95% credible interval (µg/L)‡` = if_else(n ==
        1, NA_character_, paste0(cri_95_lower, "–", cri_95_upper)), `Wastewater range (µg/L)` = if_else(n ==
        1, NA_character_, paste0(min, "–", max)), `Wastewater n` = round(n, 0),
        `Wastewater median (µg/L)†` = median) %>%
    dplyr::select(Compound, "Wastewater n", "Wastewater median (µg/L)†", "Wastewater 95% credible interval (µg/L)‡",
        "Wastewater range (µg/L)")

surf_table <- conc_summary_df %>%
    dplyr::filter(matrix == "surfacewater") %>%
    dplyr::mutate(across(where(is.double), ~formatC(.x, format = "f", digits = 4))) %>%
    dplyr::mutate(Compound = paste0(compound_name, " (", compound_cas, ")"), `Surface water 95% credible interval (µg/L)‡` = if_else(n ==
        1, NA_character_, paste0(cri_95_lower, "–", cri_95_upper)), `Surface water range (µg/L)` = if_else(n ==
        1, NA_character_, paste0(min, "–", max)), `Surface water n` = round(n,
        0), `Surface water median (µg/L)†` = median) %>%
    dplyr::select(Compound, "Surface water n", "Surface water median (µg/L)†",
        "Surface water 95% credible interval (µg/L)‡", "Surface water range (µg/L)")

table_all <- full_join(surf_table, eff_table, by = "Compound") %>%
    gt() %>%
    cols_align(align = "center", columns = everything()) %>%
    tab_options(table.font.size = px(11)) %>%
    tab_header(title = md("**Summary of Measured Pharmaceutical Concentrations**"),
        subtitle = md("This summary table was compiled for *Martin et al. (2025)*. It is based on a filtered synthesis of three publicly available datasets: (1) the NORMAN EMPODAT database for chemical occurrence (accessed 18/03/2025), (2) the Umweltbundesamt Pharmaceuticals in the Environment database (PHARMS-UBA; accessed 19/12/2024), and (3) Wilkinson et al. (2022) Pharmaceutical Pollution of the World’s Rivers database. Data were restricted to entries reported in mass per volume of water (e.g., µg/L) and relevant to surface water and wastewater matrices (for details of the filtering process, please refer to Martin et al. (2025)). Users of this table should **cite the original sources of the data: NORMAN EMPODAT, PHARMS-UBA, and Wilkinson et al. (2022).**")) %>%
    tab_source_note(md("(†) Summary statistics reflect only positive detections. (‡) Credible intervals represent the lower and upper 95% empirical quantiles."))

Saving the table as a HTLM file

gtsave(table_all, filename = paste0(fig_wd, "./martin-et-al-supplemratry-file-1-concentration-summary-table.html"))

Here we will add the summary statistics from above to the full database, so we can see how many EIPAAB tested concentrations fall under the median surface water and wastewater concentrations for each compound.

rename_cols <- conc_summary %>%
    dplyr::select(-matrix_group, -compound_name_corrected) %>%
    colnames()

conc_summary_eff <- conc_summary %>%
    dplyr::filter(matrix_group == "effluent") %>%
    dplyr::rename_with(.cols = all_of(rename_cols), .fn = ~paste0("effluent_", .)) %>%
    dplyr::select(-matrix_group)


conc_summary_surf <- conc_summary %>%
    dplyr::filter(matrix_group == "surfacewater") %>%
    dplyr::rename_with(.cols = all_of(rename_cols), .fn = ~paste0("surfacewater_",
        .)) %>%
    dplyr::select(-matrix_group)


all_databases_with_sum <- all_databases %>%
    dplyr::filter(source == "eipaab") %>%
    dplyr::left_join(., conc_summary_eff, by = "compound_name_corrected") %>%
    dplyr::left_join(., conc_summary_surf, by = "compound_name_corrected") %>%
    dplyr::mutate(under_med_surf = if_else(value < surfacewater_median, 1, 0), under_hci_surf = if_else(value <
        surfacewater_ci_upper, 1, 0), under_med_effl = if_else(value < effluent_median,
        1, 0), under_hci_effl = if_else(value < effluent_ci_upper, 1, 0), diff_med_surf = value/surfacewater_median,
        diff_hic_surf = value/surfacewater_ci_upper, diff_med_effl = value/effluent_median,
        diff_hic_effl = value/effluent_ci_upper)

Making a database for surface waters and waste waters

surfacewater_conc <- all_databases_with_sum %>%
    dplyr::filter(!is.na(surfacewater_median), !is.na(value)) %>%
    dplyr::mutate(value_log = log(value), surfacewater_median_log = log(surfacewater_median),
        relative_year = year - min(year))

wastewater_conc <- all_databases_with_sum %>%
    dplyr::filter(!is.na(effluent_median), !is.na(value)) %>%
    dplyr::mutate(value_log = log(value), effluent_median_log = log(effluent_median),
        relative_year = year - min(year))

The relative_year is based on the earliest year, 1992.

all_databases_with_sum %>%
    dplyr::filter(!is.na(surfacewater_median), !is.na(value)) %>%
    dplyr::summarise(min_year = min(year, na.rm = TRUE)) %>%
    dplyr::pull(min_year)
## [1] 1992

Modeling

🌊 Surface water

Model structure

In these models, we examine the relationship between the log-transformed minimum tested concentration (value_log) and the log-transformed median surface water concentration (surfacewater_median_log).analysis

n = nrow(surfacewater_conc)
print(glue("We have a sample size of {n} for this analysis, as its on the level of exposure but only for compounds with correspond surfacewater detections"))
## We have a sample size of 706 for this analysis, as its on the level of exposure but only for compounds with correspond surfacewater detections

To account for temporal trends and study design differences, we include two additional covariates:

relative_year, defined as the number of years since the first publication in 1992, and doses, the number of concentration levels tested in each study.

We fit a full model that includes all three predictors, and a null model that excludes wastewater_median_log, allowing us to assess the added predictive value of surface water concentrations through model comparison.

concentration_str <- bf(value_log ~ surfacewater_median_log + relative_year + doses,
    family = gaussian())

concentration_str_null <- bf(value_log ~ relative_year + doses, family = gaussian())

These are the default priors.

suppressWarnings(get_prior(concentration_str, data = surfacewater_conc, family = gaussian())) %>%
    flextable() %>%
    autofit()

prior

class

coef

group

resp

dpar

nlpar

lb

ub

source

b

default

b

doses

default

b

relative_year

default

b

surfacewater_median_log

default

student_t(3, -0.2, 4.4)

Intercept

default

student_t(3, 0, 4.4)

sigma

0

default

Run model

Run model. This has been hashed out as I have saved the model, and will re-load it instead of re-running it.

options(brms.backend = "cmdstanr")
concentration_mod_log <- brm(
  concentration_str,
  data = surfacewater_conc,
  cores = 4,
  chains = 4,
  #prior = mod_priors, #use defaults
  warmup = 1000,
  seed = 20250418,
  thin = 2,
  iter = 8000,
  control = list(max_treedepth = 20, adapt_delta = 0.95),
  save_pars = save_pars(all = TRUE),
  sample_prior = TRUE,
  file = paste0(mods_wd, './concentration_mod_log')
)

concentration_mod_null_log <- brm(
  concentration_str_null,
  data = surfacewater_conc,
  cores = 4,
  chains = 4,
  #prior = mod_priors, #use defaults
  warmup = 1000,
  seed = 20250418,
  thin = 2,
  iter = 8000,
  control = list(max_treedepth = 20, adapt_delta = 0.95),
  save_pars = save_pars(all = TRUE),
  sample_prior = TRUE,
  file = paste0(mods_wd, './concentration_mod_null_log')
)
Re-load models

Reload the model if necessary.

concentration_mod_log <- readRDS(file = paste0(mods_wd, "./concentration_mod_log.rds"))
concentration_mod_null_log <- readRDS(file = paste0(mods_wd, "./concentration_mod_null_log.rds"))
Model diagnostics

There’s no clear evidence that the concentration model is better or worse than the null model for out-of-sample prediction. Based on the expected log predictive density (ELPD) between two models using approximate leave-one-out cross-validation (LOO-CV).

loo_null <- loo(concentration_mod_null_log, reloo = TRUE)
loo_mod <- loo(concentration_mod_log, reloo = TRUE)
loo_compare(loo_mod, loo_null)
##                            elpd_diff se_diff
## concentration_mod_null_log  0.0       0.0   
## concentration_mod_log      -0.7       1.1

A Bayes factor (BF) quantifies how much more likely the data are under one model compared to another. Where, BF > 1 support for the first model (concentration_mod_log), BF < 1 support for the second model (concentration_mod_null_log).

In this case: BF = 0.536 so the null model is about 1.87× more supported by the data (1/0.536 = 1.87)

bayes_factor(concentration_mod_log, concentration_mod_null_log)
## Iteration: 1
## Iteration: 2
## Iteration: 3
## Iteration: 4
## Iteration: 1
## Iteration: 2
## Iteration: 3
## Iteration: 4
## Estimated Bayes factor in favor of concentration_mod_log over concentration_mod_null_log: 0.53717

This is a very low R², suggesting:The predictor(s) in the model account for very little of the outcome variability.

Metric Value
Conditional R² 0.022
95% CI [0.004, 0.042]
performance::r2_bayes(concentration_mod_log, robust = FALSE, ci = 0.95)
## # Bayesian R2 with Compatibility Interval
## 
##   Conditional R2: 0.022 (95% CI [0.004, 0.042])

The model fits the marginal distribution of the response variable well.

color_scheme_set("red")
brms::pp_check(concentration_mod_log, ndraws = 25, type = "dens_overlay")

brms::pp_check(concentration_mod_log, ndraws = 25, type = "ecdf_overlay")

y_rep <- posterior_predict(concentration_mod_log, ndraws = 1000)
y_obs <- concentration_mod_log$data$value_log  # Replace with your actual response variable
ppc_stat_2d(y = y_obs, yrep = y_rep, stat = c("median", "mad"))

performance::check_model(concentration_mod_log)

Checking R-hat values to confirm model convergence and ESS fo estimates.

max_rhat <- max(rhat(concentration_mod_log), na.rm = TRUE)
print(glue::glue("All R-hat values ≤ {max_rhat} (good convergence)."))
## All R-hat values ≤ 1.00041606613586 (good convergence).
print(summary(concentration_mod_log, prob = 0.95))  #All Rhat = 1 (good).  
##  Family: gaussian 
##   Links: mu = identity; sigma = identity 
## Formula: value_log ~ surfacewater_median_log + relative_year + doses 
##    Data: surfacewater_conc (Number of observations: 706) 
##   Draws: 4 chains, each with iter = 8000; warmup = 1000; thin = 2;
##          total post-warmup draws = 14000
## 
## Regression Coefficients:
##                         Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## Intercept                   2.63      1.11     0.48     4.82 1.00    13376
## surfacewater_median_log     0.13      0.13    -0.13     0.39 1.00    13082
## relative_year              -0.11      0.03    -0.17    -0.04 1.00    13016
## doses                       0.07      0.08    -0.09     0.23 1.00    12523
##                         Tail_ESS
## Intercept                  11549
## surfacewater_median_log    11490
## relative_year              12209
## doses                      12514
## 
## Further Distributional Parameters:
##       Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## sigma     4.38      0.12     4.16     4.62 1.00    13039    12408
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

Diagnostic plots look fine. A smaller shape value indicates greater overdispersion (variance is much larger than the mean).

color_scheme_set("red")
plot(concentration_mod_log)

Model estimates

Pulling the marginal effect estimates from the model for our plots.

estimate_data_1 <- marginal_effects(concentration_mod_log, effects = "surfacewater_median_log")[[1]]
estimate_data_year_1 <- marginal_effects(concentration_mod_log, effects = "relative_year")[[1]]

These estimated coefficients represent the average effects of each predictor on the log-transformed tested concentration, controlling for (not averaging over) the other variables in the model. Because this is a log–log model, the coefficient for log(surfacewater_median) can be interpreted as an elasticity—that is, the percent change in the response associated with a percent change in the predictor.

The estimated effect of log(surfacewater_median) was β = 0.128 (95% CrI: –0.133 to 0.389), suggesting that a 1% increase in surface water concentration is associated with an average 0.128% increase in tested concentration. However, the 95% credible interval includes zero, indicating substantial uncertainty and that the effect is not well-supported by the data.

The estimated effect of relative_year was β = –0.106 (95% CrI: –0.170 to –0.042), providing strong evidence of a temporal decline in tested concentrations. Since the predictor is linear and the outcome is log-transformed, this corresponds to an average 10% decrease in tested concentration per year (where the exp(–0.106) ≈ 0.899; and (0.899−1) × 100 = −10.1%), holding all else constant.

The estimated coefficient for doses was β = 0.068 (95% CrI: –0.094 to 0.234), suggesting a weak positive association, but again with considerable uncertainty—the effect is not credibly different from zero.

model_summary_1 <- as.data.frame(fixef(concentration_mod_log, summary = TRUE)) %>%
    rownames_to_column() %>%
    clean_names()

estimate_1 <- model_summary_1$estimate[2]
L95CI_1 <- model_summary_1$q2_5[2]
H95CI_1 <- model_summary_1$q97_5[2]

estimate_year_1 <- model_summary_1$estimate[3]
L95CI_year_1 <- model_summary_1$q2_5[3]
H95CI_year_1 <- model_summary_1$q97_5[3]

model_summary_1_ft <- model_summary_1 %>%
    dplyr::mutate(across(where(is.double), ~round(.x, 3))) %>%
    dplyr::rename(predictor = rowname) %>%
    dplyr::mutate(CrI = paste0(q2_5, " to ", q97_5)) %>%
    dplyr::select(predictor, estimate, CrI) %>%
    flextable() %>%
    set_header_labels(predictor = "Predictor", estimate = "Estimate", CrI = "95% CrI") %>%
    autofit()

model_summary_1_ft

Predictor

Estimate

95% CrI

Intercept

2.631

0.481 to 4.817

surfacewater_median_log

0.128

-0.133 to 0.389

relative_year

-0.106

-0.17 to -0.042

doses

0.068

-0.094 to 0.234

Saving the table

save_as_docx(model_summary_1_ft, path = paste0(fig_wd, "./table-s2.docx"))

This gives you the model-derived expected number of years (from the 1992 baseline) for convergence of tested and detected concentrations, for an average compound with average dose and average water concentration.

b0 <- model_summary_1$estimate[1]  # Intercept
b1 <- model_summary_1$estimate[2]  # surfacewater_median_log
b2 <- model_summary_1$estimate[3]  # relative_year
b3 <- model_summary_1$estimate[4]  # doses

x_bar <- mean(surfacewater_conc$surfacewater_median_log, na.rm = TRUE)
d_bar <- mean(surfacewater_conc$doses, na.rm = TRUE)

average_equal_years <- round(((-b0 + (1 - b1) * x_bar - b3 * d_bar)/b2), 0)
years_to_equal <- (1992 + average_equal_years)

glue::glue("Based on model predictions for average compound, with the average number of doses, and average water concentration, it would take {average_equal_years} years from our baseline (i.e. 1992), for the tested dose to be equal with the meassured dose. In other words, based on current trends, tested doses wont be equal to field levels until {years_to_equal}")
## Based on model predictions for average compound, with the average number of doses, and average water concentration, it would take 63 years from our baseline (i.e. 1992), for the tested dose to be equal with the meassured dose. In other words, based on current trends, tested doses wont be equal to field levels until 2055
Fig 1a
text_coord <- surfacewater_conc %>%
    summarise(y_max = max(value_log, na.rm = TRUE), x_max = max(surfacewater_median_log,
        na.rm = TRUE), x_min = min(surfacewater_median_log, na.rm = TRUE), x = mean(c(x_max,
        x_min)))

y_coord <- text_coord %>%
    pull(y_max)

x_coord <- text_coord %>%
    pull(x_min)


fig_1a <- ggplot() + geom_line(data = estimate_data_1, aes(x = surfacewater_median_log,
    y = estimate__), color = "#2D5F34", linewidth = 1) + geom_ribbon(data = estimate_data_1,
    aes(x = surfacewater_median_log, ymin = lower__, ymax = upper__), fill = "#2D5F34",
    alpha = 0.1) + geom_point(data = surfacewater_conc, aes(x = surfacewater_median_log,
    y = value_log), alpha = 0.4, shape = 21, color = "#2D5F34", fill = "#2D5F34") +
    annotate("text", x = x_coord, y = y_coord, label = paste0("Bayesian log-log Gaussian regression",
        "\n", "Effect estimate: ", round(estimate_1, 3), "\n", "95%CrI: ", round(L95CI_1,
            3), " to ", round(H95CI_1, 3)), hjust = 0, vjust = 1, size = 4) + labs(title = "(A)  Surface water vs exposure concentration",
    x = "Exposure concentration (μg/L)*", y = "Environmental concentration (μg/L)*",
    caption = "*Values in μg/L space on a log scale") + scale_y_continuous(labels = function(x) (scales::label_scientific())(exp(x))) +
    scale_x_continuous(labels = function(x) (scales::label_scientific())(exp(x))) +
    theme_few() + theme(legend.position = "bottom", legend.justification = "center",
    plot.caption = element_text(size = 8, hjust = 0.5, margin = margin(t = 6)))
fig_1a

Save this figure for the MS

ggsave(filename = paste0(fig_wd, "./figure-1a.pdf"), plot = fig_1a, width = 210,
    height = 297/1.5, units = "mm")
Fig S1a

Plot surface water concentrations by year

text_coord <- wastewater_conc %>%
    summarise(y_max = max(value_log, na.rm = TRUE), x_max = max(relative_year, na.rm = TRUE),
        x_min = min(relative_year, na.rm = TRUE), x_mean = mean(c(x_max, x_min)))

y_coord <- text_coord %>%
    pull(y_max)

x_coord <- text_coord %>%
    pull(x_min)


fig_s1a <- ggplot() + geom_line(data = estimate_data_year_1, aes(x = relative_year,
    y = estimate__), color = "#2D5F34", linewidth = 1) + geom_ribbon(data = estimate_data_year_1,
    aes(x = relative_year, ymin = lower__, ymax = upper__), fill = "#2D5F34", alpha = 0.1) +
    geom_point(data = surfacewater_conc, aes(x = relative_year, y = value_log), alpha = 0.4,
        shape = 21, color = "#2D5F34", fill = "#2D5F34") + annotate("text", x = x_coord,
    y = y_coord, label = paste0("Bayesian log-log Gaussian regression", "\n", "Effect estimate: ",
        round(estimate_year_1, 3), "\n", "95%CrI: ", round(L95CI_year_1, 3), " to ",
        round(H95CI_year_1, 3)), hjust = 0, vjust = 1, size = 4) + labs(title = "(A)  Exposure concentration vs year (surface water compounds)",
    x = "Year", y = "Exposure concentration (μg/L)*", caption = "*Concentration values in μg/L space on a log scale") +
    scale_y_continuous(labels = function(x) (scales::label_scientific())(exp(x))) +
    scale_x_continuous(labels = function(x) x + 1992) + theme_few() + theme(legend.position = "bottom",
    legend.justification = "center", plot.caption = element_text(size = 8, hjust = 0.5,
        margin = margin(t = 6)))
fig_s1a

Save this figure for the MS

ggsave(filename = paste0(fig_wd, "./figure-s1a.pdf"), plot = fig_s1a, width = 210,
    height = 297/1.5, units = "mm")

🚽 Waste water

Model structure

In these models, we examine the relationship between the log-transformed minimum tested concentration (value_log) and the log-transformed median wastewater concentration (effluent_median_log).

but only for compounds with correspond wastewater detections

n = nrow(wastewater_conc)
print(glue("We have a sample size of {n} for this analysis, as its on the level of exposure, but only for compounds with correspond wastewater detections"))
## We have a sample size of 714 for this analysis, as its on the level of exposure, but only for compounds with correspond wastewater detections

To account for temporal trends and study design differences, we include two additional covariates:

relative_year, defined as the number of years since the first publication in 1992, and doses, the number of concentration levels tested in each study.

We fit a full model that includes all three predictors, and a null model that excludes effluent_median_log, allowing us to assess the added predictive value of wastewater concentrations through model comparison.

eff_concentration_str <- bf(value_log ~ effluent_median_log + relative_year + doses,
    family = gaussian())

eff_concentration_str_null <- bf(value_log ~ relative_year + doses, family = gaussian())

These are the default priors.

suppressWarnings(get_prior(eff_concentration_str, data = wastewater_conc, family = gaussian())) %>%
    flextable()

prior

class

coef

group

resp

dpar

nlpar

lb

ub

source

b

default

b

doses

default

b

effluent_median_log

default

b

relative_year

default

student_t(3, -0.2, 4.5)

Intercept

default

student_t(3, 0, 4.5)

sigma

0

default

Run model

Run model. This has been hashed out as I have saved the model, and will re-load it instead of re-running it.

options(brms.backend = "cmdstanr")
eff_concentration_mod_log <- brm(
  eff_concentration_str,
  data = wastewater_conc,
  cores = 4,
  chains = 4,
  #prior = mod_priors, #use defaults
  warmup = 1000,
  seed = 20250418,
  thin = 2,
  iter = 8000,
  control = list(max_treedepth = 20, adapt_delta = 0.95),
  save_pars = save_pars(all = TRUE),
  sample_prior = TRUE,
  file = paste0(mods_wd, './eff_concentration_mod_log')
)

eff_concentration_mod_null_log <- brm(
  eff_concentration_str_null,
  data = wastewater_conc,
  cores = 4,
  chains = 4,
  #prior = mod_priors, #use defaults
  warmup = 1000,
  seed = 20250418,
  thin = 2,
  iter = 8000,
  control = list(max_treedepth = 20, adapt_delta = 0.95),
  save_pars = save_pars(all = TRUE),
  sample_prior = TRUE,
  file = paste0(mods_wd, './eff_concentration_mod_null_log')
)
Re-load models

Reload the model if necessary.

eff_concentration_mod_log <- readRDS(file = paste0(mods_wd, "./eff_concentration_mod_log.rds"))
eff_concentration_mod_null_log <- readRDS(file = paste0(mods_wd, "./eff_concentration_mod_null_log.rds"))
Model diagnostics

We compared the full model (including effluent_median_log) with the null model (excluding it) using approximate leave-one-out cross-validation (LOO-CV).

The expected log predictive density (ELPD) difference was:

ΔELPD = 31.7 in favour of the full model, with a standard error (SE) of 8.1.

Since the ELPD difference is approximately four times larger than its standard error (Δ/SE ≈ 3.9), this indicates strong evidence that the full model provides substantially better out-of-sample predictive performance than the null model.

In other words, including effluent_median_log as a predictor significantly improves the model’s ability to predict tested concentrations across studies.

loo_compare(loo(eff_concentration_mod_log, moment_match = TRUE), loo(eff_concentration_mod_null_log,
    moment_match = TRUE))
##                                elpd_diff se_diff
## eff_concentration_mod_log        0.0       0.0  
## eff_concentration_mod_null_log -31.7       8.1

The Bayes factor comparing the full model (including effluent_median_log) to the null model (excluding it) was approximately 2.25 × 10¹³, overwhelmingly favouring the full model.

A Bayes factor (BF) this large indicates decisive evidence that the full model provides a better explanation of the data than the null model. In other words, the observed data are roughly 22 trillion times more likely under the full model than under the null.

This provides exceptionally strong support for including effluent_median_log as a predictor of tested concentrations, reinforcing the conclusion from the LOO-CV comparison.

bayes_factor(eff_concentration_mod_log, eff_concentration_mod_null_log)
## Iteration: 1
## Iteration: 2
## Iteration: 3
## Iteration: 4
## Iteration: 1
## Iteration: 2
## Iteration: 3
## Iteration: 4
## Estimated Bayes factor in favor of eff_concentration_mod_log over eff_concentration_mod_null_log: 30862546421405.30469

The Bayesian R² for the full model (including effluent_median_log) was 0.108, with a 95% compatibility interval ranging from 0.070 to 0.150.

This indicates that the model explains approximately 10.8% of the variance in tested concentrations across studies, with credible support for this value lying between 7.0% and 15.0%.

While this represents a modest proportion of explained variance, it nonetheless reflects a meaningful improvement over the null model — especially when considered alongside the strong support from both the LOO-CV comparison and the Bayes factor. This suggests that effluent_median_log is an informative predictor, even if the overall model explains a limited portion of the variability, which may reflect substantial heterogeneity across studies.

performance::r2_bayes(eff_concentration_mod_log, robust = FALSE, ci = 0.95)
## # Bayesian R2 with Compatibility Interval
## 
##   Conditional R2: 0.108 (95% CI [0.070, 0.150])

The model fits the marginal distribution of the response variable well.

color_scheme_set("red")
brms::pp_check(eff_concentration_mod_log, ndraws = 25, type = "dens_overlay")

brms::pp_check(eff_concentration_mod_log, ndraws = 25, type = "ecdf_overlay")

y_rep <- posterior_predict(eff_concentration_mod_log, ndraws = 1000)
y_obs <- eff_concentration_mod_log$data$value_log  # Replace with your actual response variable
ppc_stat_2d(y = y_obs, yrep = y_rep, stat = c("median", "mad"))

Based on the diagnostic plots:(1) The residuals exhibit no major departures from randomness; (2) The Q-Q and density plots support the normality assumption of the residuals; (3) The absence of strong patterns in the autocorrelation and scale-location plots further suggests that the model is well-specified with homoscedastic and independent errors.

performance::check_model(eff_concentration_mod_log)

Checking R-hat values to confirm model convergence and ESS fo estimates.

max_rhat <- max(rhat(eff_concentration_mod_log), na.rm = TRUE)
print(glue::glue("All R-hat values ≤ {max_rhat}"))
## All R-hat values ≤ 1.00029834752456

General model output

print(summary(eff_concentration_mod_log, prob = 0.95))
##  Family: gaussian 
##   Links: mu = identity; sigma = identity 
## Formula: value_log ~ effluent_median_log + relative_year + doses 
##    Data: wastewater_conc (Number of observations: 714) 
##   Draws: 4 chains, each with iter = 8000; warmup = 1000; thin = 2;
##          total post-warmup draws = 14000
## 
## Regression Coefficients:
##                     Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## Intercept               5.58      0.95     3.71     7.45 1.00    13485    12258
## effluent_median_log     0.68      0.08     0.52     0.84 1.00    12501    12460
## relative_year          -0.13      0.03    -0.19    -0.07 1.00    12979    12064
## doses                  -0.08      0.08    -0.24     0.08 1.00    13233    12270
## 
## Further Distributional Parameters:
##       Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## sigma     4.19      0.11     3.98     4.42 1.00    12826    11539
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

Diagnostic plots look fine. A smaller shape value indicates greater overdispersion (variance is much larger than the mean).

color_scheme_set("red")
plot(eff_concentration_mod_log)

Model estimates

Pulling the marginal effect estimates from the model for our plots.

# Extract marginal effects for surface water and year
estimate_data_2 <- marginal_effects(eff_concentration_mod_log, effects = "effluent_median_log")[[1]]
estimate_data_year_2 <- marginal_effects(eff_concentration_mod_log, effects = "relative_year")[[1]]

These estimated coefficients represent the average effects across the dataset, controlling for (rather than averaging over) the other predictors in the model. Because the model is log–log for value_log and effluent_median_log, the coefficient for log(effluent_median) reflects an elasticity—that is, the percent change in the response for a percent change in the predictor.

The estimated effect of log(effluent_median) was β = 0.679 (95% CrI: 0.516 to 0.841), indicating that a 1% increase in effluent concentration is associated with an average 0.679% increase in tested concentration, all else being equal. Since the 95% credible interval does not include zero, this effect is statistically well-supported.

The coefficient for relative_year was β = –0.134 (95% CrI: –0.195 to –0.073), suggesting a consistent temporal decline in tested concentrations over time. As this predictor is linear while the response is log-transformed, this corresponds to an average 12.6% decrease in tested concentration per year (exp(–0.134) ≈ 0.874), holding other variables constant.

The estimated effect of doses was β = –0.080 (95% CrI: –0.242 to 0.084), suggesting a weak and uncertain negative association between the number of doses tested and the minimum reported concentration. However, the credible interval includes zero, indicating no strong evidence for an effect of doses in this model.

model_summary_2 <- as.data.frame(fixef(eff_concentration_mod_log, summary = TRUE)) %>%
    rownames_to_column() %>%
    clean_names()

estimate_2 <- model_summary_2$estimate[2]
L95CI_2 <- model_summary_2$q2_5[2]
H95CI_2 <- model_summary_2$q97_5[2]

estimate_year_2 <- model_summary_2$estimate[3]
L95CI_year_2 <- model_summary_2$q2_5[3]
H95CI_year_2 <- model_summary_2$q97_5[3]


model_summary_2_ft <- model_summary_2 %>%
    dplyr::mutate(across(where(is.double), ~round(.x, 3))) %>%
    dplyr::rename(predictor = rowname) %>%
    dplyr::mutate(CrI = paste0(q2_5, " to ", q97_5)) %>%
    dplyr::select(predictor, estimate, CrI) %>%
    flextable() %>%
    set_header_labels(predictor = "Predictor", estimate = "Estimate", CrI = "95% CrI") %>%
    autofit()

model_summary_2_ft

Predictor

Estimate

95% CrI

Intercept

5.576

3.709 to 7.45

effluent_median_log

0.679

0.516 to 0.841

relative_year

-0.134

-0.195 to -0.073

doses

-0.080

-0.242 to 0.084

Saving the table

save_as_docx(model_summary_2_ft, path = paste0(fig_wd, "./table-s3.docx"))
Fig 1b
text_coord <- wastewater_conc %>%
    summarise(y = max(value_log, na.rm = TRUE), x_max = max(effluent_median_log,
        na.rm = TRUE), x_min = min(effluent_median_log, na.rm = TRUE), x = mean(c(x_max,
        x_min)))

y_coord <- text_coord %>%
    pull(y)

x_coord <- text_coord %>%
    pull(x)


fig_1b <- ggplot() + geom_line(data = estimate_data_2, aes(x = effluent_median_log,
    y = estimate__), color = "#5B489D", linewidth = 1) + geom_ribbon(data = estimate_data_2,
    aes(x = effluent_median_log, ymin = lower__, ymax = upper__), fill = "#5B489D",
    alpha = 0.1) + geom_point(data = wastewater_conc, aes(x = effluent_median_log,
    y = value_log), alpha = 0.4, shape = 21, color = "#5B489D", fill = "#5B489D") +
    annotate("text", x = x_coord, y = y_coord, label = paste0("Bayesian log-log Gaussian regression",
        "\n", "Effect estimate: ", round(estimate_2, 3), "\n", "95%CrI: ", round(L95CI_2,
            3), " to ", round(H95CI_2, 3)), hjust = 0, vjust = 1, size = 4) + labs(title = "(B)  Wastewater vs exposure concentration",
    x = "Exposure concentration (μg/L)*", y = "Environmental concentration (μg/L)*",
    caption = "*Values in μg/L space on a log scale") + scale_y_continuous(labels = function(x) (scales::label_scientific())(exp(x))) +
    scale_x_continuous(labels = function(x) (scales::label_scientific())(exp(x))) +
    theme_few() + theme(legend.position = "bottom", legend.justification = "center",
    plot.caption = element_text(size = 8, hjust = 0.5, margin = margin(t = 6)))
fig_1b

Saving figure

ggsave(filename = paste0(fig_wd, "./figure-1b.pdf"),
       plot = fig_1b,
       width = 210,
       height = 297/1.5,  # Specify the height of the plot
       units = "mm")
Fig S1b

Plot surface water concentrations by year

text_coord <- wastewater_conc %>%
    summarise(y_max = max(value_log, na.rm = TRUE), x_max = max(relative_year, na.rm = TRUE),
        x_min = min(relative_year, na.rm = TRUE), x_mean = mean(c(x_max, x_min)))

y_coord <- text_coord %>%
    pull(y_max)

x_coord <- text_coord %>%
    pull(x_min)


fig_s1b <- ggplot() + geom_line(data = estimate_data_year_2, aes(x = relative_year,
    y = estimate__), color = "#5B489D", linewidth = 1) + geom_ribbon(data = estimate_data_year_2,
    aes(x = relative_year, ymin = lower__, ymax = upper__), fill = "#5B489D", alpha = 0.1) +
    geom_point(data = wastewater_conc, aes(x = relative_year, y = value_log), alpha = 0.4,
        shape = 21, color = "#5B489D", fill = "#5B489D") + annotate("text", x = x_coord,
    y = y_coord, label = paste0("Bayesian log-log Gaussian regression", "\n", "Effect estimate: ",
        round(estimate_year_2, 3), "\n", "95%CrI: ", round(L95CI_year_2, 3), " to ",
        round(H95CI_year_2, 3)), hjust = 0, vjust = 1, size = 4) + labs(title = "(B)  Exposure concentration vs year (wastewater compounds)",
    x = "Year", y = "Exposure concentration (μg/L)*", caption = "*Concentration values in μg/L space on a log scale") +
    scale_y_continuous(labels = function(x) (scales::label_scientific())(exp(x))) +
    scale_x_continuous(labels = function(x) x + 1992) + theme_few() + theme(legend.position = "bottom",
    legend.justification = "center", plot.caption = element_text(size = 8, hjust = 0.5,
        margin = margin(t = 6)))
fig_s1b

Save this figure for the MS

ggsave(filename = paste0(fig_wd, "./figure-s1b.pdf"), plot = fig_s1b, width = 210,
    height = 297/1.5, units = "mm")
Outlier assemsent

Is this trend still present if the extreme concentration is removed. The compound is streptomycin (CAS: 57-92-1)

wastewater_conc %>%
    dplyr::arrange(desc(effluent_median_log)) %>%
    dplyr::slice(1) %>%
    dplyr::select(compound_cas_corrected, compound_name_corrected, value) %>%
    flextable()

compound_cas_corrected

compound_name_corrected

value

57-92-1

streptomycin

20,400

Run model

Building an identical model without streptomycin (outlier removed; or)

options(brms.backend = "cmdstanr")
eff_concentration_mod_log_or <- brm(eff_concentration_str,
               data = wastewater_conc %>%
                 dplyr::filter(compound_cas_corrected != "57-92-1"),
               cores = 4,
               chains = 4,
               #prior = mod_priors, #use defaults
               warmup = 1000,
               seed = 20250418,
               thin = 2,
               iter = 8000,
               control = list(max_treedepth = 20, adapt_delta = 0.95),
               save_pars = save_pars(all=TRUE),
               sample_prior = TRUE,
               file = paste0(mods_wd, './eff_concentration_mod_log_or'))

Reload the model if necessary.

eff_concentration_mod_log_or <- readRDS(file = paste0(mods_wd, "./eff_concentration_mod_log_or.rds"))

The effect of year seems robust to the outlier.

model_summary_2_1 <- as.data.frame(fixef(eff_concentration_mod_log_or, summary = TRUE)) %>%
    rownames_to_column() %>%
    clean_names()

table_i <- model_summary_2_1 %>%
    dplyr::mutate(across(where(is.double), ~round(.x, 3))) %>%
    dplyr::rename(predictor = rowname) %>%
    dplyr::mutate(CrI = paste0(q2_5, " to ", q97_5)) %>%
    dplyr::select(predictor, estimate, CrI) %>%
    flextable() %>%
    set_header_labels(predictor = "Predictor", estimate = "Estimate (outlier removed)",
        CrI = "95% CrI (outlier removed)") %>%
    autofit()

table_i

Predictor

Estimate (outlier removed)

95% CrI (outlier removed)

Intercept

5.635

3.726 to 7.548

effluent_median_log

0.685

0.521 to 0.851

relative_year

-0.135

-0.195 to -0.073

doses

-0.083

-0.245 to 0.078

📈 Concentration fold differences

Here we are summarising how many test were below the median surface water and effluent concentrations as well as the upper 95%CI. We are also calculating the mean-fold difference in concentrations.

conc_summary_overall <- all_databases_with_sum %>% 
  dplyr::reframe(
    eipaab_n = length(unique_row_id),
    n_compounds_sw = length(unique(compound_name_corrected[!is.na(surfacewater_median)])),
    n_compounds_ef = length(unique(compound_name_corrected[!is.na(effluent_median)])),
    n_sw = sum(!is.na(under_med_surf)),
    less_med_sw = sum(under_med_surf, na.rm = TRUE),
    less_hci_sw = sum(under_hci_surf, na.rm = TRUE),
    percent_less_med_sw = (less_med_sw / n_sw) * 100,
    percent_less_hic_sw = (less_hci_sw / n_sw) * 100,
    n_ef = sum(!is.na(under_med_effl)),
    less_med_ef = sum(under_med_effl, na.rm = TRUE),
    less_hci_ef = sum(under_hci_effl, na.rm = TRUE),
    percent_less_med_ef = (less_med_ef / n_ef) * 100,
    percent_less_hic_ef = (less_hci_ef / n_ef) * 100,
    
    # Median values
    fdiff_med_sw = median(diff_med_surf, na.rm = TRUE),
    fdiff_hci_sw = median(diff_hic_surf, na.rm = TRUE),
    fdiff_med_ef = median(diff_med_effl, na.rm = TRUE),
    fdiff_hci_ef = median(diff_hic_effl, na.rm = TRUE),
    
    # Standard Deviation
    sd_fdiff_med_sw = sd(diff_med_surf, na.rm = TRUE),
    sd_fdiff_hci_sw = sd(diff_hic_surf, na.rm = TRUE),
    sd_fdiff_med_ef = sd(diff_med_effl, na.rm = TRUE),
    sd_fdiff_hci_ef = sd(diff_hic_effl, na.rm = TRUE)
  ) %>% 
  dplyr::mutate(across(everything(), ~ ifelse(is.nan(.), NA, .)))  # Convert NaNs to NAs

Results

For the 706 exposures to compounds with surface water detections (in UBA, NORMAN, or Wilkson), only 18.70% and 37.96% employed test concentrations lower then median and upper 95% credible interval of surface water concentrations, respectively. Further, for the 714 exposures to compounds with wastewater detections, only 23.39% and 53.08% employed test concentrations lower then median and upper 95% credible interval of wastewater. On average, concentrations used in behavioural ecotoxicology were 43 times higher then median surf water concentrations, and 10 times higher then those in effluent.

surf <- conc_summary_overall %>% 
  dplyr::mutate(fdiff_med_sw = round(fdiff_med_sw,2),
                fdiff_hci_sw = round(fdiff_hci_sw)) %>% 
  dplyr::select(n_sw, less_med_sw, less_hci_sw, fdiff_med_sw, fdiff_hci_sw, percent_less_med_sw, percent_less_hic_sw, n_compounds_sw) %>% 
  dplyr::rename(total_n = n_sw, less_med = less_med_sw, less_hci = less_hci_sw, fdiff_med = fdiff_med_sw, fdiff_hci = fdiff_hci_sw, percent_less_med = percent_less_med_sw, percent_less_hic = percent_less_hic_sw, compounds = n_compounds_sw) %>% 
  dplyr::mutate(matrix = "Surface water")

effluent <- conc_summary_overall %>% 
    dplyr::mutate(fdiff_med_ef = round(fdiff_med_ef, 2),
                fdiff_hci_ef = round(fdiff_hci_ef, 2)) %>% 
  dplyr::select(n_ef, less_med_ef, less_hci_ef, fdiff_med_ef, fdiff_hci_ef, percent_less_med_ef, percent_less_hic_ef, n_compounds_ef) %>% 
  dplyr::rename(total_n = n_ef, less_med = less_med_ef, less_hci = less_hci_ef, fdiff_med = fdiff_med_ef, fdiff_hci = fdiff_hci_ef, percent_less_med = percent_less_med_ef, percent_less_hic = percent_less_hic_ef, compounds = n_compounds_ef) %>% 
  dplyr::mutate(matrix = "Effluent")

overall_summary_table <- rbind(surf, effluent) %>% 
  dplyr::select(matrix, everything()) %>% 
  dplyr::mutate(
    percent_less_med = round(percent_less_med, 2),
    percent_less_hic = round(percent_less_hic, 2)
  ) %>% 
  dplyr::rename(
    Matrix = matrix,
    `Total tests` = total_n,
    `Total compounds` = compounds,
    `Dose less than median` = less_med,
    `Dose less than upper 95%CrI` = less_hci,
    `Mean-fold difference from median` = fdiff_med,
    `Mean-fold difference from upper 95%CrI` = fdiff_hci,
    `Percent less than median (%)` = percent_less_med,
    `Percent less than upper 95%CrI (%)` = percent_less_hic
  ) %>%
  flextable() %>%
  colformat_num(j = "Total tests", digits = 0) %>%  # Format total tests as whole numbers
  align(align = "center", part = "all") %>%
  autofit()

overall_summary_table

Matrix

Total tests

Dose less than median

Dose less than upper 95%CrI

Mean-fold difference from median

Mean-fold difference from upper 95%CrI

Percent less than median (%)

Percent less than upper 95%CrI (%)

Total compounds

Surface water

706

133

269

42.70

2.00

18.84

38.10

139

Effluent

714

167

379

10.11

0.78

23.39

53.08

148

save_as_docx(overall_summary_table, path = paste0(fig_wd, "./table-s6-1.docx"))

Now we will make a summary for each compound

sample_counts <- all_databases_with_sum %>%
    dplyr::select(compound_name_corrected, surfacewater_n, effluent_n) %>%
    group_by(compound_name_corrected) %>%
    dplyr::slice(1) %>%
    ungroup()

under_levels <- all_databases_with_sum %>%
    dplyr::group_by(compound_name_corrected) %>%
    dplyr::reframe(eipaab_n = length(unique_row_id), n_sw = sum(!is.na(under_med_surf)),
        less_med_sw = sum(under_med_surf, na.rm = TRUE), less_hci_sw = sum(under_hci_surf,
            na.rm = TRUE), prop_less_med_sw = less_med_sw/n_sw, prop_less_hic_sw = less_hci_sw/n_sw,
        n_ef = sum(!is.na(under_med_effl)), less_med_ef = sum(under_med_effl, na.rm = TRUE),
        less_hci_ef = sum(under_hci_effl, na.rm = TRUE), prop_less_med_ef = less_med_ef/n_ef,
        prop_less_hic_ef = less_hci_ef/n_ef, fdiff_med_sw = median(diff_med_surf,
            na.rm = TRUE), fdiff_hci_sw = median(diff_hic_surf, na.rm = TRUE), fdiff_med_ef = median(diff_med_effl,
            na.rm = TRUE), fdiff_hci_ef = median(diff_hic_effl, na.rm = TRUE), ) %>%
    dplyr::left_join(., sample_counts, by = "compound_name_corrected") %>%
    dplyr::mutate(across(everything(), ~ifelse(is.nan(.), NA, .)))
under_levels
## # A tibble: 184 × 18
##    compound_name_corrected            eipaab_n  n_sw less_med_sw less_hci_sw
##    <chr>                                 <int> <int>       <dbl>       <dbl>
##  1 17-alpha-ethinylestradiol                56    56          51          56
##  2 17-alpha-methyldihydrotestosterone        1     0           0           0
##  3 17-beta-estradiol                        12    12           1          11
##  4 17-beta-trenbolone                       16    16           2           2
##  5 5-fluorouracil                            1     0           0           0
##  6 acetylsalicylic acid                      3     3           0           2
##  7 albendazole                               1     1           0           0
##  8 alprazolam                                1     1           0           1
##  9 aminosidine                               1     0           0           0
## 10 amiodarone                                1     1           0           0
## # ℹ 174 more rows
## # ℹ 13 more variables: prop_less_med_sw <dbl>, prop_less_hic_sw <dbl>,
## #   n_ef <int>, less_med_ef <dbl>, less_hci_ef <dbl>, prop_less_med_ef <dbl>,
## #   prop_less_hic_ef <dbl>, fdiff_med_sw <dbl>, fdiff_hci_sw <dbl>,
## #   fdiff_med_ef <dbl>, fdiff_hci_ef <dbl>, surfacewater_n <int>,
## #   effluent_n <int>

Surface water

Fig 2

Lets plot the mean-fold difference Building the plot in two halves so that it can fit in our manuscript

Fig 2 The median fold difference in tested concentrations in EIPAAB and median surface water concentration (i.e. tested dose / median surface water concentration)

plot_left

plot_right

Note: the size of the points represent the number of occurrence in EIPAAB, and colour of the point represents the number of detections in the environmental databases

Save the figure

invisible(fig_2 <- plot_left | plot_right)
ggsave(filename = paste0(fig_wd, "./figure-2.pdf"),
       plot = fig_2,
       width = 210,
       height = 297/1.25,  # Specify the height of the plot
       units = "mm")

For surface waters there were 139 compounds

nrow(plot_sw_data)
## [1] 139

Waste water

Fig S2

Let’s plot mean-fold difference fo effluent

Building the plot in two halves so that it can fit in our manuscript

plot_ef_data <- under_levels %>% 
  dplyr::filter(!is.na(fdiff_med_ef)) %>% 
  dplyr::arrange(desc(fdiff_med_ef)) %>% 
  dplyr::mutate(
    order = 1:nrow(.),
    compound_name_corrected = sentence_case(compound_name_corrected),
    compound_name_short = if_else(
      nchar(as.character(compound_name_corrected)) > 13, 
      str_trunc(compound_name_corrected, 16, ellipsis = "..."), 
      compound_name_corrected
    ),
    compound_name_short = factor(compound_name_short, levels = rev(compound_name_short)), # Fixed the parentheses
    compound_name_short = factor(compound_name_short, levels = rev(compound_name_short)), # Fixed the parentheses
    eipaab_n_cat = case_when(
      eipaab_n == 1 ~ "1",
      eipaab_n >= 2 & eipaab_n <= 5 ~ "2 to 5",
      eipaab_n >= 6 & eipaab_n <= 10 ~ "6 to 10",
      eipaab_n > 10 ~ "10+",
      TRUE ~ "ERROR"  # This catches unexpected values
    ),
    eipaab_n_cat = factor(eipaab_n_cat, levels = c("1", "2 to 5", "6 to 10", "10+"))
  )

vline_data_left <- data.frame(xintercept = log(10^(-1:2)),
                         xlabs = as.character(10^(-1:2))) %>% 
  dplyr::mutate(xlabs = paste0("×",xlabs))

vline_data_right <- data.frame(xintercept = log(10^(2:7)),
                         xlabs = as.character(10^(2:7))) %>% 
  dplyr::mutate(xlabs = paste0("×",xlabs))


n_half <- ceiling(nrow(plot_ef_data) / 2)

plot_ef_data_right <- plot_ef_data %>% 
  dplyr::slice(1:n_half)

plot_ef_data_left <- plot_ef_data %>% 
  dplyr::slice((n_half + 1):nrow(plot_ef_data))

plot_left <- plot_ef_data_left %>% 
  ggplot(aes(x = log(fdiff_med_ef), y = compound_name_short, fill = eipaab_n_cat)) +
  geom_vline(data = vline_data_left, aes(xintercept = xintercept), 
             linetype = "dashed", colour = "black", alpha = 0.5) +
  geom_text(data = vline_data_left, aes(x = xintercept, y = "Amitriptyline", label = xlabs), 
            inherit.aes = FALSE, angle = 90, vjust = -0.5, hjust = 1, size = 3) +
  geom_point(alpha = 0.5, shape = 21, size = 2) +
  theme_classic() +
  
  # Adjusted log scale with 10-fold increments
  scale_x_continuous(
    breaks = log(10^(-1:2)),  # Explicitly set log-scale breaks
    labels = function(x) format(exp(x), scientific = TRUE)
  ) +

  labs(
    subtitle = "Tested concentrations vs median waste water",
    x = "Log mean fold difference of tested and median waste water concentrations",
    y = "Compounds",
    fill = "EIPAAB data"
  ) +
  theme(
    legend.position = "bottom",
    legend.justification = "right"
  )


plot_right <- plot_ef_data_right %>% 
  ggplot(aes(x = log(fdiff_med_ef), y = compound_name_short, fill = eipaab_n_cat)) +
  geom_vline(data = vline_data_right, aes(xintercept = xintercept), 
             linetype = "dashed", colour = "black", alpha = 0.5) +
  geom_text(data = vline_data_right, aes(x = xintercept, y = "Risperidone", label = xlabs), 
            inherit.aes = FALSE, angle = 90, vjust = -0.5, hjust = 1, size = 3) +
  geom_point(alpha = 0.5, shape = 21, size = 2) +
  theme_classic() +
  
  # Adjusted log scale: removes 10^2 while keeping 10^3 to 10^7
  scale_x_continuous(
    breaks = log(c(10^(3:7))),  # Explicitly setting ticks without 10^2
    labels = function(x) format(exp(x), scientific = TRUE)
  ) +

  theme(
    axis.title = element_blank(),  # Removes axis labels
    legend.position = "none"
  )

Fig S2: The median fold difference in tested concentrations in EIPAAB and median waste water concentration (i.e. tested dose / median waste water concentration)

plot_left

plot_right

Note: the size of the points represent the number of occurrence in EIPAAB, and colour of the point represents the number of detections in the environmental databases

Save the figure

invisible(fig_s2 <- plot_left | plot_right)
ggsave(filename = paste0(fig_wd, "./figure-s2.pdf"), plot = fig_s2, width = 210,
    height = 297/1.25, units = "mm")

Summary table

Looking at the number of compounds in the database that have data at concentrations below median and upper 95 CI

The percentage of compounds in the EIPAAB database that have data at concentrations less then median and upper 95% credible interval of surface water and wastewater concentrations

compound_sum_table_surf <- under_levels %>%
    dplyr::reframe(total_compounds = sum(!is.na(prop_less_med_sw)), count_less_med_sw = sum(prop_less_med_sw ==
        0, na.rm = TRUE), count_less_hic_sw = sum(prop_less_hic_sw == 0, na.rm = TRUE),
        percent_less_med_sw = (count_less_med_sw/total_compounds) * 100, percent_less_hic_sw = (count_less_hic_sw/total_compounds) *
            100, `Percent with no data under median concentration` = paste0(round(percent_less_med_sw,
            2), "%", " (", count_less_med_sw, " of ", total_compounds, ")"), `Percent with no data under upper 95 CI concentration` = paste0(round(percent_less_hic_sw,
            2), "%", " (", count_less_hic_sw, " of ", total_compounds, ")")) %>%
    dplyr::mutate(Matrix = "Surface water") %>%
    dplyr::select(Matrix, "Percent with no data under median concentration", "Percent with no data under upper 95 CI concentration")

compound_sum_table_waste <- under_levels %>%
    dplyr::reframe(total_compounds = sum(!is.na(prop_less_med_ef)), count_less_med_ef = sum(prop_less_med_ef ==
        0, na.rm = TRUE), count_less_hic_ef = sum(prop_less_hic_ef == 0, na.rm = TRUE),
        percent_less_med_ef = (count_less_med_ef/total_compounds) * 100, percent_less_hic_ef = (count_less_hic_ef/total_compounds) *
            100, `Percent with no data under median concentration` = paste0(round(percent_less_med_ef,
            2), "%", " (", count_less_med_ef, " of ", total_compounds, ")"), `Percent with no data under upper 95 CI concentration` = paste0(round(percent_less_hic_ef,
            2), "%", " (", count_less_hic_ef, " of ", total_compounds, ")")) %>%
    dplyr::mutate(Matrix = "Wastewater") %>%
    dplyr::select(Matrix, "Percent with no data under median concentration", "Percent with no data under upper 95 CI concentration")

compound_sum_table <- rbind(compound_sum_table_surf, compound_sum_table_waste)

compound_sum_table %>%
    gt() %>%
    fmt_number(columns = c("Percent with no data under median concentration", "Percent with no data under upper 95 CI concentration"),
        decimals = 0) %>%
    cols_align(align = "center", columns = everything())
Matrix Percent with no data under median concentration Percent with no data under upper 95 CI concentration
Surface water 74.82% (104 of 139) 50.36% (70 of 139)
Wastewater 64.86% (96 of 148) 44.59% (66 of 148)

Occurance

Occurance summary data

Making a summary data frame for each compound, all_surfacewater, that includes: (1) the number of samples, (2) the number of positive detections (based on single samples, not summary statistics), (3) the rank order of detections, (4) the percentage of positive detections (only for compounds with more then 10 sample units), and, (5) the rank order of percentage of positive detection.

all_surfacewater <- all_databases %>%
    dplyr::filter(source != "eipaab", matrix_group == "surfacewater") %>%
    dplyr::group_by(compound_name_corrected) %>%
    dplyr::reframe(combind_samples = sum(n_units_est, na.rm = TRUE), combind_rows = length(unique_row_id),
        combind_singles = sum(concentration_type == "single", na.rm = TRUE), combind_detection = sum(value >
            0, na.rm = TRUE), combind_single_detection = sum(value > 0 & concentration_type ==
            "single", na.rm = TRUE), combind_percent_detection = if_else(combind_singles >
            10, (combind_single_detection/combind_singles) * 100, NA)) %>%
    ungroup() %>%
    dplyr::mutate(combind_rank_samples = rank(-combind_samples, ties.method = "max",
        na.last = "keep"), combind_rank_detection = rank(-combind_detection, ties.method = "max",
        na.last = "keep"), combind_rank_detection_percent = rank(-combind_percent_detection,
        ties.method = "max", na.last = "keep"))

There’s a total of 1650 compounds in this surface water summary

all_surfacewater %>%
    dplyr::distinct(compound_name_corrected) %>%
    nrow(.)
## [1] 1650

Making occurrence count in EIPAAB, 📊 eipaab_counts.

all_surfacewater_list <- all_surfacewater %>%
    dplyr::distinct(compound_name_corrected) %>%
    dplyr::pull(compound_name_corrected)

eipaab_counts <- all_databases %>%
    dplyr::filter(source == "eipaab" & compound_name_corrected %in% all_surfacewater_list) %>%
    dplyr::group_by(compound_name_corrected) %>%
    dplyr::reframe(eipaab_n = sum(n_units_est, na.rm = TRUE), eipaab_n_log = log(eipaab_n)) %>%
    ungroup() %>%
    dplyr::mutate(eipaab_rank = rank(-eipaab_n, ties.method = "max", na.last = "keep")) %>%
    tidyr::complete(., compound_name_corrected)

Combining the data together to make 📊 test_surfacewater

test_surfacewater <- left_join(all_surfacewater, eipaab_counts, by = "compound_name_corrected") %>%
    dplyr::mutate(rank_samples_dif = eipaab_rank - combind_rank_samples, rank_samples_dif = if_else(is.na(eipaab_n),
        NA, rank_samples_dif), rank_detection_dif = eipaab_rank - combind_rank_detection,
        rank_detection_percent_dif = eipaab_rank - combind_rank_detection_percent) %>%
    dplyr::arrange(desc(rank_detection_dif), desc(combind_detection)) %>%
    dplyr::mutate(rank_detection_dif = 1:nrow(.)) %>%
    dplyr::arrange(desc(rank_detection_percent_dif), desc(combind_percent_detection)) %>%
    dplyr::mutate(rank_detection_percent_dif = 1:nrow(.))

uba_rank_detection_max <- test_surfacewater %>%
    dplyr::arrange(desc(combind_rank_detection)) %>%
    dplyr::slice(1) %>%
    dplyr::pull(combind_rank_detection)

uba_rank_detection_percent_max <- test_surfacewater %>%
    dplyr::arrange(desc(combind_rank_detection_percent)) %>%
    dplyr::slice(1) %>%
    dplyr::pull(combind_rank_detection_percent)

Figuring out what the max rank occurrence and positive detections could be.

uba_rank_detection_max <- test_surfacewater %>%
    dplyr::arrange(desc(combind_rank_detection)) %>%
    dplyr::slice(1) %>%
    dplyr::pull(combind_rank_detection)

uba_rank_detection_percent_max <- test_surfacewater %>%
    dplyr::arrange(desc(combind_rank_detection_percent)) %>%
    dplyr::slice(1) %>%
    dplyr::pull(combind_rank_detection_percent)

print(paste0("The max rank in terms of the number of occurrences is ", uba_rank_detection_max,
    ". The max rank for the percentage of postive detections is ", uba_rank_detection_percent_max))
## [1] "The max rank in terms of the number of occurrences is 1650. The max rank for the percentage of postive detections is 1424"

The 10 most common compounds in EIPAAB versus occurrence and detection frequency in UBA surface waters UBA samples.

table_top_surfacewater <- test_surfacewater %>%
    arrange(desc(eipaab_n)) %>%
    slice(1:10) %>%
    mutate(compound_name = sentence_case(compound_name_corrected), combind_percent_detection = round(combind_percent_detection,
        2)) %>%
    select(compound_name, eipaab_n, combind_samples, combind_detection, combind_percent_detection,
        eipaab_rank, combind_rank_detection, combind_rank_detection_percent) %>%
    rename(Name = compound_name, `EIPAAB tests` = eipaab_n, `Environmental samples` = combind_samples,
        `All detections` = combind_detection, `Precent single detections` = combind_percent_detection,
        `EIPAAB rank (1–870)` = eipaab_rank, `Rank total detections (1–1654)` = combind_rank_detection,
        `Rank  percent detections (1–1428)*` = combind_rank_detection_percent) %>%
    flextable() %>%
    colformat_num(j = c("Environmental samples", "All detections"), digits = 0) %>%
    align(align = "center", part = "all") %>%
    autofit()

table_top_surfacewater

Name

EIPAAB tests

Environmental samples

All detections

Precent single detections

EIPAAB rank (1–870)

Rank total detections (1–1654)

Rank percent detections (1–1428)*

Fluoxetine

122

6,642

197

2.92

1

185

367

17-alpha-ethinylestradiol

56

18,120

626

3.04

2

113

361

Venlafaxine

33

5,333

1,308

46.68

3

77

79

Citalopram

32

3,471

697

27.62

4

108

125

Sertraline

30

4,928

56

1.04

5

299

471

Carbamazepine

27

56,084

14,637

54.90

6

16

65

Oxazepam

26

18,858

8,704

49.00

7

23

74

Ibuprofen

18

27,447

5,389

25.04

8

30

134

17-beta-trenbolone

16

247

4

0.00

9

647

1,424

Diazepam

15

7,192

399

6.68

10

143

261

Note: the rank for total percentage detections does not range from 1 to the number of compounds (i.e. 1–1428) because percentage detections were only calculated where the number of single samples are greater than ten

Saving the table

save_as_docx(table_top_surfacewater, path = paste0(fig_wd, "./table-S7.docx"))

Modeling

Total occruance

Here we will build a simple Bayesian negative binomial (log-link) regression model to look at the relationship between the two parameters.

Model structure

test_surfacewater %>%
    dplyr::filter(!is.na(eipaab_n))
## # A tibble: 163 × 16
##    compound_name_corrected combind_samples combind_rows combind_singles
##    <chr>                             <int>        <int>           <int>
##  1 guanylurea                          718           68              27
##  2 irbesartan                         7084         6876            6840
##  3 lidocaine                          7540         6189            6143
##  4 sulfapyridine                      3069         1851            1714
##  5 desvenlafaxine                     4045         2012            1918
##  6 metoprolol                        16887        11528           11332
##  7 cetirizine                         2012         1921            1912
##  8 genistein                           143          143             143
##  9 nicotine                           3620         3620            3620
## 10 diethyltoluamide                  38383        38383           38383
## # ℹ 153 more rows
## # ℹ 12 more variables: combind_detection <int>, combind_single_detection <int>,
## #   combind_percent_detection <dbl>, combind_rank_samples <int>,
## #   combind_rank_detection <int>, combind_rank_detection_percent <int>,
## #   eipaab_n <int>, eipaab_n_log <dbl>, eipaab_rank <int>,
## #   rank_samples_dif <int>, rank_detection_dif <int>,
## #   rank_detection_percent_dif <int>
detection_negbin_str <- bf(eipaab_n ~ combind_detection, family = negbinomial())

These are the default priors.

suppressWarnings(get_prior(detection_negbin_str, data = test_surfacewater, family = negbinomial()))
##                 prior     class              coef group resp dpar nlpar lb ub
##                (flat)         b                                              
##                (flat)         b combind_detection                            
##  student_t(3, 0, 2.5) Intercept                                              
##   inv_gamma(0.4, 0.3)     shape                                          0   
##        source
##       default
##  (vectorized)
##       default
##       default

Run model

Run model. This has been hashed out as I have saved the model, and will re-load it instead of re-running it.

options(brms.backend = "cmdstanr")
detection_negbin_mod <- brm(
  detection_negbin_str,
  data = test_surfacewater,
  cores = 4,
  chains = 4,
  #prior = mod_priors, #use defaults
  warmup = 1000,
  seed = 20250418,
  thin = 2,
  iter = 8000,
  control = list(max_treedepth = 20, adapt_delta = 0.95),
  save_pars = save_pars(all = TRUE),
  sample_prior = TRUE,
  file = paste0(mods_wd, './detection_negbin_mod')
)

Re-load models

Reload the model if necessary.

detection_negbin_mod <- readRDS(file = paste0(mods_wd, "./detection_negbin_mod.rds"))

Model diagnostics

The R2 value is very low, but should be fine for the purposes of this investigation.

performance::r2_bayes(detection_negbin_mod, robust = FALSE, ci = 0.95)
## # Bayesian R2 with Compatibility Interval
## 
##   Conditional R2: 0.077 (95% CI [1.251e-07, 0.254])

The mode generally follows the observed data, but isn’t the best fit. Again, this should be fine for the purposes of this investigation.

color_scheme_set("red")
brms::pp_check(detection_negbin_mod, ndraws = 50, type = "ecdf_overlay")

Checking R-hat values to confirm model convergence and ESS fo estimates.

print(summary(detection_negbin_mod, prob = 0.95))  #All Rhat = 1 (good).
##  Family: negbinomial 
##   Links: mu = log; shape = identity 
## Formula: eipaab_n ~ combind_detection 
##    Data: test_surfacewater (Number of observations: 163) 
##   Draws: 4 chains, each with iter = 8000; warmup = 1000; thin = 2;
##          total post-warmup draws = 14000
## 
## Regression Coefficients:
##                   Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## Intercept             1.35      0.10     1.15     1.56 1.00     8978     9012
## combind_detection     0.00      0.00     0.00     0.00 1.00    13850    12137
## 
## Further Distributional Parameters:
##       Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## shape     0.81      0.09     0.65     1.01 1.00     8054     7759
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

Diagnostic plots look fine. A smaller shape value indicates greater overdispersion (variance is much larger than the mean).

color_scheme_set("red")
plot(detection_negbin_mod)

Model estimates

Pulling the conditional effect estimates from the model. The estimate corresponds to the effect of the predictor on the log of the expected response variable. A 1-unit increase (i.e. 1 detection) in the number of environmental detections increases the expected occurrence in EIPAAB database by 0.011% (i.e. (exp(0.0001105696)-1)×100 = 0.01105757). In other words, for each 1000 detections we would expect a 5% increase in test in the EIPAAB database.

esitmates_3 <- conditional_effects(detection_negbin_mod)
estimate_data_3 <- esitmates_3$combind_detection

model_summary_3 <- as.data.frame(fixef(detection_negbin_mod, summary = TRUE)) %>%
    rownames_to_column() %>%
    clean_names()

estimate_3 <- model_summary_3$estimate[2]
L95CI_3 <- model_summary_3$q2_5[2]
H95CI_3 <- model_summary_3$q97_5[2]
estimate_percent_3 <- (exp(estimate_3) - 1) * 100
L95CI_percent_3 <- (exp(L95CI_3) - 1) * 100
H95CI_percent_3 <- (exp(H95CI_3) - 1) * 100

model_summary_3_ft <- model_summary_3 %>%
    dplyr::mutate(across(where(is.double), ~round(.x, 5))) %>%
    dplyr::rename(predictor = rowname) %>%
    dplyr::mutate(CrI = paste0(q2_5, " to ", q97_5)) %>%
    dplyr::select(predictor, estimate, CrI) %>%
    flextable() %>%
    set_header_labels(predictor = "Predictor", estimate = "Estimate", CrI = "95% CrI") %>%
    autofit()

model_summary_3_ft

Predictor

Estimate

95% CrI

Intercept

1.35109

1.15232 to 1.55699

combind_detection

0.00011

4e-05 to 0.00019

test_surfacewater %>%
    filter(!is.na(eipaab_n)) %>%
    dplyr::distinct(compound_name_corrected) %>%
    dplyr::arrange(desc(compound_name_corrected))
## # A tibble: 163 × 1
##    compound_name_corrected
##    <chr>                  
##  1 yohimbine              
##  2 xylazine               
##  3 verapamil              
##  4 venlafaxine            
##  5 tylosin                
##  6 tramadol               
##  7 toltrazuril            
##  8 tiamulin               
##  9 thrimethoprim          
## 10 tetracycline           
## # ℹ 153 more rows
Fig S3a

Each pharmaceutical occurrence in the behavioural database plotted against the number of positive detections in the environmental databases. The five compounds highlighted represent those that are currently underrepresented in the behaviour test database based on their rank detections (i.e. rank occurrence in behaviour test databases vs rank detections). The point size represents the total number of samples across all environmental databases.

text_coord <- test_surfacewater %>%
    filter(!is.na(eipaab_n)) %>%
    summarise(x = max(combind_detection, na.rm = TRUE)/2, y = max(eipaab_n, na.rm = TRUE))

y_coord <- text_coord %>%
    pull(y)

x_coord <- text_coord %>%
    pull(x)


fig_s3a <- ggplot() + geom_line(data = estimate_data_3, aes(x = combind_detection,
    y = estimate__), color = "#E01F33", linewidth = 1) + geom_ribbon(data = estimate_data_3,
    aes(x = combind_detection, ymin = lower__, ymax = upper__), fill = "#E01F33",
    alpha = 0.1) + geom_point(data = test_surfacewater %>%
    filter(!is.na(eipaab_n)), aes(x = combind_detection, y = eipaab_n, size = combind_samples),
    alpha = 0.4, shape = 21, color = "#01080A", fill = "#E01F33") + annotate("text",
    x = x_coord, y = y_coord, label = paste0("Bayesian negative binomial regression",
        "\n", "Effect estimate: ", round(estimate_percent_3, 3), "\n", "95%CrI: ",
        round(L95CI_percent_3, 3), " to ", round(H95CI_percent_3, 3)), hjust = 0,
    vjust = 1, size = 4) + geom_text_repel(data = test_surfacewater %>%
    filter(!is.na(eipaab_n), rank_detection_dif %in% 1:5), aes(x = combind_detection,
    y = eipaab_n, label = compound_name_corrected), size = 3, box.padding = 0.7) +
    # Adjust scales
scale_fill_gradientn(colours = colorspace::diverge_hcl(7)) + scale_colour_manual(values = c(`TRUE` = "#01080A",
    `FALSE` = "#01080A"), guide = "none") + labs(title = "Test vs environmental detections",
    x = "Total detections in environmental databases", y = "Occurrence in behavioural database",
    fill = "Number of surface water samples", size = "Number of surface water samples") +
    theme_clean() + theme(legend.position = "bottom", legend.justification = "center")

fig_s3a

This code saves the figure above as PDF.

ggsave(filename = paste0(fig_wd, "./figure-s3a.pdf"),
       plot = fig_s3a,
       width = 210,
       height = 297/1.5,  # Specify the height of the plot
       units = "mm")

Percentage detections

Model structure.

percent_detection_negbin_str <- bf(eipaab_n ~ combind_percent_detection, family = negbinomial())

Run model

Run model. This has been hashed out as I have saved the model, and will re-load it instead of re-running it.

options(brms.backend = "cmdstanr")
percent_detection_negbin_mod <- brm(
  percent_detection_negbin_str,
  data = test_surfacewater,
  cores = 4,
  chains = 4,
  #prior = mod_priors, #use defaults
  warmup = 1000,
  seed = 20250418,
  thin = 2,
  iter = 8000,
  control = list(max_treedepth = 20, adapt_delta = 0.95),
  save_pars = save_pars(all = TRUE),
  sample_prior = TRUE,
  file = paste0(mods_wd, './percent_detection_negbin_mod')
)

Re-load models

Reload the model if necessary.

percent_detection_negbin_mod <- readRDS(file = paste0(mods_wd, "./percent_detection_negbin_mod.rds"))

Model diagnostics

The R2 value is very low, but should be fine for the purposes of this investigation.

performance::r2_bayes(percent_detection_negbin_mod, robust = FALSE, ci = 0.95)
## # Bayesian R2 with Compatibility Interval
## 
##   Conditional R2: 0.020 (95% CI [1.900e-10, 0.065])

Check fit.

color_scheme_set("red")
brms::pp_check(percent_detection_negbin_mod, ndraws = 100, type = "ecdf_overlay")

Checking R-hat values to confirm model convergence.

print(summary(percent_detection_negbin_mod, prob = 0.95))  #All Rhat = 1 (good).
##  Family: negbinomial 
##   Links: mu = log; shape = identity 
## Formula: eipaab_n ~ combind_percent_detection 
##    Data: test_surfacewater (Number of observations: 156) 
##   Draws: 4 chains, each with iter = 8000; warmup = 1000; thin = 2;
##          total post-warmup draws = 14000
## 
## Regression Coefficients:
##                           Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS
## Intercept                     1.37      0.12     1.13     1.62 1.00    13369
## combind_percent_detection     0.01      0.01     0.00     0.02 1.00    12856
##                           Tail_ESS
## Intercept                    12015
## combind_percent_detection    12000
## 
## Further Distributional Parameters:
##       Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
## shape     0.78      0.09     0.62     0.97 1.00    13705    12182
## 
## Draws were sampled using sample(hmc). For each parameter, Bulk_ESS
## and Tail_ESS are effective sample size measures, and Rhat is the potential
## scale reduction factor on split chains (at convergence, Rhat = 1).

Check chains, and estimates.

color_scheme_set("red")
plot(percent_detection_negbin_mod)

Model estimates

Pulling the conditional effect estimates from the model. The estimate corresponds to the effect of the predictor on the log of the expected response variable. A 1-unit increase (i.e. 1%) in the percentage of environmental detections increases the expected occurrence in EIPAAB database by 1.17% (i.e. (exp(0.01160477)-1)*100 = 1.167237).

esitmates_4 <- conditional_effects(percent_detection_negbin_mod)
estimate_data_4 <- esitmates_4$combind_percent_detection

model_summary_4 <- as.data.frame(fixef(percent_detection_negbin_mod, summary = TRUE)) %>%
    rownames_to_column() %>%
    clean_names()

estimate_4 <- model_summary_4$estimate[2]
L95CI_4 <- model_summary_4$q2_5[2]
H95CI_4 <- model_summary_4$q97_5[2]
estimate_percent_4 <- (exp(estimate_4) - 1) * 100
L95CI_percent_4 <- (exp(L95CI_4) - 1) * 100
H95CI_percent_4 <- (exp(H95CI_4) - 1) * 100

model_summary_4_ft <- model_summary_4 %>%
    dplyr::mutate(across(where(is.double), ~round(.x, 3))) %>%
    dplyr::rename(predictor = rowname) %>%
    dplyr::mutate(CrI = paste0(q2_5, " to ", q97_5)) %>%
    dplyr::select(predictor, estimate, CrI) %>%
    flextable() %>%
    set_header_labels(predictor = "Predictor", estimate = "Estimate", CrI = "95% CrI") %>%
    autofit()

model_summary_4_ft

Predictor

Estimate

95% CrI

Intercept

1.366

1.127 to 1.615

combind_percent_detection

0.012

0.001 to 0.023

Compounds with the most environmental detections that are not present in EIPAAB.

table_S5 <- test_surfacewater %>%
    filter(is.na(eipaab_n)) %>%
    arrange(desc(combind_detection)) %>%
    slice(1:15) %>%
    mutate(compound_name = sentence_case(compound_name_corrected)) %>%
    select(compound_name, combind_samples, combind_detection, combind_percent_detection,
        combind_rank_samples, combind_rank_detection) %>%
    rename(Name = compound_name, Samples = combind_samples, Detections = combind_detection,
        `Percentage detections` = combind_percent_detection, `Rank samples` = combind_rank_samples,
        `Rank detections` = combind_rank_detection) %>%
    flextable() %>%
    colformat_num(j = c("Samples", "Detections"), digits = 0) %>%
    align(align = "center", part = "all") %>%
    autofit()

table_S5

Name

Samples

Detections

Percentage detections

Rank samples

Rank detections

Glyphosate

79,682

33,956

42.614392

28

1

Atrazine

121,709

29,480

24.221709

3

2

Bentazone

98,363

28,315

28.786231

19

3

Carbendazim

110,395

22,410

20.299832

9

4

Lead

57,662

21,463

37.222087

56

5

Tebuconazole

106,651

20,664

19.375346

12

6

Imidacloprid

90,527

20,383

22.515934

21

7

Boscalid

68,930

19,456

28.225736

39

8

Chlorotoluron

114,605

18,155

15.841368

4

9

Diflufenican

70,085

17,074

24.361846

38

11

Isoproturon

128,238

16,944

13.212932

1

12

Azoxystrobin

83,421

16,516

19.798372

25

13

Propyzamide

105,962

16,302

15.384761

14

14

Chloridazon

98,996

15,203

15.357186

17

15

Ethofumesate

111,478

10,649

9.552557

8

19

Fig S3b

Pharmaceutical occurrence in the behavioural test database plotted against the percentage of positive detections (i.e. positive detections relative to total samples for that compound) in environmental databases. The five compounds highlighted represent those that are currently underrepresented in the behaviour test database based on their rank percentage detections (i.e. rank occurrence in behaviour test databases vs rank percentage detections in environmental databases). The point size represents the total number of samples across all environmental databases.

text_coord <- test_surfacewater %>%
    filter(!is.na(eipaab_n)) %>%
    summarise(x = max(combind_percent_detection, na.rm = TRUE)/2, y = max(eipaab_n,
        na.rm = TRUE))

y_coord <- text_coord %>%
    pull(y)

x_coord <- text_coord %>%
    pull(x)

fig_s3b <- ggplot() + geom_line(data = estimate_data_4, aes(x = combind_percent_detection,
    y = estimate__), color = "#1c4595", linewidth = 1) + geom_ribbon(data = estimate_data_4,
    aes(x = combind_percent_detection, ymin = lower__, ymax = upper__), fill = "#1c4595",
    alpha = 0.1) + geom_point(data = test_surfacewater %>%
    filter(!is.na(eipaab_n)) %>%
    dplyr::arrange(rank_detection_percent_dif) %>%
    mutate(is_top5 = rank_detection_percent_dif %in% 1:5), aes(x = combind_percent_detection,
    y = eipaab_n, size = combind_singles), alpha = 0.4, shape = 21, color = "#01080A",
    fill = "#1C4794") + annotate("text", x = x_coord, y = y_coord, label = paste0("Bayesian negative binomial regression",
    "\n", "Effect estimate (%): ", round(estimate_percent_4, 3), "\n", "95%CrI: ",
    round(L95CI_percent_4, 3), " to ", round(H95CI_percent_4, 3)), hjust = 0, vjust = 1,
    size = 4) + geom_text_repel(data = test_surfacewater %>%
    filter(!is.na(eipaab_n), rank_detection_percent_dif %in% 1:5), aes(x = combind_percent_detection,
    y = eipaab_n, label = compound_name_corrected), size = 3, box.padding = 0.7) +
    scale_fill_gradientn(colours = colorspace::diverge_hcl(7)) + scale_colour_manual(values = c(`TRUE` = "#01080A",
    `FALSE` = "#01080A"), guide = "none") + labs(title = "Test vs percentage detections",
    x = "Percentage detections in environmental databases", y = "Occurrence in behavioural database",
    size = "Number of single samples") + theme_clean() + theme(legend.position = "bottom",
    legend.justification = "center")

fig_s3b

Save the figure

ggsave(filename = paste0(fig_wd, "./figure-s3b.pdf"),
       plot = fig_s3b,
       width = 210,
       height = 297/1.5,  # Specify the height of the plot
       units = "mm")

Here are compound that are not in EIPAAB database, but have high positive detection rates in the environment.

gap_tb <- test_surfacewater %>%
    filter(is.na(eipaab_n), !is.na(combind_percent_detection)) %>%
    arrange(desc(combind_percent_detection)) %>%
    slice(1:20) %>%
    mutate(compound_name = sentence_case(compound_name_corrected), combind_percent_detection = round(combind_percent_detection,
        2)) %>%
    select(compound_name, combind_singles, combind_single_detection, combind_percent_detection) %>%
    rename(Name = compound_name, `Single samples` = combind_singles, `Single detections` = combind_single_detection,
        `Percent detections` = combind_percent_detection) %>%
    flextable() %>%
    align(align = "center", part = "all") %>%
    autofit()

gap_tb

Name

Single samples

Single detections

Percent detections

Sulisobenzone

101

101

100.00

10,11-dihydro-10,11-dihydroxy carbamazepine

13

13

100.00

Alpha-hydroxy metoprolol

24

24

100.00

Acetaminophen sulfate

24

24

100.00

17-beta-estradiol-17-glucuronide

14

14

100.00

Estriol-16-glucuronide

14

14

100.00

2-hydroxy ibuprofen

41

40

97.56

4-acetaminoantipyrine

21

20

95.24

Methenamine

811

768

94.70

1-hydroxy ibuprofen

61

57

93.44

Carbamazepine-3oh

26

24

92.31

Carboxyibuprofen

39

36

92.31

O-desmethyltramadol

34

31

91.18

Perindopril

112

101

90.18

Lamotrigine

1,555

1,378

88.62

4-formylaminoantipyrine

40

35

87.50

Erythromycin-h2o

168

143

85.12

10-hydroxy-amitriptyline

13

11

84.62

10,11-dihydro-10,11-epoxycarbamazepine

44

37

84.09

Carbamazepine-2oh

31

25

80.65

Saving the table

save_as_docx(gap_tb, path = paste0(fig_wd, "./gap_tb.docx"))

☕️ Dose selection function

select_doses() Generates a sequence of recommended doses based on observed environmental concentration data, using a specified measure of central tendency (mean, median, or mode) and geometric spacing. The function selects up to max_doses values spaced above and/or below the central value, optionally constrained to observed values and limits of quantification (LOQ). It returns a dose table, a summary of the distribution, and side-by-side plots showing dose placement on both raw and log-scaled density curves. Designed to support transparent and data-informed dose selection for ecotoxicological study design.

select_doses <- function(data, variable, central_tendency = "median", min_spacing = 3.2,
    max_doses = 5, LOQ = 1e-04, direction = "both", limit_to_observed = FALSE) {
    library(ggplot2)
    library(dplyr)
    library(patchwork)

    values <- data[[variable]]
    values <- values[!is.na(values) & values > 0]
    if (length(values) < 3)
        stop("Not enough data points.")

    # Central tendency
    center <- switch(tolower(central_tendency), mean = mean(values), median = median(values),
        mode = {
            d <- density(values)
            d$x[which.max(d$y)]
        }, stop("Invalid central_tendency"))

    upper <- quantile(values, 0.95)
    lower <- quantile(values, 0.05)
    max_val <- max(values)
    min_val <- min(values)

    doses <- c(center)
    labels <- c("D1")

    # Generate above and below doses
    above <- c()
    below <- c()

    # Above
    val <- center
    while (length(above) < max_doses) {
        val <- val * min_spacing
        if (limit_to_observed && val > max_val)
            break
        above <- c(above, val)
    }

    # Below
    val <- center
    while (length(below) < max_doses) {
        val <- val/min_spacing
        if (val <= LOQ)
            break
        if (limit_to_observed && val < min_val)
            break
        below <- c(below, val)
    }

    # Alternate selection
    dose_list <- c(center)
    i <- 1
    while (length(dose_list) < max_doses) {
        if (i <= length(above))
            dose_list <- c(dose_list, above[i])
        if (length(dose_list) == max_doses)
            break
        if (i <= length(below))
            dose_list <- c(dose_list, below[i])
        if (length(dose_list) == max_doses)
            break
        i <- i + 1
        if (i > 20)
            break
    }

    if (length(dose_list) < max_doses) {
        warning("Unable to select enough doses within constraints.")
        return(NA)
    }

    dose_list <- sort(dose_list)
    dose_df <- data.frame(dose = paste0("D", seq_along(dose_list)), value = dose_list)

    # Summary
    summary_tbl <- data.frame(central = center, upper_95 = upper, lower_05 = lower,
        max = max_val, min = min_val, LOQ = LOQ, row.names = NULL)

    # Base density for grey fill
    dens <- density(values)
    dens_df <- data.frame(x = dens$x, y = dens$y)
    dens_df <- dens_df %>%
        mutate(in_cri = x >= lower & x <= upper)

    caption_text <- paste("Density plots show the distribution of observed environmental data.\n",
        "The blue dashed line indicates the selected measure of central tendency\n",
        "(mean, median, or mode). The red dot-dash line marks the Limit of\n", "Quantification (LOQ), defult 0.0001. Black points represent the recommended doses\n",
        "based on spacing from the central value. The shaded grey region denotes\n",
        "the 95% credible interval (CrI) of the data distribution.", sep = "")

    # Plot
    base_plot <- ggplot() + geom_density(data = data, aes_string(x = variable), size = 1.2) +
        geom_ribbon(data = dens_df, aes(x = x, ymin = 0, ymax = ifelse(in_cri, y,
            NA)), fill = "grey80", alpha = 0.5) + geom_vline(xintercept = center,
        color = "blue", linetype = "dashed", size = 1) + geom_vline(xintercept = LOQ,
        color = "red", linetype = "dotdash") + geom_point(data = dose_df, aes(x = value,
        y = 0), size = 3, color = "black") + geom_text(data = dose_df, aes(x = value,
        y = 0.01, label = dose), vjust = -1) + ggtitle("Dose Selection (Raw Scale)") +
        theme_minimal() + labs(caption = caption_text) + theme(plot.caption = element_text(size = 9,
        hjust = 0, lineheight = 1.1))

    dens_log <- density(log10(values))
    dens_df_log <- data.frame(x = 10^dens_log$x, y = dens_log$y)

    log_plot <- ggplot() + geom_line(data = dens_df_log, aes(x = x, y = y), size = 1.2) +
        annotate("rect", xmin = lower, xmax = upper, ymin = 0, ymax = max(dens_df_log$y),
            fill = "grey80", alpha = 0.4) + geom_vline(xintercept = center, color = "blue",
        linetype = "dashed", size = 1) + geom_vline(xintercept = LOQ, color = "red",
        linetype = "dotdash") + geom_point(data = dose_df, aes(x = value, y = 0),
        size = 3, color = "black") + geom_text(data = dose_df, aes(x = value, y = 0.01 *
        max(dens_df_log$y), label = dose), vjust = -1) + scale_x_log10() + ggtitle("Dose Selection (Log Scale)") +
        theme_minimal()

    plots <- print(base_plot + log_plot)

    plots

    covered_prop <- mean(values >= min(dose_list) & values <= max(dose_list))
    summary_tbl$distribution_covered <- covered_prop

    return(list(doses = dose_df, summary = summary_tbl, plot = plots))
}

Example use

Here’s an example data set based on one of the compounds in the dataset, fluoxetine

fluoxetine_surfacwaters <- all_databases %>%
    dplyr::filter(matrix_group == "surfacewater" & compound_name == "fluoxetine") %>%
    dplyr::arrange(value) %>%
    dplyr::filter(value > 0)

Apply to concentration data

my_doses <- select_doses(data = fluoxetine_surfacwaters, variable = "value", central_tendency = "median",
    min_spacing = 5, max_doses = 3, LOQ = 1e-04, direction = "both", limit_to_observed = FALSE)

LS0tDQp0aXRsZTogImZpZWxkLXZzLWxhYi1kb3Nlcy1yLTIiDQphdXRob3I6ICJKYWtlIE1hcnRpbiINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBkZXB0aDogNA0KICAgIG51bWJlcl9zZWN0aW9uczogbm8NCiAgICB0aGVtZTogIGNvc21vDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCmtuaXQ6IHwNCiAgKGZ1bmN0aW9uKGlucHV0LCAuLi4pIHsNCiAgICBybWFya2Rvd246OnJlbmRlcigNCiAgICAgIGlucHV0LA0KICAgICAgb3V0cHV0X2ZpbGUgPSBwYXN0ZTAoDQogICAgICAgJ2luZGV4Lmh0bWwnDQogICAgICApLA0KICAgICAgZW52aXIgPSBnbG9iYWxlbnYoKQ0KICAgICkNCiAgfSkNCi0tLQ0KDQoNClshW0xpY2Vuc2U6IENDIEJZIDQuMF0oaHR0cHM6Ly9pbWcuc2hpZWxkcy5pby9iYWRnZS9MaWNlbnNlLUNDJTIwQlklMjA0LjAtbGlnaHRncmV5LnN2ZyldKGh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS80LjAvKQ0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIPCfk5UgUkVBRE1FDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCioqU0NSSVBUIFBBUlQgMiBvZiAzKiogDQpUaGlzIHNjcmlwdCAiZmllbGQtdnMtbGFiLWRvc2VzLXItMiIgaXMgdGhlIHNlY29uZCBvZiB0aHJlZSB1c2VkIGZvciB0aGlzIHByb2plY3QuIFRoZSBzZWNvbmQgc2NyaXB0LCAiZmllbGQtdnMtbGFiLWRvc2VzLXItMiIsIGRlYWxzIHdpdGggdGhlIGFuYWx5c2lzLiBUaGUgZmlyc3Qgc2NyaXB0LCAiZmllbGQtdnMtbGFiLWRvc2VzLXItMSIsIHRpZGllcyB0aGUgaW5wdXQgZGF0YWJhc2VzIGFuZCBjb21iaW5lcyB0aGVtIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBmb3IgYW5hbHlzaXMsIHRoZSB0aGlyZCBkZWFscyB3aXRoIGEgc3BlY2lmaWMgc3ViLXNldCBRdWFsaXR5IEFzc3VyYW5jZSBhbmQgUXVhbGl0eSBDb250cm9sIChRQS9RQykgYW5hbHlzaXMuIElmIHlvdSBkb3dubG9hZCB0aGUgZGF0YSBmaWxlcyBmb3IgdGhpcyBwcm9qZWN0IGZyb20gdGhlIE9wZW4gU2NpZW5jZSBGcmFtZXdvcmsgKGh0dHBzOi8vb3NmLmlvL2g2Y2RlLywgRE9JIDEwLjE3NjA1L09TRi5JTy9INkNERSksIHlvdSBjYW4gcnVuIHRoaXMgc2NyaXB0IGluZGVwZW5kZW50bHkuICAgDQoNCioqQVJUSUNMRSoqIA0KVGhpcyBSIHNjcmlwdCBpcyBhc3NvY2lhdGVkIHdpdGggKipNYXJ0aW4gZXQgYWwgKDIwMjUpKiogIkFsaWduaW5nIEJlaGF2aW91cmFsIEVjb3RveGljb2xvZ3kgVGVzdHMgd2l0aCBSZWFsLVdvcmxkIFdhdGVyIENvbmNlbnRyYXRpb25zOiBDdXJyZW50IE1pbmltdW0gVGVzdGVkIExldmVscyBGYXIgRXhjZWVkIEVudmlyb25tZW50YWwgUmVhbGl0eSIgRE9JOiA8KnRvIGJlIGFkZGVkKj4gDQoNCioqQVVUSE9SUyoqICANCkpha2UgTS4gTWFydGluIDxzdXA+MSwyLDM8L3N1cD4qICANCkVyaW4gUy4gTWNDYWxsdW0gPHN1cD4xPC9zdXA+ICAgDQpKYWNrIEEuIEJhbmQgPHN1cD4yLDQ8L3N1cD4gDQoNCioqQUZGSUxJQVRJT05TKiogIA0KKDEpIFNjaG9vbCBvZiBMaWZlIGFuZCBFbnZpcm9ubWVudGFsIFNjaWVuY2VzLCBEZWFraW4gVW5pdmVyc2l0eSwgR2VlbG9uZywgQXVzdHJhbGlhICANCigyKSBEZXBhcnRtZW50IG9mIFdpbGRsaWZlLCBGaXNoLCBhbmQgRW52aXJvbm1lbnRhbCBTdHVkaWVzLCBTd2VkaXNoIFVuaXZlcnNpdHkgb2YgQWdyaWN1bHR1cmFsIFNjaWVuY2VzLCBVbWXDpSwgU3dlZGVuICANCigzKSBTY2hvb2wgb2YgQmlvbG9naWNhbCBTY2llbmNlcywgTW9uYXNoIFVuaXZlcnNpdHksIE1lbGJvdXJuZSwgQXVzdHJhbGlhICANCig0KSBJbnN0aXR1dGUgb2YgWm9vbG9neSwgWm9vbG9naWNhbCBTb2NpZXR5IG9mIExvbmRvbiwgTG9uZG9uLCBVbml0ZWQgS2luZ2RvbSAgDQooKikgQ29ycmVzcG9uZGluZyBhdXRob3IgIA0KDQoqKkFJTSoqICANClRoZSBhaW0gb2YgdGhpcyBzdHVkeSBpcyB0d28tZm9sZDogKDEpIGlkZW50aWZ5IHdoZXRoZXIgdGVzdGVkIGNvbmNlbnRyYXRpb25zIGluIGJlaGF2aW91cmFsIGVjb3RveGljb2xvZ3kgc3R1ZGllcyByZWZsZWN0IHRob3NlIHRoYXQgYXJlIGRldGVjdGVkIGluIHN1cmZhY2Ugd2F0ZXJzIGFuZC9vciB3YXN0ZSB3YXRlcjsgKDIpIHRvIGNvbXBhcmUgdGhlIHRhcmdldCBwaGFybWFjZXV0aWNhbHMgdGVzdGVkIGluIGJlaGF2aW91cmFsIGVjb3RveGljb2xvZ3kgdG8gd2hhdCBoYXZlIGJlZW4gZGV0ZWN0ZWQgaW4gdGhlIGVudmlyb25tZW50LCBoaWdobGlnaHRpbmcgcG90ZW50aWFsIHRhcmdldHMgZm9yIGZ1dHVyZSBpbnZlc3RpZ2F0aW9uLiAgDQoNCioqTUVUSE9EUyoqICANClRvIGFjaGlldmUgdGhlc2UgYWltcywgdGhpcyBzdHVkeSBjYXBpdGFsaXNlcyBvbiBmb3VyIGV4dGVuc2l2ZSBvcGVuIGFjY2VzcyBkYXRhYmFzZXMsIG9uZSBvZiB3aGljaCBmb2N1c2VzIG9uIHBlZXItcmV2aWV3IHJlc2VhcmNoIG9uIHRoZSBiZWhhdmlvdXJhbCBlY290b3hpY29sb2d5IG9mIHBoYXJtYWNldXRpY2FsczxzdXA+MTwvc3VwPiwgYW5kIHRoZSByZW1haW5pbmcgZm9jdXMgb24gcGhhcm1hY2V1dGljYWwgZW52aXJvbm1lbnRhbCBvY2N1cnJlbmNlIGFuZCBjb25jZW50cmF0aW9uczxzdXA+MiwzLDQ8L3N1cD4uICANCg0KKDEpIFRoZSBFdmlkZW5jZSBvZiB0aGUgSW1wYWN0cyBvZiBQaGFybWFjZXV0aWNhbHMgb24gQXF1YXRpYyBBbmltYWwgQmVoYXZpb3VyIChFSVBBQUIpIGRhdGFiYXNlIChbTWFydGluIGV0IGFsLiAyMDI1XShodHRwczovL2RvaS5vcmcvMTAuMzI5NDIvWDJORzlSKSkgICANCg0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij4NCjxhIGhyZWY9Imh0dHBzOi8vZ2l0aHViLmNvbS9KYWtlTWFydGluUmVzZWFyY2gvRUlQQUFCLWRhdGFiYXNlL3RyZWUvbWFpbi9pbnB1dC1kYXRhIiANCnRhcmdldD0iX2JsYW5rIiANCnN0eWxlPSJkaXNwbGF5OiBpbmxpbmUtYmxvY2s7IHBhZGRpbmc6IDVweCAxMHB4OyBmb250LXNpemU6IDE2cHg7IGNvbG9yOiB3aGl0ZTsgYmFja2dyb3VuZC1jb2xvcjogIzVENjUzMjsgdGV4dC1kZWNvcmF0aW9uOiBub25lOyBib3JkZXItcmFkaXVzOiA1cHg7Ij4NCvCfk6UgRUlQQUFCIERhdGFiYXNlDQo8L2E+DQo8L2Rpdj4NCg0KKDIpIFRoZSBVbXdlbHQgQnVuZGVzYW10IFBoYXJtYWNldXRpY2FscyBpbiB0aGUgZW52aXJvbm1lbnQgKFBIQVJNUy1VQkEpIGRhdGFiYXNlDQoNCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPg0KPGEgaHJlZj0iaHR0cHM6Ly93d3cudW13ZWx0YnVuZGVzYW10LmRlL2VuL2RhdGFiYXNlLXBoYXJtYWNldXRpY2Fscy1pbi10aGUtZW52aXJvbm1lbnQtMSIgDQp0YXJnZXQ9Il9ibGFuayIgDQpzdHlsZT0iZGlzcGxheTogaW5saW5lLWJsb2NrOyBwYWRkaW5nOiA1cHggMTBweDsgZm9udC1zaXplOiAxNnB4OyBjb2xvcjogd2hpdGU7IGJhY2tncm91bmQtY29sb3I6ICM1RDY1MzI7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgYm9yZGVyLXJhZGl1czogNXB4OyI+DQrwn5OlIFBIQVJNUy1VQkEgRGF0YWJhc2UNCjwvYT4NCjwvZGl2Pg0KDQoNCigzKSBQaGFybWFjZXV0aWNhbCBwb2xsdXRpb24gb2YgdGhlIHdvcmxk4oCZcyByaXZlcnMgKFtXaWxraW5zb24gZXQgYWwgMjAyMl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwNzMvcG5hcy4yMTEzOTQ3MTE5KSksIHNwZWNpZmljYWxseSAiRGF0YXNldCBTNC4gRGF0YWJhc2Ugb2YgcGhhcm1hY2V1dGljYWwgY29uY2VudHJhdGlvbnMgYXQgYWxsIHRoZSBzYW1wbGluZyBsb2NhdGlvbnMgbW9uaXRvcmVkIGluIHRoaXMgcHJvamVjdCIgIA0KDQo8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBjZW50ZXI7Ij4NCjxhIGhyZWY9Imh0dHBzOi8vd3d3LnBuYXMub3JnL2RvaS9zdXBwbC8xMC4xMDczL3BuYXMuMjExMzk0NzExOS9zdXBwbF9maWxlL3BuYXMuMjExMzk0NzExOS5zZDA0Lnhsc3giIA0KdGFyZ2V0PSJfYmxhbmsiIA0Kc3R5bGU9ImRpc3BsYXk6IGlubGluZS1ibG9jazsgcGFkZGluZzogNXB4IDEwcHg7IGZvbnQtc2l6ZTogMTZweDsgY29sb3I6IHdoaXRlOyBiYWNrZ3JvdW5kLWNvbG9yOiAjNUQ2NTMyOyB0ZXh0LWRlY29yYXRpb246IG5vbmU7IGJvcmRlci1yYWRpdXM6IDVweDsiPg0K8J+TpSBXaWxraW5zb24gRGF0YWJhc2UNCjwvYT4NCjwvZGl2Pg0KDQoNCig0KSBUaGUgTk9STUFOIEVNUE9EQVQgRGF0YWJhc2UgLSBDaGVtaWNhbCBPY2N1cnJlbmNlIERhdGEuDQoNCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPg0KPGEgaHJlZj0iaHR0cHM6Ly93d3cubm9ybWFuLW5ldHdvcmsuY29tL25kcy9lbXBvZGF0L2NoZW1pY2FsU2VhcmNoLnBocD9zPW5ldyIgDQp0YXJnZXQ9Il9ibGFuayIgDQpzdHlsZT0iZGlzcGxheTogaW5saW5lLWJsb2NrOyBwYWRkaW5nOiA1cHggMTBweDsgZm9udC1zaXplOiAxNnB4OyBjb2xvcjogd2hpdGU7IGJhY2tncm91bmQtY29sb3I6ICM1RDY1MzI7IHRleHQtZGVjb3JhdGlvbjogbm9uZTsgYm9yZGVyLXJhZGl1czogNXB4OyI+DQrwn5OlIE5PUk1BTiBFTVBPREFUIERhdGFiYXNlDQo8L2E+DQo8L2Rpdj4NCg0KDQoqKlNDUklQVCoqICANClRoaXMgaXMgYW4gUiBtYXJrZG93biBzY3JpcHQgd3JpdHRlbiBpbiBSIHN0dWRpbyAoMjAyMy4wOS4wKzQ2MyDigJxEZXNlcnQgU3VuZmxvd2Vy4oCdIFJlbGVhc2UpLiBUaGUgJ2ZpZWxkLXZzLWxhYi1kb3NlcycgZ2l0IHJlcG9zaXRvcnkgaG9zdHMgdGhpcyBzY3JpcHQsIGFuZCBpZiBkb3dubG9hZGVkIChvciBwdWxsZWQpIGl0IHdpbGwgcmVwcm9kdWNlIGFsbCBkYXRhIHRpZHkvZmlsdGVyLCBhbmFseXNpcywgYW5kIHZpc3VhbGlzYXRpb25zIHVzZWQgaW4gTWFydGluIGV0IGFsICgyMDI1KS4gVGhlIEdpdEh1YiByZXBvc2l0b3J5IGluY2x1ZGVzIGFsbCByYXcgaW5wdXQgZGF0YSBmcm9tIHRoZSBmb3VyIG9wZW4gYWNjZXNzIGRhdGFiYXNlcywgYXMgd2VsbCBhcyBhbGwgb3V0cHV0IGRhdGEgYW5kIGZpZ3VyZXMuIEkgaGF2ZSB0cmllZCB0byBzdHJ1Y3R1cmUgdGhpcyBjb2RlIHdpdGggT3BlbiwgUmVsaWFibGUsIGFuZCBUcmFuc3BhcmVudCAoT1JUKSBjb2RpbmcgcHJhY3RpY2VzIGluIG1pbmQuIEZlZWwgZnJlZSB0byByZWFjaCBvdXQgaWYgYW55dGhpbmcgaXMgdW5jbGVhci4gIA0KDQpUaGUgUiBzY3JpcHQgZmlyc3QgY2xlYW5zIGFuZCBmaWx0ZXJzIGVhY2ggZGF0YWJhc2VzIHRvIG1ha2UgdGhlbSBjb21wYXJhYmxlLiBJbiB0aGUgZ2VybmFsc2l0IHNlbnNjZSBJbiB0ZXJtcyBvZiBmaWx0ZXJpbmcsIHdlIHRhcmdldCBwaGFybWFjZXV0aWNhbCBjb21wb3VuZHMsIHN1cmZhY2Ugd2F0ZXJzL3dhc3RlIHdhdGVyLCBhbmQgZW52aXJvbm1lbnRhbC9sYWIgc2FtcGxlcyBtZWFzdXJlZCBpbiB1bml0cyBvZiBtYXNzIHBlciB2b2x1bWUgb2Ygd2F0ZXIgKGUuZy4gdWcvTCkuICANCg0KV2UgdGhlbiBzdW1tYXJpc2Ugb2NjdXJyZW5jZSBhbmQgY29uY2VudHJhdGlvbnMgYmV0d2VlbiBFSVBBQUIgdGhlIHRocmVlIGVudmlyb25tZW50YWwgZGF0YWJhc2VzIChVQkEsIFdpbGtzb24gYW5kIE5PUk1BTikgc2VwYXJhdGVseSwgdG8gaGlnaGxpZ2h0IHRoZSBjb25zaXN0ZW5jeSBvZiBkaWZmZXJlbnQgZW52aXJvbm1lbnRhbCBkYXRhYmFzZXMuICAgDQoNCkxhc3RseSB3ZSBjb21iaW5lZCBhbGwgZW52aXJvbm1lbnRhbCBkYXRhc2V0cyBmb3IgYW4gb3ZlcmFsbCBhc3Nlc3NlcyBvZiBvY2N1cnJlbmNlIGFuZCBjb25jZW50cmF0aW9ucyBiZXR3ZWVuIEVJUEFBQiBhbmQgZW52aXJvbm1lbnRhbCBkYXRhLiAgDQoNClsqKkdpdEh1YioqXShodHRwczovL2dpdGh1Yi5jb20vSmFrZU1hcnRpblJlc2VhcmNoL2ZpZWxkLXZzLWxhYi1kb3NlcykNCg0KKipESVNDTEFJTUVSKiogIA0KSSAoSmFrZSBNYXJ0aW4pIGFtIGR5c2xleGljLiBJIGhhdmUgbWFkZSBhbiBlZmZvcnQgdG8gcmV2aWV3IHRoZSBzY3JpcHQgZm9yIGdyYW1tYXRpY2FsIGVycm9ycywgYnV0IHNvbWUgd2lsbCBsaWtlbHkgcmVtYWluLiBJIGFwb2xvZ2lzZS4gUGxlYXNlIHJlYWNoIG91dCB2aWEgdGhlIGNvbnRhY3QgZGV0YWlscyBiZWxvdyBpZiBhbnl0aGluZyBpcyB1bmNsZWFyLiAgDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMg8J+TpyBDb250YWN0DQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCvCfkJ8gKipKYWtlIE0uIE1hcnRpbioqDQogIA0K8J+TpyAqKkVtYWlsOioqIFtqYWtlLm1hcnRpbkBkZWFraW4uZWR1LmF1XShtYWlsdG86amFrZS5tYXJ0aW5AZGVha2luLmVkdS5hdSkgIA0KICANCvCfk6cgKipBbHQgRW1haWw6KiogW2pha2UubWFydGluLnJlc2VhcmNoQGdtYWlsLmNvbV0obWFpbHRvOmpha2UubWFydGluLnJlc2VhcmNoQGdtYWlsLmNvbSkgDQogIA0K8J+MkCAqKldlYjoqKiBbamFrZW1hcnRpbi5vcmddKGh0dHBzOi8vamFrZW1hcnRpbi5vcmcvKSAgDQogIA0K8J+QmSAqKkdpdEh1YioqOiBbSmFrZU1hcnRpblJlc2VhcmNoXShodHRwczovL2dpdGh1Yi5jb20vSmFrZU1hcnRpblJlc2VhcmNoKSAgDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMg8J+TkSBTaGFyaW5nL2FjY2Vzc2luZyBhbmQgY2l0aW5nDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCjEuICoqTGljZW5zZXMvcmVzdHJpY3Rpb25zIHBsYWNlZCBvbiB0aGUgZGF0YToqKiBDQy1CWSA0LjAgIA0KDQoyLiAqKkxpbmsgdG8gdGhlIGFzc29jaWF0ZWQgcHVibGljYXRpb246KiogICAgIA0K8J+apyAqKipUbyBiZSBhZGRlZCoqKiDwn5qnICAgICANCg0KMy4gKipSZWNvbW1lbmRlZCBjaXRhdGlvbiBmb3IgdGhpcyBkYXRhOioqICAgICAgDQrwn5qnICoqKlRvIGJlIGFkZGVkKioqIPCfmqcgICAgIA0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIOKame+4jyBTZXQtdXAgYW5kIHBhY2thZ2VzDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQoNCkhlcmUgd2UgZGVmaW5lIG91ciBLbml0IHNldHRpbmdzLCB0byBtYWtlIHRoZSBvdXRwdXQgbW9yZSB1c2VyIGZyaWVuZGx5LCBhbmQgdG8gY2FjaGUgb3V0cHV0IGZvciBmYXN0ZXIga25pdHRpbmcuICANCmBgYHtyIHNldHVwfQ0KI2tuaXRlciBzZXR0aW5nDQprbml0cjo6b3B0c19jaHVuayRzZXQoDQptZXNzYWdlID0gRkFMU0UsDQp3YXJuaW5nID0gRkFMU0UsICMgbm8gd2FybmluZ3MNCmNhY2hlID0gVFJVRSwjIENhY2hlaW5nIHRvIHNhdmUgdGltZSB3aGVuIGtuaXRpbmcNCmNhY2hlLmxhenkgPSBGQUxTRSwgIyBJIGFtIHJ1bm5pbmcgaW50byBpc3N1ZXMgd2l0aCBjYWNoZSBiZWNhdXNlIHRoZSBmaWxlcyBhcmUgdmVyeSBsYXJnZQ0KdGlkeSA9IFRSVUUNCikNCg0KIyAtLS0tIEluc3RhbGwgcGFjbWFuIGlmIGl0J3Mgbm90IGFscmVhZHkgaW5zdGFsbGVkIC0tLS0NCmlmICghcmVxdWlyZU5hbWVzcGFjZSgicGFjbWFuIiwgcXVpZXRseSA9IFRSVUUpKSBpbnN0YWxsLnBhY2thZ2VzKCJwYWNtYW4iKQ0KDQojIC0tLS0gTGlzdCBvZiByZXF1aXJlZCBwYWNrYWdlcyAtLS0tDQpwa2dzIDwtIGMoDQogICMgLS0tLS0gRGF0YSBWaXN1YWxpc2F0aW9uIC0tLS0tDQogICJnZ3RoZW1lcyIsICJiYXllc3Bsb3QiLCAiZ3QiLCAiZ3RzdW1tYXJ5IiwgInBsb3RseSIsICJxcXBsb3RyIiwgImdncmVwZWwiLA0KICAiY29sb3JzcGFjZSIsICJmbGV4dGFibGUiLCAib2ZmaWNlciIsICJzY2FsZXMiLA0KICANCiAgIyAtLS0tLSBUaWR5IERhdGEgYW5kIFdyYW5nbGluZyAtLS0tLQ0KICAidGlkeXZlcnNlIiwgImphbml0b3IiLCAicmVhZHhsIiwgImJyb29tLm1peGVkIiwgImRhdGEudGFibGUiLCAiZGV2dG9vbHMiLCAic2NhbGVzIiwgImdsdWUiLA0KICANCiAgIyAtLS0tLSBNb2RlbGxpbmcgYW5kIFN0YXRpc3RpY2FsIEFuYWx5c2lzIC0tLS0tDQogICJicm1zIiwgInJzdGFuIiwgImNtZHN0YW5yIiwgIm1hcmdpbmFsZWZmZWN0cyIsICJwZXJmb3JtYW5jZSIsICJlbW1lYW5zIiwNCiAgInRpZHliYXllcyIsImZ1dHVyZSIsICJjb2RhIg0KKQ0KDQojIC0tLS0gSW5zdGFsbCBhbmQgbG9hZCBhbGwgcGFja2FnZXMgdXNpbmcgcGFjbWFuIC0tLS0NCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcygNCiAgcGFjbWFuOjpwX2xvYWQoY2hhciA9IHBrZ3MsIGluc3RhbGwgPSBUUlVFKQ0KKQ0KYGBgDQoNCkhlcmUncyBhIGxpc3Qgb2YgdGhlIHBhY2thZ2UgbmFtZXMgIA0KDQpgYGB7cn0NCnBrZ3MNCmBgYA0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIPCflKcgQ3VzdG9tIGZ1bmN0aW9ucyANCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCg0KSGVyZSBhcmUgc29tZSBjdXN0b20gZnVuY3Rpb24gdXNlZCB3aXRoaW4gdGhpcyBzY3JpcHQuIA0KDQpgc2VudGVuY2VfY2FzZSgpYCBDaGFuZ2VzIGEgc3RyaW5nIGludG8gc2VudGVuY2UgY2FzZS4gIA0KDQpgYGB7cn0NCnNlbnRlbmNlX2Nhc2UgPC0gZnVuY3Rpb24odGV4dCkgew0KICB0ZXh0IDwtIHRvbG93ZXIodGV4dCkgIyBDb252ZXJ0IHRoZSBlbnRpcmUgdGV4dCB0byBsb3dlcmNhc2UNCiAgdGV4dCA8LSBzdWIoIl4oXFxzKlxcdykiLCAiXFxVXFwxIiwgdGV4dCwgcGVybCA9IFRSVUUpICMgQ2FwaXRhbGlzZSB0aGUgZmlyc3QgbGV0dGVyDQogIHJldHVybih0ZXh0KQ0KfQ0KYGBgDQoNCg0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KIyDwn5OCIERpcmVjdG9yaWVzIA0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KDQpIZXJlIHdlIGRlZmluZSB0aGUgZGlyZWN0b3JpZXMgZm9yIHRoZSBwcm9qZWN0LiBXZSBhbHNvIG1ha2UgdGhlIG91dHB1dCBkaXJlY3RvcmllcyBpZiB0aGV5IGRvbid0IGFscmVhZHkgZXhpc3QuIA0KDQojIyBJbnB1dA0KDQpUaGVzZSBhcmUgdGhlIGlucHV0IGRpcmVjdG9ycyBmb3IgdGhlIGRhdGFiYXNlcyB1c2VkIGluIHRoaXMgcHJvamVjdC4gDQoNCioq8J+TpSBkYXRhX3dkKio6IERpcmVjdG9yeSBmb3IgdGhlIGlucHV0IGRhdGEuICANCg0KYGBge3J9DQp3ZCA8LSBnZXR3ZCgpICMgZ2V0d2QgdGVsbHMgdXMgd2hhdCB0aGUgY3VycmVudCB3ZCBpcywgd2UgYXJlIHVzaW5nIHRoaXMgdG8gZHJvcCBpdCBpbiBhIHZhcmlhYmxlIGNhbGxlZCB3ZA0KZGF0YV93ZCA8LSBwYXN0ZTAod2QsICIuL2RhdGEiKSAjIGNyZWF0ZXMgYSB2YXJpYWJsZSB3aXRoIHRoZSBuYW1lIG9mIHRoZSB3ZCB3ZSB3YW50IHRvIHVzZQ0KYGBgDQoNCiMjIE91dHB1dA0KDQoqKmZpZ193ZCoqOiBEaXJlY3RvcnkgZm9yIHRoZSBvdXRwdXQgZmlndXJlcy4gIA0KDQpgYGB7ciwgZWNobyA9IFRSVUUsIHJlc3VsdHMgPSAiaGlkZSJ9DQpmaWdfd2QgPC0gcGFzdGUwKHdkLCAiLi9maWciKQ0KaWYgKCFkaXIuZXhpc3RzKCJmaWciKSkge2Rpci5jcmVhdGUoImZpZyIpfQ0KYGBgDQoNCioqbW9kc193ZCoqOiBEaXJlY3RvcnkgZm9yIHRoZSBvdXRwdXQgbW9kZWxzLiAgDQoNCmBgYHtyfQ0KbW9kc193ZCA8LSBwYXN0ZTAod2QsICIuL21vZHMiKQ0KaWYgKCFkaXIuZXhpc3RzKCJtb2RzIikpIHtkaXIuY3JlYXRlKCJtb2RzIil9DQpgYGANCg0KDQo8IS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0+DQojIPCfkr8gRGF0YWJhc2VzIA0KPCEtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tPg0KDQpUaGlzIGlzIHRoZSBkYXRhYmFzZSBtYWRlIGluIHBhcnQgb25lIG9mIHRoaXMgc2NyaXB0IGBtYXJ0aW4tZmllbGQtdnMtbGFiLWFsbC1kYXRhYmFzZXMuY3N2YC4gSXQgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSB0aGUgT3BlbiBTY2llbmNlIEZyYW1lIFdvcmsgKE9TRikgcHJvamVjdCBwYWdlICJBbGlnbmluZyBCZWhhdmlvdXJhbCBFY290b3hpY29sb2d5IHdpdGggUmVhbC1Xb3JsZCBXYXRlciBDb25jZW50cmF0aW9ucyIgYnkgSmFrZSBNLiBNYXJ0aW4sIEphY2sgQSBCcmFuZCwgYW5kIEVyaW4gTWNDYWxsdW0NCltMSU5LIGh0dHBzOi8vb3NmLmlvL2g2Y2RlL10gIA0KDQpZb3UgY2FuIGNpdGUgdGhlIGRhdGFiYXNlcyBhcyAgDQoNCj4gTWFydGluIEpNLCBCcmFuZCBKQSwgJiBNY0NhbGx1bSBFICgyMDI1KS4gQWxpZ25pbmcgQmVoYXZpb3VyYWwgRWNvdG94aWNvbG9neSB3aXRoIFJlYWwtV29ybGQgV2F0ZXIgQ29uY2VudHJhdGlvbnMgW21hcnRpbi1maWVsZC12cy1sYWItYWxsLWRhdGFiYXNlcy5jc3ZdLiA8aHR0cHM6Ly9kb2kub3JnLzEwLjE3NjA1L09TRi5JTy9INkNERT4NCg0KYGBge3J9DQphbGxfZGF0YWJhc2VzIDwtIGZyZWFkKHBhc3RlMChkYXRhX3dkLCAiLi9tYXJ0aW4tZmllbGQtdnMtbGFiLWFsbC1kYXRhYmFzZXMuY3N2IikpDQpgYGANCg0KSWYgeW91IHVzZSB0aGUgY29sbGF0ZWQgZGF0YWJhc2UsIHBsZWFzZSBhbHNvIGNpdGU6ICANCg0KPiBXaWxraW5zb24gSkwsIEJveGFsbCBBQkEsIEtvbHBpbiBEVywgTGV1bmcgS01ZLCBMYWkgUldTLCBXb25nIEQsIGV0IGFsLiBQaGFybWFjZXV0aWNhbCBwb2xsdXRpb24gb2YgdGhlIHdvcmxk4oCZcyByaXZlcnMuIFByb2NlZWRpbmdzIG9mIHRoZSBOYXRpb25hbCBBY2FkZW15IG9mIFNjaWVuY2VzLiAyMDIyOzExOTox4oCTMTAuIERPSToxMC4xMDczL3BuYXMuMjExMzk0NzExOS4gIA0KDQo+IE5ldHdvcmsgTi4gTk9STUFOIEVNUE9EQVQgRGF0YWJhc2UgLSBDaGVtaWNhbCBPY2N1cnJlbmNlIERhdGEgW0ludGVybmV0XS4gQXZhaWxhYmxlIGZyb206IGh0dHBzOi8vd3d3Lm5vcm1hbi1uZXR3b3JrLmNvbS9uZHMvZW1wb2RhdCAgDQoNCj4gYXVzIGRlciBCZWVrIFQsIFdlYmVyIEZBLCBCZXJnbWFubiBBLCBIaWNrbWFubiBTLCBFYmVydCBJLCBIZWluIEEsIGV0IGFsLiBQaGFybWFjZXV0aWNhbHMgaW4gdGhlIGVudmlyb25tZW50LUdsb2JhbCBvY2N1cnJlbmNlcyBhbmQgcGVyc3BlY3RpdmVzLiBFbnZpcm9uIFRveGljb2wgQ2hlbS4gMjAxNjszNTo4MjPigJMzNS5ET0k6MTAuMTAwMi9ldGMuMzMzOS4gICAgDQoNCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCiMg8J+nriBBbmFseXNpcyANCjwhLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLT4NCg0KIyMgU2FtcGxlcy9kYXRhDQoNCkxldCdzIHNlZSBob3cgbXVjaCBkYXRhIGlzIHByZXNlbnQgaW4gdGhlIGZpbHRlcmVkIGFuZCBjb21iaW5lZCBkYXRhYmFzZXMuICAgDQoNCmBgYHtyfQ0KdG90YWxfY29tcG91bmRzX2VpcGFhYiA8LSBhbGxfZGF0YWJhc2VzICU+JSANCiAgZHBseXI6OmZpbHRlcihzb3VyY2UgPT0gImVpcGFhYiIpICU+JSANCiAgbnJvdyguKQ0KDQp0b3RhbF93YXRlcl9zYW1wbGVzIDwtIGFsbF9kYXRhYmFzZXMgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNvdXJjZSAhPSAiZWlwYWFiIikgJT4lIA0KICBkcGx5cjo6cmVmcmFtZShuID0gc3VtKG5fdW5pdHNfZXN0LCBuYS5ybSA9IFRSVUUpKSAlPiUgDQogIGRwbHlyOjpwdWxsKG4pDQoNCmdsdWU6OmdsdWUoIlRoZXJlIGFyZSB7dG90YWxfY29tcG91bmRzX2VpcGFhYn0sIGJlaGF2aW91cmFsIGVjb3RveGljb2xvZ3kgZXhwb3N1cmVzIChjb21wb3VuZHMgYnkgc3BlY2llcykgYW5kIHt0b3RhbF93YXRlcl9zYW1wbGVzfSwgd2F0ZXIgc2FtcGxlcy4iKQ0KYGBgDQoNCg0KYGBge3J9DQphbGxfZGF0YWJhc2VzICU+JSANCiAgZHBseXI6OmZpbHRlcih2YWx1ZSA8IDApDQpgYGANCg0KSG93IG1hbnkgd2F0ZXIgc2FtcGxlcyBhcmUgc3VyZmFjZSB3YXRlciB2cyB3YXN0ZSB3YXRlcg0KDQpgYGB7cn0NCmFsbF9kYXRhYmFzZXMgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNvdXJjZSAhPSAiZWlwYWFiIikgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkobWF0cml4X2dyb3VwKSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKG4gPSBzdW0obl91bml0c19lc3QsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgZmxleHRhYmxlKCkgJT4lIA0KICBmb250KGZvbnRuYW1lID0gIkFyaWFsIiwgcGFydCA9ICJhbGwiKQ0KYGBgDQoNCiMjIyBUYWJsZSAxDQoNCkEgYnJlYWtkb3duIGZvciBkYXRhIGNvbnRyaWJ1dGVkIGJ5IGVhY2ggZGF0YSBzb3VyY2UuICANCg0KSW4gdGhlIG1hbnVzY3JpcHQgdGhpcyB3aWxsIGJlIGNhbGxlZCAqKlRhYmxlIDEqKiAgIA0KDQoqKlRhYmxlIDEqKjogVGhlIG51bWJlciBvZiBwaGFybWFjZXV0aWNhbCBjb21wb3VuZHMgd2l0aGluIGVhY2ggb2YgdGhlIGZvdXIgZGF0YXNldCAoRUlQQUFCLCBOT1JNQU4sIFBIQVJNUy1VQkEsIGFuZCBXaWxrc29tKSB1c2VkIGZvciB0aGlzIGludmVzdGlnYXRpb24sIGFuZCBudW1iZXIgb2YgaW5kaXZpZHVhbCBkYXRhIHJvd3Mgd2l0aGluIGVhY2ggb2YgdGhlIGRhdGFiYXNlIChpLmUuIHRlc3RzXmFeIGZvciBFSVBBQUIsIGFuZCB3YXRlciBzYW1wbGVzXmJeIGZvciB0aGUgZW52aXJvbm1lbnRhbCBvY2N1cnJlbmNlIGRhdGFiYXNlcykuDQoNCmBgYHtyfQ0Kc29ydWNlX2RhdGEgPC0gYWxsX2RhdGFiYXNlcyAlPiUgDQogIGRwbHlyOjpncm91cF9ieShzb3VyY2UpICU+JSANCiAgZHBseXI6OnJlZnJhbWUoRGF0YSA9IHN1bShuX3VuaXRzX2VzdCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgIk51bWJlciBvZiBjb21wb3VuZHMiID0gbGVuZ3RoKHVuaXF1ZShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkpKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoU291cmNlID0gc291cmNlKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoU291cmNlID0gY2FzZV93aGVuKA0KICAgIFNvdXJjZSA9PSAiZWlwYWFiIiB+ICJFSVBBQUIiLA0KICAgIFNvdXJjZSA9PSAibm9ybWFuIiB+ICJOT1JNQU7igKEiLA0KICAgIFNvdXJjZSA9PSAidWJhIiB+ICJQSEFSTVMtVUJBIiwNCiAgICBTb3VyY2UgPT0gIndpbGtzb24iIH4gIldpbGtzb24gZXQgYWwuIiwNCiAgKSkNCg0KdG90YWxfZGF0YSA8LSBhbGxfZGF0YWJhc2VzICU+JSANCiAgZHBseXI6OnJlZnJhbWUoRGF0YSA9IHN1bShuX3VuaXRzX2VzdCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgIk51bWJlciBvZiBjb21wb3VuZHMiID0gbGVuZ3RoKHVuaXF1ZShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkpKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoU291cmNlID0gIlRvdGFsIikNCg0Kbl9zdW1tYXJ5IDwtIHJiaW5kKHNvcnVjZV9kYXRhLCB0b3RhbF9kYXRhKQ0KDQoNCnRhYmxlXzEgPC0gbl9zdW1tYXJ5ICU+JQ0KICBtdXRhdGUoRGF0YSA9IHJvdW5kKERhdGEsIDApKSAlPiUNCiAgZmxleHRhYmxlKCkgJT4lDQogIGFsaWduKGFsaWduID0gImNlbnRlciIsIHBhcnQgPSAiYWxsIikgJT4lIA0KICBhdXRvZml0KCkNCg0KDQp0YWJsZV8xDQpgYGAgDQoNCg0KKk5vdGU6IChhKSBlYWNoIHJvdyBvZiB0aGUgRUlQQUFCIGRhdGFiYXNlIHJlcHJlc2VudHMgYSB1bmlxdWUgc3BlY2llcyBieSBjb21wb3VuZCBleHBvc3VyZTsgKGIpIGVhY2ggcm93IHdpdGhpbiB0aGUgTk9STUFOIGFuZCBQSEFSTVMtVUJBIHJlcHJlc2VudHMgb25lIG9mIG1vcmUgc2FtcGxlcywgYXMgYSByb3cgY2FuIGJlIGEgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRlbmN5IChlLmcuIG1lYW4gb3IgbWVkaWFuKSBvciBhIHNpbmdsZSB2YWx1ZSwgYWxsIHJvd3MgaW4gdGhlIFdpbGtzb24gZGF0YWJhc2UgcmVwcmVzZW50IGEgc2luZ2xlIHNhbXBsZTsg4oChIE5PUk1BTiBkYXRhYmFzZSB3YXMgZmlsdGVyZWQgdG8gcmVtb3ZlIGRhdGEgZnJvbSB0aGUgR2VybWFuIEVudmlyb25tZW50IEFnZW5jeSAoVUJBKSwgYW5kIHJlc3RyaWN0ZWQgdG8gMjAxNC0yMDIyLCBhcyB0aGUgUEhBUk1TLVVCQSBhbHNvIGluY2x1ZGVkIGRhdGEgZnJvbSBOT1JNQU4gcHJpb3IgdG8gMjAxNC4gVGh1cywgdGhpcyBudW1iZXIgaXMgbm90IGEgdHJ1ZSB0b3RhbCBudW1iZXIgb2YgcGhhcm1hY2V1dGljYWwgc2FtcGxlcyBwcmVzZW50IGluIHRoZSBOT1JNQU4gZGF0YWJhc2UuKg0KDQpTYXZlIHRoZSB0YWJsZSBhcyBhIHdvcmQgZG9jdW1lbnQgYFRhYmxlXzEuZG9jeGANCg0KYGBge3J9DQpzYXZlX2FzX2RvY3godGFibGVfMSwgcGF0aCA9IHBhc3RlMChmaWdfd2QsICIuL3RhYmxlXzEuZG9jeCIpKQ0KYGBgDQoNCg0KTnVtYmVyIG9mIGFnZ3JlZ2F0ZSB2YWx1ZXMgdGhhdCBoYWQgbWlzc2luZyBzYW1wbGUgc2l6ZS4gICANCg0KYGBge3J9DQphbGxfZGF0YSA8LSBhbGxfZGF0YWJhc2VzICU+JSANCiAgZHBseXI6OmZpbHRlcihzb3VyY2UgIT0gImVpcGFhYiIpICU+JSANCiAgZHBseXI6OnJlZnJhbWUodG90YWxfZGF0YV9uID0gbGVuZ3RoKGNvbXBvdW5kX2NhcykpICU+JSANCiAgZHBseXI6OnB1bGwodG90YWxfZGF0YV9uKQ0KDQpzb3VyY2VfdW5pdHNfbmFzIDwtIGFsbF9kYXRhYmFzZXMgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKGNvbmNlbnRyYXRpb25fdHlwZSA9PSAiY2VudHJhbCIpICU+JSANCiAgZHBseXI6Omdyb3VwX2J5KHNvdXJjZSkgJT4lIA0KICBkcGx5cjo6cmVmcmFtZSh0b3RhbF9jZW50cmFsID0gbGVuZ3RoKGNvbXBvdW5kX2NhcyksDQogICAgICAgICAgICAgICAgIHRvdGFsX21pc3NpbmdfdW5pdHMgPSBzdW0oaXMubmEobl91bml0cykpLA0KICAgICAgICAgICAgICAgICBhbGxfZGF0YV9uID0gYWxsX2RhdGEsDQogICAgICAgICAgICAgICAgIHBlcmNlbnRfYWdncmVnYXRlID0gcm91bmQoKHRvdGFsX2NlbnRyYWwvYWxsX2RhdGFfbikqMTAwLDIpLA0KICAgICAgICAgICAgICAgICBwZXJjZW50X21pc3NpbmcgPSByb3VuZCgodG90YWxfbWlzc2luZ191bml0cy9hbGxfZGF0YV9uKSoxMDAsMikpICU+JSANCiAgZHBseXI6OnJlbmFtZShTb3VyY2UgPSBzb3VyY2UsDQogICAgICAgICAgICAgICAgJ1RvdGFsIGNlbnRyYWwgZGF0YScgPSB0b3RhbF9jZW50cmFsLA0KICAgICAgICAgICAgICAgICdUb3RhbCBtaXNzaW5nIHVuaXRzJyA9IHRvdGFsX21pc3NpbmdfdW5pdHMsDQogICAgICAgICAgICAgICAgJ0FsbCBkYXRhJyA9IGFsbF9kYXRhX24sDQogICAgICAgICAgICAgICAgIlBlcmNlbnQgYWdncmVnYXRlIiA9IHBlcmNlbnRfYWdncmVnYXRlLA0KICAgICAgICAgICAgICAgICJQZXJjZW50IG1pc3NpbmciID0gcGVyY2VudF9taXNzaW5nKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoU291cmNlID0gY2FzZV93aGVuKA0KICAgIFNvdXJjZSA9PSAiZWlwYWFiIiB+ICJFSVBBQUIiLA0KICAgIFNvdXJjZSA9PSAibm9ybWFuIiB+ICJOT1JNQU7igKEiLA0KICAgIFNvdXJjZSA9PSAidWJhIiB+ICJQSEFSTVMtVUJBIiwNCiAgICBTb3VyY2UgPT0gIndpbGtzb24iIH4gIldpbGtzb24gZXQgYWwuIiwNCiAgKSkNCg0Kc291cmNlX3VuaXRzX25hcyAlPiUgDQogIGZsZXh0YWJsZSgpICU+JSANCiAgYXV0b2ZpdCgpDQpgYGANCg0KIyMgQ3Jvc3Mtb3Zlcg0KDQpDaGVja2luZyB0aGUgY3Jvc3Mtb3ZlciBiZXR3ZWVuIHRoZSBjb21wb3VuZHMgYXNzZXNzZWQgaW4gYmVoYXZpb3VyYWwgdGVzdCBhZ2FpbnN0IGFsbCBlbnZpcm9ubWVudGFsIG9jY3VycmVuY2UgZGF0YS4gIA0KDQpgYGB7cn0NCmVpcGFhYl9jb21wb3VuZHNfbGlzdCA8LSBhbGxfZGF0YWJhc2VzICU+JQ0KICBkcGx5cjo6ZmlsdGVyKHNvdXJjZSA9PSAiZWlwYWFiIikgJT4lDQogIGRwbHlyOjpkaXN0aW5jdChjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkgJT4lDQogIGRwbHlyOjpwdWxsKGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKQ0KDQpuX2VpcGFhYl9jb21wb3VuZHMgPC0gbGVuZ3RoKGVpcGFhYl9jb21wb3VuZHNfbGlzdCkNCg0KZW52aXJvX19jcm9zc19vdmVyX2NvbXBvdW5kc19saXN0IDwtIGFsbF9kYXRhYmFzZXMgJT4lDQogIGRwbHlyOjpmaWx0ZXIoc291cmNlICE9ICJlaXBhYWIiICYNCiAgICAgICAgICAgICAgICAgIGNvbXBvdW5kX25hbWVfY29ycmVjdGVkICVpbiUgZWlwYWFiX2NvbXBvdW5kc19saXN0KSAlPiUNCiAgZHBseXI6OmRpc3RpbmN0KGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSAlPiUNCiAgZHBseXI6OnB1bGwoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpDQoNCm5fY3Jvc3Nfb3Zlcl9jb21wb3VuZHMgPC0gbGVuZ3RoKGVudmlyb19fY3Jvc3Nfb3Zlcl9jb21wb3VuZHNfbGlzdCkNCg0KcGVyY2VudF9jcm9zc19vdmVyID0gcm91bmQobl9jcm9zc19vdmVyX2NvbXBvdW5kcyAvIG5fZWlwYWFiX2NvbXBvdW5kcyAqDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEwMCwgMikNCg0KZ2x1ZTo6Z2x1ZSgNCiAgIntuX2Nyb3NzX292ZXJfY29tcG91bmRzfSBvZiB0aGUge25fZWlwYWFiX2NvbXBvdW5kc30gcGhhcm1hY2V1dGljYWxzIHRlc3RlZCBpbiB0aGUgRUlQQUFCIGRhdGFiYXNlICh3aXRoIGFuIGVudmlyb25tZW50YWwgbW90aXZhdGlvbikgd2VyZSBwcmVzZW50IGluIHRoZSBlbnZpcm9ubWVudGFsIGRhdGFiYXNlcyAoaS5lLiB7cGVyY2VudF9jcm9zc19vdmVyfSUpLiINCikNCmBgYA0KDQoNCiMjIENvbmNlbnRyYXRpb25zIA0KDQpGb3IgcXVlc3Rpb25zIGFyb3VuZCBjb25jZW50cmF0aW9uLCBJIHdpbGwgZmlsdGVyZWQgZm9yIG9ubHkgY2FzZXMgd2hlcmUgdGhlIGxldmVscyBhcmUgYWJvdmUgemVybyBvciBhYm92ZSB0aGUgbGltaXQgb2YgZGV0ZWN0aW9uIChpLmUuIHBvc2l0aXZlIGRldGVjdGlvbnMgb3IgYWJvdmUgTE9EKS4gV2Ugd2lsbCBkbyB0aGlzIGZvciB0d28gcmVhc29ucywgdGhlIGZpcnN0IGJlaW5nIHRoYXQgd2hlbiByZXNlYXJjaGVycyBhcmUgdHJ5aW5nIHRvIGVzdGltYXRlIHRoZSByaXNrIG9mIGVudmlyb25tZW50YWwgZXhwb3N1cmUsIHdlIGFzc3VtZSB0aGF0IHRoZXkgYXJlIGVtdWxhdGluZyBwb3RlbnRpYWwgZXhwb3N1cmUgZXZlbnRzIGF0IGEgY29udGFtaW5hdGVkIHNpdGUsIG5vdCB0cnlpbmcgdG8gcmVwbGljYXRlIGEgdGhlb3JldGljYWwgZ2xvYmFsIHN1cmZhY2Ugd2F0ZXIgYXZlcmFnZS4gVGhlIHNlY29uZCBpcywgYnkgcmVtb3ZpbmcgemVybyB2YWx1ZXMgYW5kIHZhbHVlcyBiZWxvdyBMT0RzLCB3ZSBhcmUgdHJ5aW5nIHRvIGJlICoqKm1vcmUgY29uc2VydmF0aXZlKioqIHdpdGggdGhlIHJlbGF0aXZlIGNvbXBhcmlzb24gYmV0d2VlbiB0ZXN0ZWQgZG9zZXMgaW4gYmVoYXZpb3VyYWwgZWNvdG94aWNvbG9neSBhbmQgb2JzZXJ2ZWQgZW52aXJvbm1lbnRhbCBkb3NlcyAoYXMgb3Bwb3NlZCB0byB1c2luZyBzb21lIHZhbHVlIHN1YnN0aXR1dGUvZXN0aW1hdGVkIHZhbHVlIGZvciBzYW1wbGVzIGJlbG93IGRldGVjdGlvbiBsaW1pdCkuIFdlIGFyZSBhbHNvIHVzaW5nIGJvdGggc2luZ2xlIHZhbHVlcyBhbmQgc3VtbWFyeSB2YWx1ZXMgb2YgY2VudHJhbCB0ZW5kZW5jeSAobWVhbnMsIG1lZGlhbiwgZWN0KS4gRmlnDQoNCkluIHRoaXMgc2VjdGlvbiB3ZSBhcmUgY2FsY3VsYXRpbmcgc3VtbWFyeSBtZXRyaWNzIGZvciBjb25jZW50cmF0aW9ucyB1c2VkIGluIHRoZSBiZWhhdmlvdXJhbCB0ZXN0IGFuZCBkZXRlY3RlZCBpbiBzdXJmYWNlIHdhdGVycyBhbmQgd2FzdGV3YXRlciBpbiB0aGUgZW52aXJvbWVudGFsIGRhdGEuIFdlIHdpbGwgZG8gdGhpcyBzZXBhcmF0ZWx5IGZvciB3YXN0ZXdhdGVyIGFuZCBzdXJmYWNlIHdhdGVycyBzYW1wbGVzLiBXZSBhcmUgY2FsY3VsYXRpbmcsIHRoZSBtZWFuLCBtZWRpYW4sIG1pbiwgbWF4LCByYW5nZSwgbiAobnVtYmVyIG9mIGNvbnRyaWJ1dGluZyBkYXRhIHBvaW50cyksIHN0YW5kYXJkIGRldmlhdGlvbiAoc2QpLCBzdGFuZGFyZCBlcnJvciAoc2UpLCBhbmQgdGhlIHVwcGVyIGFuZCBsb3dlciAqKjk1JSBjcmVkaWJsZSBpbnRlcnZhbHMgKGNpX3VwcGVyLCBjaV9sb3dlcikgdXNpbmcgZW1waXJpY2FsIHF1YW50aWxlcyoqLiAgDQoNCmBgYHtyfQ0KY29uY19zdW1tYXJ5IDwtIGFsbF9kYXRhYmFzZXMgJT4lDQogIGRwbHlyOjpmaWx0ZXIodmFsdWUgPiAwKSAlPiUNCiAgZHBseXI6Omdyb3VwX2J5KG1hdHJpeF9ncm91cCwgY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpICU+JQ0KICBkcGx5cjo6cmVmcmFtZSgNCiAgICBtZWFuID0gbWVhbih2YWx1ZSwgbmEucm0gPSBUUlVFKSwNCiAgICBtZWRpYW4gPSBtZWRpYW4odmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgbWluID0gbWluKHZhbHVlLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1heCA9IG1heCh2YWx1ZSwgbmEucm0gPSBUUlVFKSwNCiAgICByYW5nZSA9IG1heCAtIG1pbiwNCiAgICBuID0gbGVuZ3RoKHVuaXF1ZV9yb3dfaWQpLA0KICAgIHNkID0gc2QodmFsdWUsIG5hLnJtID0gVFJVRSksDQogICAgY2lfbG93ZXIgPSBxdWFudGlsZSh2YWx1ZSwgMC4wMjUsIG5hLnJtID0gVFJVRSksDQogICAgY2lfdXBwZXIgPSBxdWFudGlsZSh2YWx1ZSwgMC45NzUsIG5hLnJtID0gVFJVRSkNCiAgKSAlPiUNCiAgZHBseXI6Om11dGF0ZSgNCiAgICBjaV9sb3dlciA9IGlmX2Vsc2UoaXMubmEoY2lfbG93ZXIpLCBOQSwgY2lfbG93ZXIpLA0KICAgIGNpX3VwcGVyID0gaWZfZWxzZShpcy5uYShjaV91cHBlciksIE5BLCBjaV91cHBlcikNCiAgKSAlPiUNCiAgdGlkeXI6OmNvbXBsZXRlKHRpZHlyOjpleHBhbmQobmVzdGluZyhtYXRyaXhfZ3JvdXAsIGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSkpDQpgYGANCg0KDQpNYWtpbmcgYSBtb3JlIHVzZSBmcmllbmRseSB0YWJsZSB3aXRoIHRoZSBjb25jZW50cmF0aW9uIG9mIGVhY2ggY29tcG91bmQgdGhhdCBoYXMgYSBwb3NpdGl2ZSBkZXRlY3Rpb24gZm9yIHdhc3Rld2F0ZXIgYW5kIG9yIHN1cmYgd2F0ZXJzLg0KDQpgYGB7cn0NCmNhc19udW1iZXIgPC0gYWxsX2RhdGFiYXNlcyAlPiUNCiAgZHBseXI6OmZpbHRlcih2YWx1ZSA+IDApICU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoY29tcG91bmRfY2FzX2NvcnJlY3RlZCkgJT4lIA0KICBkcGx5cjo6c2xpY2UoMSkgJT4lIA0KICBkcGx5cjo6dW5ncm91cCgpICU+JSANCiAgZHBseXI6OnNlbGVjdChjb21wb3VuZF9jYXNfY29ycmVjdGVkLCBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkNCg0KDQpjb25jX3N1bW1hcnlfZGYgPC0gY29uY19zdW1tYXJ5ICU+JSANCiAgZHBseXI6OmxlZnRfam9pbiguLCBjYXNfbnVtYmVyLCBieSA9ICJjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCIpICU+JSANCiAgZHBseXI6OnNlbGVjdChjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCwgY29tcG91bmRfY2FzX2NvcnJlY3RlZCwgbiwgZXZlcnl0aGluZygpKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoY29tcG91bmRfbmFtZSA9IGNvbXBvdW5kX25hbWVfY29ycmVjdGVkLA0KICAgICAgICAgICAgICAgIGNvbXBvdW5kX2NhcyA9IGNvbXBvdW5kX2Nhc19jb3JyZWN0ZWQsDQogICAgICAgICAgICAgICAgbWF0cml4ID0gbWF0cml4X2dyb3VwLA0KICAgICAgICAgICAgICAgIGNyaV85NV9sb3dlciA9IGNpX2xvd2VyLA0KICAgICAgICAgICAgICAgIGNyaV85NV91cHBlciA9IGNpX3VwcGVyKQ0KYGBgDQoNClNhdmluZyBhcyBhIGRhdGUgZnJhbWUgDQoNCmBgYHtyfQ0Kd3JpdGUuY3N2KGNvbmNfc3VtbWFyeV9kZiwgcGFzdGUwKGRhdGFfd2QsICIuL21hcnRpbi1jb25jZW50cmF0aW9uLXN1bW1hcnktdGFibGUuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNCkZvciB0aGUgU3VwcGxlbWVudGFyeSBtYXJ0aWFsLCB3ZSB3aWxsIG1ha2UgYSBzbWFsbGVyIHRhYmxlLiANCg0KYGBge3J9DQplZmZfdGFibGUgPC0gY29uY19zdW1tYXJ5X2RmICU+JSANCiAgZHBseXI6OmZpbHRlcihtYXRyaXggPT0gImVmZmx1ZW50IikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyh3aGVyZShpcy5kb3VibGUpLCB+IGZvcm1hdEMoLngsIGZvcm1hdCA9ICJmIiwgZGlnaXRzID0gNCkpKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoDQogICAgQ29tcG91bmQgPSBwYXN0ZTAoY29tcG91bmRfbmFtZSwgIiAoIiwgY29tcG91bmRfY2FzLCAiKSIpLA0KICAgICdXYXN0ZXdhdGVyIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCAowrVnL0wp4oChJyA9IGlmX2Vsc2UoDQogICAgICBuID09IDEsDQogICAgICBOQV9jaGFyYWN0ZXJfLA0KICAgICAgcGFzdGUwKGNyaV85NV9sb3dlciwgIuKAkyIsIGNyaV85NV91cHBlcikNCiAgICApLA0KICAgICdXYXN0ZXdhdGVyIHJhbmdlICjCtWcvTCknID0gaWZfZWxzZSgNCiAgICAgIG4gPT0gMSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8sDQogICAgICBwYXN0ZTAobWluLCAi4oCTIiwgbWF4KQ0KICAgICksDQogICAgJ1dhc3Rld2F0ZXIgbicgPSByb3VuZChuLDApLA0KICAgICdXYXN0ZXdhdGVyIG1lZGlhbiAowrVnL0wp4oCgJyA9IG1lZGlhbg0KICApICU+JQ0KICBkcGx5cjo6c2VsZWN0KENvbXBvdW5kLCAnV2FzdGV3YXRlciBuJywgJ1dhc3Rld2F0ZXIgbWVkaWFuICjCtWcvTCnigKAnLCAnV2FzdGV3YXRlciA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgKMK1Zy9MKeKAoScsICdXYXN0ZXdhdGVyIHJhbmdlICjCtWcvTCknKQ0KDQpzdXJmX3RhYmxlIDwtIGNvbmNfc3VtbWFyeV9kZiAlPiUgDQogIGRwbHlyOjpmaWx0ZXIobWF0cml4ID09ICJzdXJmYWNld2F0ZXIiKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIH4gZm9ybWF0QygueCwgZm9ybWF0ID0gImYiLCBkaWdpdHMgPSA0KSkpICU+JSANCiAgZHBseXI6Om11dGF0ZSgNCiAgICBDb21wb3VuZCA9IHBhc3RlMChjb21wb3VuZF9uYW1lLCAiICgiLCBjb21wb3VuZF9jYXMsICIpIiksDQogICAgJ1N1cmZhY2Ugd2F0ZXIgOTUlIGNyZWRpYmxlIGludGVydmFsICjCtWcvTCnigKEnID0gaWZfZWxzZSgNCiAgICAgIG4gPT0gMSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8sDQogICAgICBwYXN0ZTAoY3JpXzk1X2xvd2VyLCAi4oCTIiwgY3JpXzk1X3VwcGVyKQ0KICAgICksDQogICAgJ1N1cmZhY2Ugd2F0ZXIgcmFuZ2UgKMK1Zy9MKScgPSBpZl9lbHNlKA0KICAgICAgbiA9PSAxLA0KICAgICAgTkFfY2hhcmFjdGVyXywNCiAgICAgIHBhc3RlMChtaW4sICLigJMiLCBtYXgpDQogICAgKSwNCiAgICAnU3VyZmFjZSB3YXRlciBuJyA9IHJvdW5kKG4sMCksDQogICAgJ1N1cmZhY2Ugd2F0ZXIgbWVkaWFuICjCtWcvTCnigKAnID0gbWVkaWFuDQogICkgJT4lDQogIGRwbHlyOjpzZWxlY3QoQ29tcG91bmQsICdTdXJmYWNlIHdhdGVyIG4nLCAnU3VyZmFjZSB3YXRlciBtZWRpYW4gKMK1Zy9MKeKAoCcsICdTdXJmYWNlIHdhdGVyIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCAowrVnL0wp4oChJywgJ1N1cmZhY2Ugd2F0ZXIgcmFuZ2UgKMK1Zy9MKScpDQoNCnRhYmxlX2FsbCA8LSBmdWxsX2pvaW4oc3VyZl90YWJsZSwgZWZmX3RhYmxlLCBieSA9ICJDb21wb3VuZCIpICU+JQ0KICBndCgpICU+JQ0KICBjb2xzX2FsaWduKGFsaWduID0gImNlbnRlciIsIGNvbHVtbnMgPSBldmVyeXRoaW5nKCkpICU+JQ0KICB0YWJfb3B0aW9ucyh0YWJsZS5mb250LnNpemUgPSBweCgxMSkpICU+JSANCiAgICB0YWJfaGVhZGVyKA0KICAgIHRpdGxlID0gbWQoIioqU3VtbWFyeSBvZiBNZWFzdXJlZCBQaGFybWFjZXV0aWNhbCBDb25jZW50cmF0aW9ucyoqIiksDQogICAgc3VidGl0bGUgPSBtZCgiVGhpcyBzdW1tYXJ5IHRhYmxlIHdhcyBjb21waWxlZCBmb3IgKk1hcnRpbiBldCBhbC4gKDIwMjUpKi4gSXQgaXMgYmFzZWQgb24gYSBmaWx0ZXJlZCBzeW50aGVzaXMgb2YgdGhyZWUgcHVibGljbHkgYXZhaWxhYmxlIGRhdGFzZXRzOiAoMSkgdGhlIE5PUk1BTiBFTVBPREFUIGRhdGFiYXNlIGZvciBjaGVtaWNhbCBvY2N1cnJlbmNlIChhY2Nlc3NlZCAxOC8wMy8yMDI1KSwgKDIpIHRoZSBVbXdlbHRidW5kZXNhbXQgUGhhcm1hY2V1dGljYWxzIGluIHRoZSBFbnZpcm9ubWVudCBkYXRhYmFzZSAoUEhBUk1TLVVCQTsgYWNjZXNzZWQgMTkvMTIvMjAyNCksIGFuZCAoMykgV2lsa2luc29uIGV0IGFsLiAoMjAyMikgUGhhcm1hY2V1dGljYWwgUG9sbHV0aW9uIG9mIHRoZSBXb3JsZOKAmXMgUml2ZXJzIGRhdGFiYXNlLiBEYXRhIHdlcmUgcmVzdHJpY3RlZCB0byBlbnRyaWVzIHJlcG9ydGVkIGluIG1hc3MgcGVyIHZvbHVtZSBvZiB3YXRlciAoZS5nLiwgwrVnL0wpIGFuZCByZWxldmFudCB0byBzdXJmYWNlIHdhdGVyIGFuZCB3YXN0ZXdhdGVyIG1hdHJpY2VzIChmb3IgZGV0YWlscyBvZiB0aGUgZmlsdGVyaW5nIHByb2Nlc3MsIHBsZWFzZSByZWZlciB0byBNYXJ0aW4gZXQgYWwuICgyMDI1KSkuIFVzZXJzIG9mIHRoaXMgdGFibGUgc2hvdWxkICoqY2l0ZSB0aGUgb3JpZ2luYWwgc291cmNlcyBvZiB0aGUgZGF0YTogTk9STUFOIEVNUE9EQVQsIFBIQVJNUy1VQkEsIGFuZCBXaWxraW5zb24gZXQgYWwuICgyMDIyKS4qKiIpDQogICkgJT4lDQogIHRhYl9zb3VyY2Vfbm90ZSgNCiAgICBtZCgiKOKAoCkgU3VtbWFyeSBzdGF0aXN0aWNzIHJlZmxlY3Qgb25seSBwb3NpdGl2ZSBkZXRlY3Rpb25zLiAo4oChKSBDcmVkaWJsZSBpbnRlcnZhbHMgcmVwcmVzZW50IHRoZSBsb3dlciBhbmQgdXBwZXIgOTUlIGVtcGlyaWNhbCBxdWFudGlsZXMuIikNCiAgKQ0KYGBgDQoNClNhdmluZyB0aGUgdGFibGUgYXMgYSBIVExNIGZpbGUNCg0KYGBge3J9DQpndHNhdmUodGFibGVfYWxsLCBmaWxlbmFtZSA9IHBhc3RlMChmaWdfd2QsICIuL21hcnRpbi1ldC1hbC1zdXBwbGVtcmF0cnktZmlsZS0xLWNvbmNlbnRyYXRpb24tc3VtbWFyeS10YWJsZS5odG1sIikpDQpgYGANCg0KDQpIZXJlIHdlIHdpbGwgYWRkIHRoZSBzdW1tYXJ5IHN0YXRpc3RpY3MgZnJvbSBhYm92ZSB0byB0aGUgZnVsbCBkYXRhYmFzZSwgc28gd2UgY2FuIHNlZSBob3cgbWFueSBFSVBBQUIgdGVzdGVkIGNvbmNlbnRyYXRpb25zIGZhbGwgdW5kZXIgdGhlIG1lZGlhbiBzdXJmYWNlIHdhdGVyIGFuZCB3YXN0ZXdhdGVyIGNvbmNlbnRyYXRpb25zIGZvciBlYWNoIGNvbXBvdW5kLiAgIA0KDQpgYGB7cn0NCnJlbmFtZV9jb2xzIDwtIGNvbmNfc3VtbWFyeSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtbWF0cml4X2dyb3VwLC1jb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkgJT4lDQogIGNvbG5hbWVzKCkNCg0KY29uY19zdW1tYXJ5X2VmZiA8LSBjb25jX3N1bW1hcnkgJT4lDQogIGRwbHlyOjpmaWx0ZXIobWF0cml4X2dyb3VwID09ICJlZmZsdWVudCIpICU+JQ0KICBkcGx5cjo6cmVuYW1lX3dpdGgoLmNvbHMgPSBhbGxfb2YocmVuYW1lX2NvbHMpLA0KICAgICAgICAgICAgICAgICAgICAgLmZuID0gfiBwYXN0ZTAoImVmZmx1ZW50XyIsIC4pKSAlPiUNCiAgZHBseXI6OnNlbGVjdCgtbWF0cml4X2dyb3VwKQ0KDQoNCmNvbmNfc3VtbWFyeV9zdXJmIDwtIGNvbmNfc3VtbWFyeSAlPiUNCiAgZHBseXI6OmZpbHRlcihtYXRyaXhfZ3JvdXAgPT0gInN1cmZhY2V3YXRlciIpICU+JQ0KICBkcGx5cjo6cmVuYW1lX3dpdGgoLmNvbHMgPSBhbGxfb2YocmVuYW1lX2NvbHMpLA0KICAgICAgICAgICAgICAgICAgICAgLmZuID0gfiBwYXN0ZTAoInN1cmZhY2V3YXRlcl8iLCAuKSkgJT4lDQogIGRwbHlyOjpzZWxlY3QoLW1hdHJpeF9ncm91cCkNCg0KDQphbGxfZGF0YWJhc2VzX3dpdGhfc3VtIDwtIGFsbF9kYXRhYmFzZXMgJT4lDQogIGRwbHlyOjpmaWx0ZXIoc291cmNlID09ICJlaXBhYWIiKSAlPiUNCiAgZHBseXI6OmxlZnRfam9pbiguLCBjb25jX3N1bW1hcnlfZWZmLCBieSA9ICJjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCIpICU+JQ0KICBkcGx5cjo6bGVmdF9qb2luKC4sIGNvbmNfc3VtbWFyeV9zdXJmLCBieSA9ICJjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCIpICU+JQ0KICBkcGx5cjo6bXV0YXRlKA0KICAgIHVuZGVyX21lZF9zdXJmID0gaWZfZWxzZSh2YWx1ZSA8IHN1cmZhY2V3YXRlcl9tZWRpYW4sIDEsIDApLA0KICAgIHVuZGVyX2hjaV9zdXJmID0gaWZfZWxzZSh2YWx1ZSA8IHN1cmZhY2V3YXRlcl9jaV91cHBlciwgMSwgMCksDQogICAgdW5kZXJfbWVkX2VmZmwgPSBpZl9lbHNlKHZhbHVlIDwgZWZmbHVlbnRfbWVkaWFuLCAxLCAwKSwNCiAgICB1bmRlcl9oY2lfZWZmbCA9IGlmX2Vsc2UodmFsdWUgPCBlZmZsdWVudF9jaV91cHBlciwgMSwgMCksDQogICAgZGlmZl9tZWRfc3VyZiA9IHZhbHVlIC8gc3VyZmFjZXdhdGVyX21lZGlhbiwNCiAgICBkaWZmX2hpY19zdXJmID0gdmFsdWUgLyBzdXJmYWNld2F0ZXJfY2lfdXBwZXIsDQogICAgZGlmZl9tZWRfZWZmbCA9IHZhbHVlIC8gZWZmbHVlbnRfbWVkaWFuLA0KICAgIGRpZmZfaGljX2VmZmwgPSB2YWx1ZSAvIGVmZmx1ZW50X2NpX3VwcGVyDQogICkNCmBgYA0KDQpNYWtpbmcgYSBkYXRhYmFzZSBmb3Igc3VyZmFjZSB3YXRlcnMgYW5kIHdhc3RlIHdhdGVycyAgIA0KDQpgYGB7cn0NCnN1cmZhY2V3YXRlcl9jb25jIDwtIGFsbF9kYXRhYmFzZXNfd2l0aF9zdW0gJT4lDQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHN1cmZhY2V3YXRlcl9tZWRpYW4pLCFpcy5uYSh2YWx1ZSkpICU+JQ0KICBkcGx5cjo6bXV0YXRlKA0KICAgIHZhbHVlX2xvZyA9IGxvZyh2YWx1ZSksDQogICAgc3VyZmFjZXdhdGVyX21lZGlhbl9sb2cgPSBsb2coc3VyZmFjZXdhdGVyX21lZGlhbiksDQogICAgcmVsYXRpdmVfeWVhciA9IHllYXIgLSBtaW4oeWVhcikNCiAgKQ0KDQp3YXN0ZXdhdGVyX2NvbmMgPC0gYWxsX2RhdGFiYXNlc193aXRoX3N1bSAlPiUNCiAgZHBseXI6OmZpbHRlcighaXMubmEoZWZmbHVlbnRfbWVkaWFuKSwhaXMubmEodmFsdWUpKSAlPiUNCiAgZHBseXI6Om11dGF0ZSgNCiAgICB2YWx1ZV9sb2cgPSBsb2codmFsdWUpLA0KICAgIGVmZmx1ZW50X21lZGlhbl9sb2cgPSBsb2coZWZmbHVlbnRfbWVkaWFuKSwNCiAgICByZWxhdGl2ZV95ZWFyID0geWVhciAtIG1pbih5ZWFyKQ0KICApDQpgYGANCg0KVGhlIHJlbGF0aXZlX3llYXIgaXMgYmFzZWQgb24gdGhlIGVhcmxpZXN0IHllYXIsIDE5OTIuDQoNCmBgYHtyfQ0KYWxsX2RhdGFiYXNlc193aXRoX3N1bSAlPiUNCiAgZHBseXI6OmZpbHRlcighaXMubmEoc3VyZmFjZXdhdGVyX21lZGlhbiksIWlzLm5hKHZhbHVlKSkgJT4lDQogIGRwbHlyOjpzdW1tYXJpc2UobWluX3llYXIgPSBtaW4oeWVhciwgbmEucm0gPSBUUlVFKSkgJT4lDQogIGRwbHlyOjpwdWxsKG1pbl95ZWFyKQ0KYGBgDQoNCg0KIyMjIE1vZGVsaW5nDQoNCiMjIyMg8J+MiiBTdXJmYWNlIHdhdGVyDQoNCiMjIyMjIE1vZGVsIHN0cnVjdHVyZQ0KDQpJbiB0aGVzZSBtb2RlbHMsIHdlIGV4YW1pbmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBsb2ctdHJhbnNmb3JtZWQgbWluaW11bSB0ZXN0ZWQgY29uY2VudHJhdGlvbiAodmFsdWVfbG9nKSBhbmQgdGhlIGxvZy10cmFuc2Zvcm1lZCBtZWRpYW4gc3VyZmFjZSB3YXRlciBjb25jZW50cmF0aW9uIChzdXJmYWNld2F0ZXJfbWVkaWFuX2xvZykuYW5hbHlzaXMNCg0KYGBge3J9DQpuID0gbnJvdyhzdXJmYWNld2F0ZXJfY29uYykNCnByaW50KGdsdWUoIldlIGhhdmUgYSBzYW1wbGUgc2l6ZSBvZiB7bn0gZm9yIHRoaXMgYW5hbHlzaXMsIGFzIGl0cyBvbiB0aGUgbGV2ZWwgb2YgZXhwb3N1cmUgYnV0IG9ubHkgZm9yIGNvbXBvdW5kcyB3aXRoIGNvcnJlc3BvbmQgc3VyZmFjZXdhdGVyIGRldGVjdGlvbnMiKSkNCmBgYA0KDQpUbyBhY2NvdW50IGZvciB0ZW1wb3JhbCB0cmVuZHMgYW5kIHN0dWR5IGRlc2lnbiBkaWZmZXJlbmNlcywgd2UgaW5jbHVkZSB0d28gYWRkaXRpb25hbCBjb3ZhcmlhdGVzOg0KDQpyZWxhdGl2ZV95ZWFyLCBkZWZpbmVkIGFzIHRoZSBudW1iZXIgb2YgeWVhcnMgc2luY2UgdGhlIGZpcnN0IHB1YmxpY2F0aW9uIGluIDE5OTIsIGFuZCBkb3NlcywgdGhlIG51bWJlciBvZiBjb25jZW50cmF0aW9uIGxldmVscyB0ZXN0ZWQgaW4gZWFjaCBzdHVkeS4NCg0KV2UgZml0IGEgZnVsbCBtb2RlbCB0aGF0IGluY2x1ZGVzIGFsbCB0aHJlZSBwcmVkaWN0b3JzLCBhbmQgYSBudWxsIG1vZGVsIHRoYXQgZXhjbHVkZXMgd2FzdGV3YXRlcl9tZWRpYW5fbG9nLCBhbGxvd2luZyB1cyB0byBhc3Nlc3MgdGhlIGFkZGVkIHByZWRpY3RpdmUgdmFsdWUgb2Ygc3VyZmFjZSB3YXRlciBjb25jZW50cmF0aW9ucyB0aHJvdWdoIG1vZGVsIGNvbXBhcmlzb24uDQoNCmBgYHtyfQ0KY29uY2VudHJhdGlvbl9zdHIgPC0gYmYodmFsdWVfbG9nIH4gc3VyZmFjZXdhdGVyX21lZGlhbl9sb2cgKyByZWxhdGl2ZV95ZWFyICsgZG9zZXMsDQogICAgICAgICAgICAgICAgICBmYW1pbHkgPSBnYXVzc2lhbigpKQ0KDQpjb25jZW50cmF0aW9uX3N0cl9udWxsIDwtIGJmKHZhbHVlX2xvZyB+IHJlbGF0aXZlX3llYXIgKyBkb3NlcywNCiAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGdhdXNzaWFuKCkpDQpgYGANCg0KVGhlc2UgYXJlIHRoZSBkZWZhdWx0IHByaW9ycy4gIA0KDQpgYGB7cn0NCnN1cHByZXNzV2FybmluZ3MoZ2V0X3ByaW9yKGNvbmNlbnRyYXRpb25fc3RyLCBkYXRhID0gc3VyZmFjZXdhdGVyX2NvbmMsICBmYW1pbHkgPSBnYXVzc2lhbigpKSkgJT4lIA0KICBmbGV4dGFibGUoKSAlPiUgDQogIGF1dG9maXQoKQ0KYGBgDQoNCiMjIyMjIFJ1biBtb2RlbA0KUnVuIG1vZGVsLiBUaGlzIGhhcyBiZWVuIGhhc2hlZCBvdXQgYXMgSSBoYXZlIHNhdmVkIHRoZSBtb2RlbCwgYW5kIHdpbGwgcmUtbG9hZCBpdCBpbnN0ZWFkIG9mIHJlLXJ1bm5pbmcgaXQuIA0KDQpgYGB7cn0NCm9wdGlvbnMoYnJtcy5iYWNrZW5kID0gImNtZHN0YW5yIikNCmNvbmNlbnRyYXRpb25fbW9kX2xvZyA8LSBicm0oDQogIGNvbmNlbnRyYXRpb25fc3RyLA0KICBkYXRhID0gc3VyZmFjZXdhdGVyX2NvbmMsDQogIGNvcmVzID0gNCwNCiAgY2hhaW5zID0gNCwNCiAgI3ByaW9yID0gbW9kX3ByaW9ycywgI3VzZSBkZWZhdWx0cw0KICB3YXJtdXAgPSAxMDAwLA0KICBzZWVkID0gMjAyNTA0MTgsDQogIHRoaW4gPSAyLA0KICBpdGVyID0gODAwMCwNCiAgY29udHJvbCA9IGxpc3QobWF4X3RyZWVkZXB0aCA9IDIwLCBhZGFwdF9kZWx0YSA9IDAuOTUpLA0KICBzYXZlX3BhcnMgPSBzYXZlX3BhcnMoYWxsID0gVFJVRSksDQogIHNhbXBsZV9wcmlvciA9IFRSVUUsDQogIGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgJy4vY29uY2VudHJhdGlvbl9tb2RfbG9nJykNCikNCg0KY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2cgPC0gYnJtKA0KICBjb25jZW50cmF0aW9uX3N0cl9udWxsLA0KICBkYXRhID0gc3VyZmFjZXdhdGVyX2NvbmMsDQogIGNvcmVzID0gNCwNCiAgY2hhaW5zID0gNCwNCiAgI3ByaW9yID0gbW9kX3ByaW9ycywgI3VzZSBkZWZhdWx0cw0KICB3YXJtdXAgPSAxMDAwLA0KICBzZWVkID0gMjAyNTA0MTgsDQogIHRoaW4gPSAyLA0KICBpdGVyID0gODAwMCwNCiAgY29udHJvbCA9IGxpc3QobWF4X3RyZWVkZXB0aCA9IDIwLCBhZGFwdF9kZWx0YSA9IDAuOTUpLA0KICBzYXZlX3BhcnMgPSBzYXZlX3BhcnMoYWxsID0gVFJVRSksDQogIHNhbXBsZV9wcmlvciA9IFRSVUUsDQogIGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgJy4vY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2cnKQ0KKQ0KYGBgDQoNCiMjIyMjIFJlLWxvYWQgbW9kZWxzDQoNClJlbG9hZCB0aGUgbW9kZWwgaWYgbmVjZXNzYXJ5LiAgDQoNCmBgYHtyfQ0KY29uY2VudHJhdGlvbl9tb2RfbG9nIDwtICByZWFkUkRTKGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgIi4vY29uY2VudHJhdGlvbl9tb2RfbG9nLnJkcyIpKQ0KY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2cgPC0gIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChtb2RzX3dkLCAiLi9jb25jZW50cmF0aW9uX21vZF9udWxsX2xvZy5yZHMiKSkNCmBgYA0KDQoNCiMjIyMjIE1vZGVsIGRpYWdub3N0aWNzDQoNClRoZXJlJ3Mgbm8gY2xlYXIgZXZpZGVuY2UgdGhhdCB0aGUgY29uY2VudHJhdGlvbiBtb2RlbCBpcyBiZXR0ZXIgb3Igd29yc2UgdGhhbiB0aGUgbnVsbCBtb2RlbCBmb3Igb3V0LW9mLXNhbXBsZSBwcmVkaWN0aW9uLiBCYXNlZCBvbiB0aGUgZXhwZWN0ZWQgbG9nIHByZWRpY3RpdmUgZGVuc2l0eSAoRUxQRCkgYmV0d2VlbiB0d28gbW9kZWxzIHVzaW5nIGFwcHJveGltYXRlIGxlYXZlLW9uZS1vdXQgY3Jvc3MtdmFsaWRhdGlvbiAoTE9PLUNWKS4gDQoNCmBgYHtyfQ0KbG9vX251bGwgPC0gbG9vKGNvbmNlbnRyYXRpb25fbW9kX251bGxfbG9nLCByZWxvbyA9IFRSVUUpDQpsb29fbW9kIDwtIGxvbyhjb25jZW50cmF0aW9uX21vZF9sb2csIHJlbG9vID0gVFJVRSkNCmxvb19jb21wYXJlKGxvb19tb2QsIGxvb19udWxsKQ0KYGBgDQoNCkEgQmF5ZXMgZmFjdG9yIChCRikgcXVhbnRpZmllcyBob3cgbXVjaCBtb3JlIGxpa2VseSB0aGUgZGF0YSBhcmUgdW5kZXIgb25lIG1vZGVsIGNvbXBhcmVkIHRvIGFub3RoZXIuIFdoZXJlLCBCRiA+IDEgc3VwcG9ydCBmb3IgdGhlIGZpcnN0IG1vZGVsIChjb25jZW50cmF0aW9uX21vZF9sb2cpLCBCRiA8IDEgc3VwcG9ydCBmb3IgdGhlIHNlY29uZCBtb2RlbCAoY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2cpLiANCg0KSW4gdGhpcyBjYXNlOiBCRiA9IDAuNTM2IHNvIHRoZSBudWxsIG1vZGVsIGlzIGFib3V0IDEuODfDlyBtb3JlIHN1cHBvcnRlZCBieSB0aGUgZGF0YSAoMS8wLjUzNiA9IDEuODcpIA0KDQoNCmBgYHtyfQ0KYmF5ZXNfZmFjdG9yKGNvbmNlbnRyYXRpb25fbW9kX2xvZywgY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2cpDQpgYGANCg0KVGhpcyBpcyBhIHZlcnkgbG93IFLCsiwgc3VnZ2VzdGluZzpUaGUgcHJlZGljdG9yKHMpIGluIHRoZSBtb2RlbCBhY2NvdW50IGZvciB2ZXJ5IGxpdHRsZSBvZiB0aGUgb3V0Y29tZSB2YXJpYWJpbGl0eS4gIA0KDQp8IE1ldHJpYyAgICAgICAgIHwgVmFsdWUgICAgICAgICAgIHwNCnwgLS0tLS0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLS0tLS0gfA0KfCBDb25kaXRpb25hbCBSwrIgfCAqMC4wMjIqICAgICAgICAgfA0KfCA5NSUgQ0kgICAgICAgICB8IFswLjAwNCwgMC4wNDJdICB8DQoNCg0KYGBge3J9DQpwZXJmb3JtYW5jZTo6cjJfYmF5ZXMoY29uY2VudHJhdGlvbl9tb2RfbG9nLCByb2J1c3QgPSBGQUxTRSwgY2kgPSAwLjk1KQ0KYGBgDQoNClRoZSBtb2RlbCBmaXRzIHRoZSBtYXJnaW5hbCBkaXN0cmlidXRpb24gb2YgdGhlIHJlc3BvbnNlIHZhcmlhYmxlIHdlbGwuDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQ0KY29sb3Jfc2NoZW1lX3NldCgicmVkIikNCmJybXM6OnBwX2NoZWNrKGNvbmNlbnRyYXRpb25fbW9kX2xvZywgbmRyYXdzID0gMjUsIHR5cGUgPSAiZGVuc19vdmVybGF5IikNCmJybXM6OnBwX2NoZWNrKGNvbmNlbnRyYXRpb25fbW9kX2xvZywgbmRyYXdzID0gMjUsIHR5cGUgPSAiZWNkZl9vdmVybGF5IikgIA0KeV9yZXAgPC0gcG9zdGVyaW9yX3ByZWRpY3QoY29uY2VudHJhdGlvbl9tb2RfbG9nLCBuZHJhd3MgPSAxMDAwKQ0KeV9vYnMgPC0gY29uY2VudHJhdGlvbl9tb2RfbG9nJGRhdGEkdmFsdWVfbG9nICAjIFJlcGxhY2Ugd2l0aCB5b3VyIGFjdHVhbCByZXNwb25zZSB2YXJpYWJsZQ0KcHBjX3N0YXRfMmQoeSA9IHlfb2JzLCB5cmVwID0geV9yZXAsIHN0YXQgPSBjKCJtZWRpYW4iLCAibWFkIikpDQpgYGANCg0KYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9DQpwZXJmb3JtYW5jZTo6Y2hlY2tfbW9kZWwoY29uY2VudHJhdGlvbl9tb2RfbG9nKQ0KYGBgDQoNCkNoZWNraW5nIFItaGF0IHZhbHVlcyB0byBjb25maXJtIG1vZGVsIGNvbnZlcmdlbmNlIGFuZCBFU1MgZm8gZXN0aW1hdGVzLiAgIA0KDQpgYGB7cn0NCm1heF9yaGF0IDwtIG1heChyaGF0KGNvbmNlbnRyYXRpb25fbW9kX2xvZyksIG5hLnJtID0gVFJVRSkNCnByaW50KGdsdWU6OmdsdWUoIkFsbCBSLWhhdCB2YWx1ZXMg4omkIHttYXhfcmhhdH0gKGdvb2QgY29udmVyZ2VuY2UpLiIpKQ0KYGBgDQoNCmBgYHtyfQ0KcHJpbnQoc3VtbWFyeShjb25jZW50cmF0aW9uX21vZF9sb2csIHByb2IgPSAwLjk1KSkgI0FsbCBSaGF0ID0gMSAoZ29vZCkuICANCmBgYA0KDQpEaWFnbm9zdGljIHBsb3RzIGxvb2sgZmluZS4gQSBzbWFsbGVyIHNoYXBlIHZhbHVlIGluZGljYXRlcyBncmVhdGVyIG92ZXJkaXNwZXJzaW9uICh2YXJpYW5jZSBpcyBtdWNoIGxhcmdlciB0aGFuIHRoZSBtZWFuKS4gIA0KDQpgYGB7ciwgZmlnLmhlaWdodD03LCBmaWcud2lkdGg9N30NCmNvbG9yX3NjaGVtZV9zZXQoInJlZCIpICANCnBsb3QoY29uY2VudHJhdGlvbl9tb2RfbG9nKSAgDQpgYGANCg0KDQoNCiMjIyMjIE1vZGVsIGVzdGltYXRlcw0KDQpQdWxsaW5nIHRoZSBtYXJnaW5hbCBlZmZlY3QgZXN0aW1hdGVzIGZyb20gdGhlIG1vZGVsIGZvciBvdXIgcGxvdHMuICANCg0KYGBge3J9DQplc3RpbWF0ZV9kYXRhXzEgPC0gbWFyZ2luYWxfZWZmZWN0cyhjb25jZW50cmF0aW9uX21vZF9sb2csIGVmZmVjdHMgPSAic3VyZmFjZXdhdGVyX21lZGlhbl9sb2ciKVtbMV1dDQplc3RpbWF0ZV9kYXRhX3llYXJfMSA8LSBtYXJnaW5hbF9lZmZlY3RzKGNvbmNlbnRyYXRpb25fbW9kX2xvZywgZWZmZWN0cyA9ICJyZWxhdGl2ZV95ZWFyIilbWzFdXQ0KYGBgDQoNCg0KVGhlc2UgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyByZXByZXNlbnQgdGhlIGF2ZXJhZ2UgZWZmZWN0cyBvZiBlYWNoIHByZWRpY3RvciBvbiB0aGUgbG9nLXRyYW5zZm9ybWVkIHRlc3RlZCBjb25jZW50cmF0aW9uLCBjb250cm9sbGluZyBmb3IgKG5vdCBhdmVyYWdpbmcgb3ZlcikgdGhlIG90aGVyIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwuIEJlY2F1c2UgdGhpcyBpcyBhIGxvZ+KAk2xvZyBtb2RlbCwgdGhlIGNvZWZmaWNpZW50IGZvciBgbG9nKHN1cmZhY2V3YXRlcl9tZWRpYW4pYCBjYW4gYmUgaW50ZXJwcmV0ZWQgYXMgYW4gZWxhc3RpY2l0eeKAlHRoYXQgaXMsIHRoZSBwZXJjZW50IGNoYW5nZSBpbiB0aGUgcmVzcG9uc2UgYXNzb2NpYXRlZCB3aXRoIGEgcGVyY2VudCBjaGFuZ2UgaW4gdGhlIHByZWRpY3Rvci4NCg0KVGhlIGVzdGltYXRlZCBlZmZlY3Qgb2YgYGxvZyhzdXJmYWNld2F0ZXJfbWVkaWFuKWAgd2FzICoqzrIgPSAwLjEyOCoqICg5NSUgQ3JJOiDigJMwLjEzMyB0byAwLjM4OSksIHN1Z2dlc3RpbmcgdGhhdCBhICoqMSUgaW5jcmVhc2UgaW4gc3VyZmFjZSB3YXRlciBjb25jZW50cmF0aW9uKiogaXMgYXNzb2NpYXRlZCB3aXRoIGFuIGF2ZXJhZ2UgKiowLjEyOCUgaW5jcmVhc2UgaW4gdGVzdGVkIGNvbmNlbnRyYXRpb24qKi4gSG93ZXZlciwgdGhlIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCBpbmNsdWRlcyB6ZXJvLCBpbmRpY2F0aW5nIHN1YnN0YW50aWFsIHVuY2VydGFpbnR5IGFuZCB0aGF0IHRoZSBlZmZlY3QgaXMgKipub3Qgd2VsbC1zdXBwb3J0ZWQqKiBieSB0aGUgZGF0YS4NCg0KVGhlIGVzdGltYXRlZCBlZmZlY3Qgb2YgcmVsYXRpdmVfeWVhciB3YXMgKirOsiA9IOKAkzAuMTA2KiogKDk1JSBDckk6IOKAkzAuMTcwIHRvIOKAkzAuMDQyKSwgcHJvdmlkaW5nIHN0cm9uZyBldmlkZW5jZSBvZiBhIHRlbXBvcmFsIGRlY2xpbmUgaW4gdGVzdGVkIGNvbmNlbnRyYXRpb25zLiBTaW5jZSB0aGUgcHJlZGljdG9yIGlzIGxpbmVhciBhbmQgdGhlIG91dGNvbWUgaXMgbG9nLXRyYW5zZm9ybWVkLCB0aGlzIGNvcnJlc3BvbmRzIHRvIGFuIGF2ZXJhZ2UgKioxMCUgZGVjcmVhc2UgaW4gdGVzdGVkIGNvbmNlbnRyYXRpb24gcGVyIHllYXIqKiAod2hlcmUgdGhlIGV4cCjigJMwLjEwNikg4omIIDAuODk5OyBhbmQgKDAuODk54oiSMSkgw5cgMTAwID0g4oiSMTAuMSUpLCBob2xkaW5nIGFsbCBlbHNlIGNvbnN0YW50Lg0KDQpUaGUgZXN0aW1hdGVkIGNvZWZmaWNpZW50IGZvciBkb3NlcyB3YXMgKirOsiA9IDAuMDY4KiogKDk1JSBDckk6IOKAkzAuMDk0IHRvIDAuMjM0KSwgc3VnZ2VzdGluZyBhIHdlYWsgcG9zaXRpdmUgYXNzb2NpYXRpb24sIGJ1dCBhZ2FpbiB3aXRoIGNvbnNpZGVyYWJsZSB1bmNlcnRhaW50eeKAlCoqdGhlIGVmZmVjdCBpcyBub3QgY3JlZGlibHkgZGlmZmVyZW50IGZyb20gemVybyoqLg0KDQoNCg0KYGBge3J9DQptb2RlbF9zdW1tYXJ5XzEgPC0gIGFzLmRhdGEuZnJhbWUoZml4ZWYoY29uY2VudHJhdGlvbl9tb2RfbG9nLCBzdW1tYXJ5ID0gVFJVRSkpICU+JSANCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lIA0KICBjbGVhbl9uYW1lcygpDQoNCmVzdGltYXRlXzEgPC0gbW9kZWxfc3VtbWFyeV8xJGVzdGltYXRlWzJdDQpMOTVDSV8xIDwtIG1vZGVsX3N1bW1hcnlfMSRxMl81WzJdDQpIOTVDSV8xIDwtIG1vZGVsX3N1bW1hcnlfMSRxOTdfNVsyXQ0KDQplc3RpbWF0ZV95ZWFyXzEgPC0gbW9kZWxfc3VtbWFyeV8xJGVzdGltYXRlWzNdDQpMOTVDSV95ZWFyXzEgPC0gbW9kZWxfc3VtbWFyeV8xJHEyXzVbM10NCkg5NUNJX3llYXJfMSA8LSBtb2RlbF9zdW1tYXJ5XzEkcTk3XzVbM10NCg0KbW9kZWxfc3VtbWFyeV8xX2Z0IDwtIG1vZGVsX3N1bW1hcnlfMSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIH4gcm91bmQoLngsIDMpKSkgJT4lIA0KICBkcGx5cjo6cmVuYW1lKCJwcmVkaWN0b3IiID0gcm93bmFtZSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKENySSA9IHBhc3RlMChxMl81LCAiIHRvICIsIHE5N181KSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXN0aW1hdGUsIENySSkgJT4lIA0KICBmbGV4dGFibGUoKSAlPiUNCiAgc2V0X2hlYWRlcl9sYWJlbHMoDQogICAgcHJlZGljdG9yID0gIlByZWRpY3RvciIsDQogICAgZXN0aW1hdGUgPSAiRXN0aW1hdGUiLA0KICAgIENySSA9ICI5NSUgQ3JJIg0KICApICU+JQ0KICBhdXRvZml0KCkNCg0KbW9kZWxfc3VtbWFyeV8xX2Z0DQpgYGANCg0KDQpTYXZpbmcgdGhlIHRhYmxlIA0KDQpgYGB7cn0NCnNhdmVfYXNfZG9jeChtb2RlbF9zdW1tYXJ5XzFfZnQsIHBhdGggPSBwYXN0ZTAoZmlnX3dkLCAiLi90YWJsZS1zMi5kb2N4IikpDQpgYGANCg0KVGhpcyBnaXZlcyB5b3UgdGhlIG1vZGVsLWRlcml2ZWQgZXhwZWN0ZWQgbnVtYmVyIG9mIHllYXJzIChmcm9tIHRoZSAxOTkyIGJhc2VsaW5lKSBmb3IgY29udmVyZ2VuY2Ugb2YgdGVzdGVkIGFuZCBkZXRlY3RlZCBjb25jZW50cmF0aW9ucywgZm9yIGFuIGF2ZXJhZ2UgY29tcG91bmQgd2l0aCBhdmVyYWdlIGRvc2UgYW5kIGF2ZXJhZ2Ugd2F0ZXIgY29uY2VudHJhdGlvbi4gDQoNCmBgYHtyfQ0KYjAgPC0gbW9kZWxfc3VtbWFyeV8xJGVzdGltYXRlWzFdICAjIEludGVyY2VwdA0KYjEgPC0gbW9kZWxfc3VtbWFyeV8xJGVzdGltYXRlWzJdICAjIHN1cmZhY2V3YXRlcl9tZWRpYW5fbG9nDQpiMiA8LSBtb2RlbF9zdW1tYXJ5XzEkZXN0aW1hdGVbM10gICMgcmVsYXRpdmVfeWVhcg0KYjMgPC0gbW9kZWxfc3VtbWFyeV8xJGVzdGltYXRlWzRdICAjIGRvc2VzDQoNCnhfYmFyIDwtIG1lYW4oc3VyZmFjZXdhdGVyX2NvbmMkc3VyZmFjZXdhdGVyX21lZGlhbl9sb2csIG5hLnJtID0gVFJVRSkNCmRfYmFyIDwtIG1lYW4oc3VyZmFjZXdhdGVyX2NvbmMkZG9zZXMsIG5hLnJtID0gVFJVRSkNCg0KYXZlcmFnZV9lcXVhbF95ZWFycyA8LSByb3VuZCgoKC1iMCArICgxIC0gYjEpICogeF9iYXIgLSBiMyAqIGRfYmFyKSAvIGIyKSwwKQ0KeWVhcnNfdG9fZXF1YWwgPC0gKDE5OTIgKyBhdmVyYWdlX2VxdWFsX3llYXJzKQ0KDQpnbHVlOjpnbHVlKCJCYXNlZCBvbiBtb2RlbCBwcmVkaWN0aW9ucyBmb3IgYXZlcmFnZSBjb21wb3VuZCwgd2l0aCB0aGUgYXZlcmFnZSBudW1iZXIgb2YgZG9zZXMsIGFuZCBhdmVyYWdlIHdhdGVyIGNvbmNlbnRyYXRpb24sIGl0IHdvdWxkIHRha2Uge2F2ZXJhZ2VfZXF1YWxfeWVhcnN9IHllYXJzIGZyb20gb3VyIGJhc2VsaW5lIChpLmUuIDE5OTIpLCBmb3IgdGhlIHRlc3RlZCBkb3NlIHRvIGJlIGVxdWFsIHdpdGggdGhlIG1lYXNzdXJlZCBkb3NlLiBJbiBvdGhlciB3b3JkcywgYmFzZWQgb24gY3VycmVudCB0cmVuZHMsIHRlc3RlZCBkb3NlcyB3b250IGJlIGVxdWFsIHRvIGZpZWxkIGxldmVscyB1bnRpbCB7eWVhcnNfdG9fZXF1YWx9IikNCmBgYA0KDQojIyMjIyBGaWcgMWENCg0KYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTZ9DQp0ZXh0X2Nvb3JkIDwtIHN1cmZhY2V3YXRlcl9jb25jICU+JQ0KICBzdW1tYXJpc2UoDQogICAgeV9tYXggPSBtYXgodmFsdWVfbG9nLCBuYS5ybSA9IFRSVUUpLA0KICAgIHhfbWF4ID0gbWF4KHN1cmZhY2V3YXRlcl9tZWRpYW5fbG9nLCBuYS5ybSA9IFRSVUUpLA0KICAgIHhfbWluID0gbWluKHN1cmZhY2V3YXRlcl9tZWRpYW5fbG9nLCBuYS5ybSA9IFRSVUUpLA0KICAgIHggPSBtZWFuKGMoeF9tYXgsIHhfbWluKSkNCiAgKQ0KDQp5X2Nvb3JkIDwtIHRleHRfY29vcmQgJT4lIHB1bGwoeV9tYXgpDQoNCnhfY29vcmQgPC0gdGV4dF9jb29yZCAlPiUgcHVsbCh4X21pbikNCg0KDQpmaWdfMWEgPC0gZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoDQogICAgZGF0YSA9IGVzdGltYXRlX2RhdGFfMSwNCiAgICBhZXMoeCA9IHN1cmZhY2V3YXRlcl9tZWRpYW5fbG9nLCB5ID0gZXN0aW1hdGVfXyksDQogICAgY29sb3IgPSAiIzJENUYzNCIsDQogICAgbGluZXdpZHRoID0gMQ0KICApICsNCiAgZ2VvbV9yaWJib24oDQogICAgZGF0YSA9IGVzdGltYXRlX2RhdGFfMSwNCiAgICBhZXMoeCA9IHN1cmZhY2V3YXRlcl9tZWRpYW5fbG9nLCB5bWluID0gbG93ZXJfXywgeW1heCA9IHVwcGVyX18pLA0KICAgIGZpbGwgPSAiIzJENUYzNCIsDQogICAgYWxwaGEgPSAwLjENCiAgKSArDQogIGdlb21fcG9pbnQoDQogICAgZGF0YSA9IHN1cmZhY2V3YXRlcl9jb25jLA0KICAgIGFlcyh4ID0gc3VyZmFjZXdhdGVyX21lZGlhbl9sb2csIHkgPSB2YWx1ZV9sb2cpLA0KICAgIGFscGhhID0gMC40LA0KICAgIHNoYXBlID0gMjEsDQogICAgY29sb3IgPSAiIzJENUYzNCIsDQogICAgZmlsbCA9ICIjMkQ1RjM0Ig0KICApICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSB4X2Nvb3JkLA0KICAgIHkgPSB5X2Nvb3JkLA0KICAgIGxhYmVsID0gcGFzdGUwKA0KICAgICAgIkJheWVzaWFuIGxvZy1sb2cgR2F1c3NpYW4gcmVncmVzc2lvbiIsDQogICAgICAiXG4iLA0KICAgICAgIkVmZmVjdCBlc3RpbWF0ZTogIiwNCiAgICAgIHJvdW5kKGVzdGltYXRlXzEsIDMpLA0KICAgICAgIlxuIiwNCiAgICAgICI5NSVDckk6ICIsDQogICAgICByb3VuZChMOTVDSV8xLCAzKSwNCiAgICAgICIgdG8gIiwNCiAgICAgIHJvdW5kKEg5NUNJXzEsIDMpDQogICAgKSwNCiAgICBoanVzdCA9IDAsDQogICAgdmp1c3QgPSAxLA0KICAgIHNpemUgPSA0DQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIihBKSAgU3VyZmFjZSB3YXRlciB2cyBleHBvc3VyZSBjb25jZW50cmF0aW9uIiwNCiAgICB4ID0gIkV4cG9zdXJlIGNvbmNlbnRyYXRpb24gKM68Zy9MKSoiLA0KICAgIHkgPSAiRW52aXJvbm1lbnRhbCBjb25jZW50cmF0aW9uICjOvGcvTCkqIiwNCiAgICBjYXB0aW9uID0gIipWYWx1ZXMgaW4gzrxnL0wgc3BhY2Ugb24gYSBsb2cgc2NhbGUiDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkNCiAgICAgIHNjYWxlczo6bGFiZWxfc2NpZW50aWZpYygpKGV4cCh4KSkNCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBsYWJlbHMgPSBmdW5jdGlvbih4KQ0KICAgICAgc2NhbGVzOjpsYWJlbF9zY2llbnRpZmljKCkoZXhwKHgpKQ0KICApICsNCiAgdGhlbWVfZmV3KCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLA0KICAgIHBsb3QuY2FwdGlvbiAgICAgID0gZWxlbWVudF90ZXh0KA0KICAgICAgc2l6ZSAgID0gOCwNCiAgICAgIGhqdXN0ICA9IDAuNSwNCiAgICAgIG1hcmdpbiA9IG1hcmdpbih0ID0gNikNCiAgICApDQogICkNCmZpZ18xYQ0KYGBgDQoNClNhdmUgdGhpcyBmaWd1cmUgZm9yIHRoZSBNUyAgIA0KDQpgYGB7cn0NCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChmaWdfd2QsICIuL2ZpZ3VyZS0xYS5wZGYiKSwNCiAgICAgICBwbG90ID0gZmlnXzFhLA0KICAgICAgIHdpZHRoID0gMjEwLA0KICAgICAgIGhlaWdodCA9IDI5Ny8xLjUsDQogICAgICAgdW5pdHMgPSAibW0iKQ0KYGBgDQoNCg0KIyMjIyMgRmlnIFMxYQ0KDQpQbG90IHN1cmZhY2Ugd2F0ZXIgY29uY2VudHJhdGlvbnMgYnkgeWVhciAgIA0KDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0NCnRleHRfY29vcmQgPC0gd2FzdGV3YXRlcl9jb25jICU+JQ0KICBzdW1tYXJpc2UoDQogICAgeV9tYXggPSBtYXgodmFsdWVfbG9nLCBuYS5ybSA9IFRSVUUpLA0KICAgIHhfbWF4ID0gbWF4KHJlbGF0aXZlX3llYXIsIG5hLnJtID0gVFJVRSksDQogICAgeF9taW4gPSBtaW4ocmVsYXRpdmVfeWVhciwgbmEucm0gPSBUUlVFKSwNCiAgICB4X21lYW4gPSBtZWFuKGMoeF9tYXgsIHhfbWluKSkNCiAgKQ0KDQp5X2Nvb3JkIDwtIHRleHRfY29vcmQgJT4lIHB1bGwoeV9tYXgpDQoNCnhfY29vcmQgPC0gdGV4dF9jb29yZCAlPiUgcHVsbCh4X21pbikNCg0KDQpmaWdfczFhIDwtIGdncGxvdCgpICsNCiAgZ2VvbV9saW5lKA0KICAgIGRhdGEgPSBlc3RpbWF0ZV9kYXRhX3llYXJfMSwNCiAgICBhZXMoeCA9IHJlbGF0aXZlX3llYXIsIHkgPSBlc3RpbWF0ZV9fKSwNCiAgICBjb2xvciA9ICIjMkQ1RjM0IiwNCiAgICBsaW5ld2lkdGggPSAxDQogICkgKw0KICBnZW9tX3JpYmJvbigNCiAgICBkYXRhID0gZXN0aW1hdGVfZGF0YV95ZWFyXzEsDQogICAgYWVzKHggPSByZWxhdGl2ZV95ZWFyLCB5bWluID0gbG93ZXJfXywgeW1heCA9IHVwcGVyX18pLA0KICAgIGZpbGwgPSAiIzJENUYzNCIsDQogICAgYWxwaGEgPSAwLjENCiAgKSArDQogIGdlb21fcG9pbnQoDQogICAgZGF0YSA9IHN1cmZhY2V3YXRlcl9jb25jLA0KICAgIGFlcyh4ID0gcmVsYXRpdmVfeWVhciwgeSA9IHZhbHVlX2xvZyksDQogICAgYWxwaGEgPSAwLjQsDQogICAgc2hhcGUgPSAyMSwNCiAgICBjb2xvciA9ICIjMkQ1RjM0IiwNCiAgICBmaWxsID0gIiMyRDVGMzQiDQogICkgKw0KICBhbm5vdGF0ZSgNCiAgICAidGV4dCIsDQogICAgeCA9IHhfY29vcmQsDQogICAgeSA9IHlfY29vcmQsDQogICAgbGFiZWwgPSBwYXN0ZTAoDQogICAgICAiQmF5ZXNpYW4gbG9nLWxvZyBHYXVzc2lhbiByZWdyZXNzaW9uIiwNCiAgICAgICJcbiIsDQogICAgICAiRWZmZWN0IGVzdGltYXRlOiAiLA0KICAgICAgcm91bmQoZXN0aW1hdGVfeWVhcl8xLCAzKSwNCiAgICAgICJcbiIsDQogICAgICAiOTUlQ3JJOiAiLA0KICAgICAgcm91bmQoTDk1Q0lfeWVhcl8xLCAzKSwNCiAgICAgICIgdG8gIiwNCiAgICAgIHJvdW5kKEg5NUNJX3llYXJfMSwgMykNCiAgICApLA0KICAgIGhqdXN0ID0gMCwNCiAgICB2anVzdCA9IDEsDQogICAgc2l6ZSA9IDQNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiKEEpICBFeHBvc3VyZSBjb25jZW50cmF0aW9uIHZzIHllYXIgKHN1cmZhY2Ugd2F0ZXIgY29tcG91bmRzKSIsDQogICAgeCA9ICJZZWFyIiwNCiAgICB5ID0gIkV4cG9zdXJlIGNvbmNlbnRyYXRpb24gKM68Zy9MKSoiLA0KICAgIGNhcHRpb24gPSAiKkNvbmNlbnRyYXRpb24gdmFsdWVzIGluIM68Zy9MIHNwYWNlIG9uIGEgbG9nIHNjYWxlIg0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIGxhYmVscyA9IGZ1bmN0aW9uKHgpDQogICAgICBzY2FsZXM6OmxhYmVsX3NjaWVudGlmaWMoKShleHAoeCkpDQogICkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkNCiAgICAgIHggKyAxOTkyDQogICkgKw0KdGhlbWVfZmV3KCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLA0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dCgNCiAgICAgIHNpemUgICA9IDgsDQogICAgICBoanVzdCAgPSAwLjUsDQogICAgICBtYXJnaW4gPSBtYXJnaW4odCA9IDYpDQogICAgKQ0KICApDQpmaWdfczFhDQpgYGANCg0KU2F2ZSB0aGlzIGZpZ3VyZSBmb3IgdGhlIE1TICAgDQoNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZpZ193ZCwgIi4vZmlndXJlLXMxYS5wZGYiKSwNCiAgICAgICBwbG90ID0gZmlnX3MxYSwNCiAgICAgICB3aWR0aCA9IDIxMCwNCiAgICAgICBoZWlnaHQgPSAyOTcvMS41LA0KICAgICAgIHVuaXRzID0gIm1tIikNCmBgYA0KDQojIyMjIPCfmr0gV2FzdGUgd2F0ZXINCg0KIyMjIyMgTW9kZWwgc3RydWN0dXJlDQoNCkluIHRoZXNlIG1vZGVscywgd2UgZXhhbWluZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGxvZy10cmFuc2Zvcm1lZCBtaW5pbXVtIHRlc3RlZCBjb25jZW50cmF0aW9uICh2YWx1ZV9sb2cpIGFuZCB0aGUgbG9nLXRyYW5zZm9ybWVkIG1lZGlhbiB3YXN0ZXdhdGVyIGNvbmNlbnRyYXRpb24gKGVmZmx1ZW50X21lZGlhbl9sb2cpLg0KDQpidXQgb25seSBmb3IgY29tcG91bmRzIHdpdGggY29ycmVzcG9uZCB3YXN0ZXdhdGVyIGRldGVjdGlvbnMNCg0KYGBge3J9DQpuID0gbnJvdyh3YXN0ZXdhdGVyX2NvbmMpDQpwcmludChnbHVlKCJXZSBoYXZlIGEgc2FtcGxlIHNpemUgb2Yge259IGZvciB0aGlzIGFuYWx5c2lzLCBhcyBpdHMgb24gdGhlIGxldmVsIG9mIGV4cG9zdXJlLCBidXQgb25seSBmb3IgY29tcG91bmRzIHdpdGggY29ycmVzcG9uZCB3YXN0ZXdhdGVyIGRldGVjdGlvbnMiKSkNCmBgYA0KDQpUbyBhY2NvdW50IGZvciB0ZW1wb3JhbCB0cmVuZHMgYW5kIHN0dWR5IGRlc2lnbiBkaWZmZXJlbmNlcywgd2UgaW5jbHVkZSB0d28gYWRkaXRpb25hbCBjb3ZhcmlhdGVzOg0KDQpyZWxhdGl2ZV95ZWFyLCBkZWZpbmVkIGFzIHRoZSBudW1iZXIgb2YgeWVhcnMgc2luY2UgdGhlIGZpcnN0IHB1YmxpY2F0aW9uIGluIDE5OTIsIGFuZCBkb3NlcywgdGhlIG51bWJlciBvZiBjb25jZW50cmF0aW9uIGxldmVscyB0ZXN0ZWQgaW4gZWFjaCBzdHVkeS4NCg0KV2UgZml0IGEgZnVsbCBtb2RlbCB0aGF0IGluY2x1ZGVzIGFsbCB0aHJlZSBwcmVkaWN0b3JzLCBhbmQgYSBudWxsIG1vZGVsIHRoYXQgZXhjbHVkZXMgZWZmbHVlbnRfbWVkaWFuX2xvZywgYWxsb3dpbmcgdXMgdG8gYXNzZXNzIHRoZSBhZGRlZCBwcmVkaWN0aXZlIHZhbHVlIG9mIHdhc3Rld2F0ZXIgY29uY2VudHJhdGlvbnMgdGhyb3VnaCBtb2RlbCBjb21wYXJpc29uLg0KDQpgYGB7cn0NCmVmZl9jb25jZW50cmF0aW9uX3N0ciA8LSBiZih2YWx1ZV9sb2cgfiBlZmZsdWVudF9tZWRpYW5fbG9nICsgcmVsYXRpdmVfeWVhciArIGRvc2VzLA0KICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSkNCg0KZWZmX2NvbmNlbnRyYXRpb25fc3RyX251bGwgPC0gYmYodmFsdWVfbG9nIH4gcmVsYXRpdmVfeWVhciArIGRvc2VzLA0KICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSkNCmBgYA0KDQpUaGVzZSBhcmUgdGhlIGRlZmF1bHQgcHJpb3JzLiAgDQoNCmBgYHtyfQ0Kc3VwcHJlc3NXYXJuaW5ncyhnZXRfcHJpb3IoZWZmX2NvbmNlbnRyYXRpb25fc3RyLCBkYXRhID0gd2FzdGV3YXRlcl9jb25jLCAgZmFtaWx5ID0gZ2F1c3NpYW4oKSkpICU+JSANCiAgZmxleHRhYmxlKCkNCmBgYA0KIyMjIyMgUnVuIG1vZGVsDQoNClJ1biBtb2RlbC4gVGhpcyBoYXMgYmVlbiBoYXNoZWQgb3V0IGFzIEkgaGF2ZSBzYXZlZCB0aGUgbW9kZWwsIGFuZCB3aWxsIHJlLWxvYWQgaXQgaW5zdGVhZCBvZiByZS1ydW5uaW5nIGl0Lg0KDQpgYGB7cn0NCm9wdGlvbnMoYnJtcy5iYWNrZW5kID0gImNtZHN0YW5yIikNCmVmZl9jb25jZW50cmF0aW9uX21vZF9sb2cgPC0gYnJtKA0KICBlZmZfY29uY2VudHJhdGlvbl9zdHIsDQogIGRhdGEgPSB3YXN0ZXdhdGVyX2NvbmMsDQogIGNvcmVzID0gNCwNCiAgY2hhaW5zID0gNCwNCiAgI3ByaW9yID0gbW9kX3ByaW9ycywgI3VzZSBkZWZhdWx0cw0KICB3YXJtdXAgPSAxMDAwLA0KICBzZWVkID0gMjAyNTA0MTgsDQogIHRoaW4gPSAyLA0KICBpdGVyID0gODAwMCwNCiAgY29udHJvbCA9IGxpc3QobWF4X3RyZWVkZXB0aCA9IDIwLCBhZGFwdF9kZWx0YSA9IDAuOTUpLA0KICBzYXZlX3BhcnMgPSBzYXZlX3BhcnMoYWxsID0gVFJVRSksDQogIHNhbXBsZV9wcmlvciA9IFRSVUUsDQogIGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgJy4vZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZycpDQopDQoNCmVmZl9jb25jZW50cmF0aW9uX21vZF9udWxsX2xvZyA8LSBicm0oDQogIGVmZl9jb25jZW50cmF0aW9uX3N0cl9udWxsLA0KICBkYXRhID0gd2FzdGV3YXRlcl9jb25jLA0KICBjb3JlcyA9IDQsDQogIGNoYWlucyA9IDQsDQogICNwcmlvciA9IG1vZF9wcmlvcnMsICN1c2UgZGVmYXVsdHMNCiAgd2FybXVwID0gMTAwMCwNCiAgc2VlZCA9IDIwMjUwNDE4LA0KICB0aGluID0gMiwNCiAgaXRlciA9IDgwMDAsDQogIGNvbnRyb2wgPSBsaXN0KG1heF90cmVlZGVwdGggPSAyMCwgYWRhcHRfZGVsdGEgPSAwLjk1KSwNCiAgc2F2ZV9wYXJzID0gc2F2ZV9wYXJzKGFsbCA9IFRSVUUpLA0KICBzYW1wbGVfcHJpb3IgPSBUUlVFLA0KICBmaWxlID0gcGFzdGUwKG1vZHNfd2QsICcuL2VmZl9jb25jZW50cmF0aW9uX21vZF9udWxsX2xvZycpDQopDQpgYGANCg0KIyMjIyMgUmUtbG9hZCBtb2RlbHMNCg0KUmVsb2FkIHRoZSBtb2RlbCBpZiBuZWNlc3NhcnkuICANCg0KYGBge3J9DQplZmZfY29uY2VudHJhdGlvbl9tb2RfbG9nIDwtICByZWFkUkRTKGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgIi4vZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZy5yZHMiKSkNCmVmZl9jb25jZW50cmF0aW9uX21vZF9udWxsX2xvZyA8LSAgcmVhZFJEUyhmaWxlID0gcGFzdGUwKG1vZHNfd2QsICIuL2VmZl9jb25jZW50cmF0aW9uX21vZF9udWxsX2xvZy5yZHMiKSkNCmBgYA0KDQoNCiMjIyMjIE1vZGVsIGRpYWdub3N0aWNzDQoNCldlIGNvbXBhcmVkIHRoZSBmdWxsIG1vZGVsIChpbmNsdWRpbmcgZWZmbHVlbnRfbWVkaWFuX2xvZykgd2l0aCB0aGUgbnVsbCBtb2RlbCAoZXhjbHVkaW5nIGl0KSB1c2luZyBhcHByb3hpbWF0ZSBsZWF2ZS1vbmUtb3V0IGNyb3NzLXZhbGlkYXRpb24gKExPTy1DVikuDQoNClRoZSBleHBlY3RlZCBsb2cgcHJlZGljdGl2ZSBkZW5zaXR5IChFTFBEKSBkaWZmZXJlbmNlIHdhczoNCg0KzpRFTFBEID0gMzEuNyBpbiBmYXZvdXIgb2YgdGhlIGZ1bGwgbW9kZWwsIHdpdGggYSBzdGFuZGFyZCBlcnJvciAoU0UpIG9mIDguMS4NCg0KU2luY2UgdGhlIEVMUEQgZGlmZmVyZW5jZSBpcyBhcHByb3hpbWF0ZWx5IGZvdXIgdGltZXMgbGFyZ2VyIHRoYW4gaXRzIHN0YW5kYXJkIGVycm9yICjOlC9TRSDiiYggMy45KSwgdGhpcyBpbmRpY2F0ZXMgc3Ryb25nIGV2aWRlbmNlIHRoYXQgdGhlIGZ1bGwgbW9kZWwgcHJvdmlkZXMgc3Vic3RhbnRpYWxseSBiZXR0ZXIgb3V0LW9mLXNhbXBsZSBwcmVkaWN0aXZlIHBlcmZvcm1hbmNlIHRoYW4gdGhlIG51bGwgbW9kZWwuDQoNCkluIG90aGVyIHdvcmRzLCBpbmNsdWRpbmcgZWZmbHVlbnRfbWVkaWFuX2xvZyBhcyBhIHByZWRpY3RvciBzaWduaWZpY2FudGx5IGltcHJvdmVzIHRoZSBtb2RlbOKAmXMgYWJpbGl0eSB0byBwcmVkaWN0IHRlc3RlZCBjb25jZW50cmF0aW9ucyBhY3Jvc3Mgc3R1ZGllcy4NCg0KYGBge3J9DQpsb29fY29tcGFyZShsb28oZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZywgbW9tZW50X21hdGNoID0gVFJVRSksIGxvbyhlZmZfY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2csICBtb21lbnRfbWF0Y2ggPSBUUlVFKSkNCmBgYA0KDQpUaGUgQmF5ZXMgZmFjdG9yIGNvbXBhcmluZyB0aGUgZnVsbCBtb2RlbCAoaW5jbHVkaW5nIGVmZmx1ZW50X21lZGlhbl9sb2cpIHRvIHRoZSBudWxsIG1vZGVsIChleGNsdWRpbmcgaXQpIHdhcyBhcHByb3hpbWF0ZWx5IDIuMjUgw5cgMTDCucKzLCBvdmVyd2hlbG1pbmdseSBmYXZvdXJpbmcgdGhlIGZ1bGwgbW9kZWwuDQoNCkEgQmF5ZXMgZmFjdG9yIChCRikgdGhpcyBsYXJnZSBpbmRpY2F0ZXMgZGVjaXNpdmUgZXZpZGVuY2UgdGhhdCB0aGUgZnVsbCBtb2RlbCBwcm92aWRlcyBhIGJldHRlciBleHBsYW5hdGlvbiBvZiB0aGUgZGF0YSB0aGFuIHRoZSBudWxsIG1vZGVsLiBJbiBvdGhlciB3b3JkcywgdGhlIG9ic2VydmVkIGRhdGEgYXJlIHJvdWdobHkgMjIgdHJpbGxpb24gdGltZXMgbW9yZSBsaWtlbHkgdW5kZXIgdGhlIGZ1bGwgbW9kZWwgdGhhbiB1bmRlciB0aGUgbnVsbC4NCg0KVGhpcyBwcm92aWRlcyBleGNlcHRpb25hbGx5IHN0cm9uZyBzdXBwb3J0IGZvciBpbmNsdWRpbmcgZWZmbHVlbnRfbWVkaWFuX2xvZyBhcyBhIHByZWRpY3RvciBvZiB0ZXN0ZWQgY29uY2VudHJhdGlvbnMsIHJlaW5mb3JjaW5nIHRoZSBjb25jbHVzaW9uIGZyb20gdGhlIExPTy1DViBjb21wYXJpc29uLg0KDQpgYGB7cn0NCmJheWVzX2ZhY3RvcihlZmZfY29uY2VudHJhdGlvbl9tb2RfbG9nLCBlZmZfY29uY2VudHJhdGlvbl9tb2RfbnVsbF9sb2cpDQpgYGANCg0KDQpUaGUgQmF5ZXNpYW4gUsKyIGZvciB0aGUgZnVsbCBtb2RlbCAoaW5jbHVkaW5nIGVmZmx1ZW50X21lZGlhbl9sb2cpIHdhcyAwLjEwOCwgd2l0aCBhIDk1JSBjb21wYXRpYmlsaXR5IGludGVydmFsIHJhbmdpbmcgZnJvbSAwLjA3MCB0byAwLjE1MC4NCg0KVGhpcyBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9kZWwgZXhwbGFpbnMgYXBwcm94aW1hdGVseSAxMC44JSBvZiB0aGUgdmFyaWFuY2UgaW4gdGVzdGVkIGNvbmNlbnRyYXRpb25zIGFjcm9zcyBzdHVkaWVzLCB3aXRoIGNyZWRpYmxlIHN1cHBvcnQgZm9yIHRoaXMgdmFsdWUgbHlpbmcgYmV0d2VlbiA3LjAlIGFuZCAxNS4wJS4NCg0KV2hpbGUgdGhpcyByZXByZXNlbnRzIGEgbW9kZXN0IHByb3BvcnRpb24gb2YgZXhwbGFpbmVkIHZhcmlhbmNlLCBpdCBub25ldGhlbGVzcyByZWZsZWN0cyBhIG1lYW5pbmdmdWwgaW1wcm92ZW1lbnQgb3ZlciB0aGUgbnVsbCBtb2RlbCDigJQgZXNwZWNpYWxseSB3aGVuIGNvbnNpZGVyZWQgYWxvbmdzaWRlIHRoZSBzdHJvbmcgc3VwcG9ydCBmcm9tIGJvdGggdGhlIExPTy1DViBjb21wYXJpc29uIGFuZCB0aGUgQmF5ZXMgZmFjdG9yLiBUaGlzIHN1Z2dlc3RzIHRoYXQgZWZmbHVlbnRfbWVkaWFuX2xvZyBpcyBhbiBpbmZvcm1hdGl2ZSBwcmVkaWN0b3IsIGV2ZW4gaWYgdGhlIG92ZXJhbGwgbW9kZWwgZXhwbGFpbnMgYSBsaW1pdGVkIHBvcnRpb24gb2YgdGhlIHZhcmlhYmlsaXR5LCB3aGljaCBtYXkgcmVmbGVjdCBzdWJzdGFudGlhbCBoZXRlcm9nZW5laXR5IGFjcm9zcyBzdHVkaWVzLiANCg0KYGBge3J9DQpwZXJmb3JtYW5jZTo6cjJfYmF5ZXMoZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZywgcm9idXN0ID0gRkFMU0UsIGNpID0gMC45NSkNCmBgYA0KDQpUaGUgbW9kZWwgZml0cyB0aGUgbWFyZ2luYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSByZXNwb25zZSB2YXJpYWJsZSB3ZWxsLg0KDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9OH0NCmNvbG9yX3NjaGVtZV9zZXQoInJlZCIpDQpicm1zOjpwcF9jaGVjayhlZmZfY29uY2VudHJhdGlvbl9tb2RfbG9nLCBuZHJhd3MgPSAyNSwgdHlwZSA9ICJkZW5zX292ZXJsYXkiKQ0KYnJtczo6cHBfY2hlY2soZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZywgbmRyYXdzID0gMjUsIHR5cGUgPSAiZWNkZl9vdmVybGF5IikgIA0KeV9yZXAgPC0gcG9zdGVyaW9yX3ByZWRpY3QoZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZywgbmRyYXdzID0gMTAwMCkNCnlfb2JzIDwtIGVmZl9jb25jZW50cmF0aW9uX21vZF9sb2ckZGF0YSR2YWx1ZV9sb2cgICMgUmVwbGFjZSB3aXRoIHlvdXIgYWN0dWFsIHJlc3BvbnNlIHZhcmlhYmxlDQpwcGNfc3RhdF8yZCh5ID0geV9vYnMsIHlyZXAgPSB5X3JlcCwgc3RhdCA9IGMoIm1lZGlhbiIsICJtYWQiKSkNCmBgYA0KDQoNCkJhc2VkIG9uIHRoZSBkaWFnbm9zdGljIHBsb3RzOigxKSBUaGUgcmVzaWR1YWxzIGV4aGliaXQgbm8gbWFqb3IgZGVwYXJ0dXJlcyBmcm9tIHJhbmRvbW5lc3M7ICgyKSBUaGUgUS1RIGFuZCBkZW5zaXR5IHBsb3RzIHN1cHBvcnQgdGhlIG5vcm1hbGl0eSBhc3N1bXB0aW9uIG9mIHRoZSByZXNpZHVhbHM7ICgzKSBUaGUgYWJzZW5jZSBvZiBzdHJvbmcgcGF0dGVybnMgaW4gdGhlIGF1dG9jb3JyZWxhdGlvbiBhbmQgc2NhbGUtbG9jYXRpb24gcGxvdHMgZnVydGhlciBzdWdnZXN0cyB0aGF0IHRoZSBtb2RlbCBpcyB3ZWxsLXNwZWNpZmllZCB3aXRoIGhvbW9zY2VkYXN0aWMgYW5kIGluZGVwZW5kZW50IGVycm9ycy4NCg0KYGBge3IgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9OH0NCnBlcmZvcm1hbmNlOjpjaGVja19tb2RlbChlZmZfY29uY2VudHJhdGlvbl9tb2RfbG9nKQ0KYGBgDQoNCkNoZWNraW5nIFItaGF0IHZhbHVlcyB0byBjb25maXJtIG1vZGVsIGNvbnZlcmdlbmNlIGFuZCBFU1MgZm8gZXN0aW1hdGVzLiAgIA0KDQpgYGB7cn0NCm1heF9yaGF0IDwtIG1heChyaGF0KGVmZl9jb25jZW50cmF0aW9uX21vZF9sb2cpLCBuYS5ybSA9IFRSVUUpDQpwcmludChnbHVlOjpnbHVlKCJBbGwgUi1oYXQgdmFsdWVzIOKJpCB7bWF4X3JoYXR9IikpDQpgYGAgICANCg0KR2VuZXJhbCBtb2RlbCBvdXRwdXQNCg0KYGBge3J9DQpwcmludChzdW1tYXJ5KGVmZl9jb25jZW50cmF0aW9uX21vZF9sb2csIHByb2IgPSAwLjk1KSkNCmBgYA0KDQpEaWFnbm9zdGljIHBsb3RzIGxvb2sgZmluZS4gQSBzbWFsbGVyIHNoYXBlIHZhbHVlIGluZGljYXRlcyBncmVhdGVyIG92ZXJkaXNwZXJzaW9uICh2YXJpYW5jZSBpcyBtdWNoIGxhcmdlciB0aGFuIHRoZSBtZWFuKS4gIA0KDQpgYGB7cn0NCmNvbG9yX3NjaGVtZV9zZXQoInJlZCIpDQpwbG90KGVmZl9jb25jZW50cmF0aW9uX21vZF9sb2cpDQpgYGANCg0KDQojIyMjIyBNb2RlbCBlc3RpbWF0ZXMNCg0KUHVsbGluZyB0aGUgbWFyZ2luYWwgZWZmZWN0IGVzdGltYXRlcyBmcm9tIHRoZSBtb2RlbCBmb3Igb3VyIHBsb3RzLiAgDQoNCmBgYHtyfQ0KIyBFeHRyYWN0IG1hcmdpbmFsIGVmZmVjdHMgZm9yIHN1cmZhY2Ugd2F0ZXIgYW5kIHllYXINCmVzdGltYXRlX2RhdGFfMiA8LSBtYXJnaW5hbF9lZmZlY3RzKGVmZl9jb25jZW50cmF0aW9uX21vZF9sb2csIGVmZmVjdHMgPSAiZWZmbHVlbnRfbWVkaWFuX2xvZyIpW1sxXV0NCmVzdGltYXRlX2RhdGFfeWVhcl8yIDwtIG1hcmdpbmFsX2VmZmVjdHMoZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZywgZWZmZWN0cyA9ICJyZWxhdGl2ZV95ZWFyIilbWzFdXQ0KYGBgDQoNCg0KVGhlc2UgZXN0aW1hdGVkIGNvZWZmaWNpZW50cyByZXByZXNlbnQgdGhlIGF2ZXJhZ2UgZWZmZWN0cyBhY3Jvc3MgdGhlIGRhdGFzZXQsIGNvbnRyb2xsaW5nIGZvciAocmF0aGVyIHRoYW4gYXZlcmFnaW5nIG92ZXIpIHRoZSBvdGhlciBwcmVkaWN0b3JzIGluIHRoZSBtb2RlbC4gQmVjYXVzZSB0aGUgbW9kZWwgaXMgbG9n4oCTbG9nIGZvciB2YWx1ZV9sb2cgYW5kIGBlZmZsdWVudF9tZWRpYW5fbG9nYCwgdGhlIGNvZWZmaWNpZW50IGZvciBgbG9nKGVmZmx1ZW50X21lZGlhbilgIHJlZmxlY3RzIGFuIGVsYXN0aWNpdHnigJR0aGF0IGlzLCB0aGUgcGVyY2VudCBjaGFuZ2UgaW4gdGhlIHJlc3BvbnNlIGZvciBhIHBlcmNlbnQgY2hhbmdlIGluIHRoZSBwcmVkaWN0b3IuDQoNClRoZSBlc3RpbWF0ZWQgZWZmZWN0IG9mIGBsb2coZWZmbHVlbnRfbWVkaWFuKWAgd2FzICoqzrIgPSAwLjY3OSAoOTUlIENySTogMC41MTYgdG8gMC44NDEpKiosIGluZGljYXRpbmcgdGhhdCBhICoqMSUgaW5jcmVhc2UgaW4gZWZmbHVlbnQgY29uY2VudHJhdGlvbiBpcyBhc3NvY2lhdGVkIHdpdGggYW4gYXZlcmFnZSAwLjY3OSUgaW5jcmVhc2UgaW4gdGVzdGVkIGNvbmNlbnRyYXRpb24qKiwgYWxsIGVsc2UgYmVpbmcgZXF1YWwuIFNpbmNlIHRoZSA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgZG9lcyBub3QgaW5jbHVkZSB6ZXJvLCB0aGlzIGVmZmVjdCBpcyBzdGF0aXN0aWNhbGx5IHdlbGwtc3VwcG9ydGVkLg0KDQpUaGUgY29lZmZpY2llbnQgZm9yIGByZWxhdGl2ZV95ZWFyYCB3YXMgKirOsiA9IOKAkzAuMTM0ICg5NSUgQ3JJOiDigJMwLjE5NSB0byDigJMwLjA3MykqKiwgc3VnZ2VzdGluZyBhIGNvbnNpc3RlbnQgdGVtcG9yYWwgZGVjbGluZSBpbiB0ZXN0ZWQgY29uY2VudHJhdGlvbnMgb3ZlciB0aW1lLiBBcyB0aGlzIHByZWRpY3RvciBpcyBsaW5lYXIgd2hpbGUgdGhlIHJlc3BvbnNlIGlzIGxvZy10cmFuc2Zvcm1lZCwgdGhpcyBjb3JyZXNwb25kcyB0byBhbiBhdmVyYWdlICoqMTIuNiUgZGVjcmVhc2UqKiBpbiB0ZXN0ZWQgY29uY2VudHJhdGlvbiBwZXIgeWVhciAqKihleHAo4oCTMC4xMzQpIOKJiCAwLjg3NCkqKiwgaG9sZGluZyBvdGhlciB2YXJpYWJsZXMgY29uc3RhbnQuDQoNClRoZSBlc3RpbWF0ZWQgZWZmZWN0IG9mIGRvc2VzIHdhcyAqKs6yID0g4oCTMC4wODAgKDk1JSBDckk6IOKAkzAuMjQyIHRvIDAuMDg0KSoqLCBzdWdnZXN0aW5nIGEgd2VhayBhbmQgdW5jZXJ0YWluIG5lZ2F0aXZlIGFzc29jaWF0aW9uIGJldHdlZW4gdGhlIG51bWJlciBvZiBkb3NlcyB0ZXN0ZWQgYW5kIHRoZSBtaW5pbXVtIHJlcG9ydGVkIGNvbmNlbnRyYXRpb24uIEhvd2V2ZXIsIHRoZSBjcmVkaWJsZSBpbnRlcnZhbCBpbmNsdWRlcyB6ZXJvLCBpbmRpY2F0aW5nIG5vIHN0cm9uZyBldmlkZW5jZSBmb3IgYW4gZWZmZWN0IG9mIGRvc2VzIGluIHRoaXMgbW9kZWwuDQoNCg0KYGBge3J9DQptb2RlbF9zdW1tYXJ5XzIgPC0gIGFzLmRhdGEuZnJhbWUoZml4ZWYoZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZywgc3VtbWFyeSA9IFRSVUUpKSAlPiUgDQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JSANCiAgY2xlYW5fbmFtZXMoKQ0KDQplc3RpbWF0ZV8yIDwtIG1vZGVsX3N1bW1hcnlfMiRlc3RpbWF0ZVsyXQ0KTDk1Q0lfMiA8LSBtb2RlbF9zdW1tYXJ5XzIkcTJfNVsyXQ0KSDk1Q0lfMiA8LSBtb2RlbF9zdW1tYXJ5XzIkcTk3XzVbMl0NCg0KZXN0aW1hdGVfeWVhcl8yIDwtIG1vZGVsX3N1bW1hcnlfMiRlc3RpbWF0ZVszXQ0KTDk1Q0lfeWVhcl8yIDwtIG1vZGVsX3N1bW1hcnlfMiRxMl81WzNdDQpIOTVDSV95ZWFyXzIgPC0gbW9kZWxfc3VtbWFyeV8yJHE5N181WzNdDQoNCg0KbW9kZWxfc3VtbWFyeV8yX2Z0IDwtIG1vZGVsX3N1bW1hcnlfMiAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmRvdWJsZSksIH4gcm91bmQoLngsIDMpKSkgJT4lIA0KICBkcGx5cjo6cmVuYW1lKCJwcmVkaWN0b3IiID0gcm93bmFtZSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKENySSA9IHBhc3RlMChxMl81LCAiIHRvICIsIHE5N181KSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KHByZWRpY3RvciwgZXN0aW1hdGUsIENySSkgJT4lIA0KICBmbGV4dGFibGUoKSAlPiUNCiAgc2V0X2hlYWRlcl9sYWJlbHMoDQogICAgcHJlZGljdG9yID0gIlByZWRpY3RvciIsDQogICAgZXN0aW1hdGUgPSAiRXN0aW1hdGUiLA0KICAgIENySSA9ICI5NSUgQ3JJIg0KICApICU+JQ0KICBhdXRvZml0KCkNCg0KbW9kZWxfc3VtbWFyeV8yX2Z0DQpgYGANCg0KU2F2aW5nIHRoZSB0YWJsZSANCg0KYGBge3J9DQpzYXZlX2FzX2RvY3gobW9kZWxfc3VtbWFyeV8yX2Z0LCBwYXRoID0gcGFzdGUwKGZpZ193ZCwgIi4vdGFibGUtczMuZG9jeCIpKQ0KYGBgDQoNCiMjIyMjIEZpZyAxYg0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02fQ0KdGV4dF9jb29yZCA8LSB3YXN0ZXdhdGVyX2NvbmMgJT4lIA0KICBzdW1tYXJpc2UoeSA9IG1heCh2YWx1ZV9sb2csIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICB4X21heCA9IG1heChlZmZsdWVudF9tZWRpYW5fbG9nLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgeF9taW4gPSBtaW4oZWZmbHVlbnRfbWVkaWFuX2xvZywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHggPSBtZWFuKGMoeF9tYXgseF9taW4pKSkNCg0KeV9jb29yZCA8LSB0ZXh0X2Nvb3JkICU+JSBwdWxsKHkpDQoNCnhfY29vcmQgPC0gdGV4dF9jb29yZCAlPiUgcHVsbCh4KQ0KDQoNCmZpZ18xYiA8LSBnZ3Bsb3QoKSArDQogIGdlb21fbGluZSgNCiAgICBkYXRhID0gZXN0aW1hdGVfZGF0YV8yLA0KICAgIGFlcyh4ID0gZWZmbHVlbnRfbWVkaWFuX2xvZywgeSA9IGVzdGltYXRlX18pLA0KICAgIGNvbG9yID0gIiM1QjQ4OUQiLA0KICAgIGxpbmV3aWR0aCA9IDENCiAgKSArDQogIGdlb21fcmliYm9uKA0KICAgIGRhdGEgPSBlc3RpbWF0ZV9kYXRhXzIsDQogICAgYWVzKHggPSBlZmZsdWVudF9tZWRpYW5fbG9nLCB5bWluID0gbG93ZXJfXywgeW1heCA9IHVwcGVyX18pLA0KICAgIGZpbGwgPSAiIzVCNDg5RCIsDQogICAgYWxwaGEgPSAwLjENCiAgKSArDQogIGdlb21fcG9pbnQoDQogICAgZGF0YSA9IHdhc3Rld2F0ZXJfY29uYywNCiAgICBhZXMoeCA9IGVmZmx1ZW50X21lZGlhbl9sb2csIHkgPSB2YWx1ZV9sb2cpLA0KICAgIGFscGhhID0gMC40LA0KICAgIHNoYXBlID0gMjEsDQogICAgY29sb3IgPSAiIzVCNDg5RCIsDQogICAgZmlsbCA9ICIjNUI0ODlEIg0KICApICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSB4X2Nvb3JkLA0KICAgIHkgPSB5X2Nvb3JkLA0KICAgIGxhYmVsID0gcGFzdGUwKA0KICAgICAgIkJheWVzaWFuIGxvZy1sb2cgR2F1c3NpYW4gcmVncmVzc2lvbiIsDQogICAgICAiXG4iLA0KICAgICAgIkVmZmVjdCBlc3RpbWF0ZTogIiwNCiAgICAgIHJvdW5kKGVzdGltYXRlXzIsIDMpLA0KICAgICAgIlxuIiwNCiAgICAgICI5NSVDckk6ICIsDQogICAgICByb3VuZChMOTVDSV8yLCAzKSwNCiAgICAgICIgdG8gIiwNCiAgICAgIHJvdW5kKEg5NUNJXzIsIDMpDQogICAgKSwNCiAgICBoanVzdCA9IDAsDQogICAgdmp1c3QgPSAxLA0KICAgIHNpemUgPSA0DQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIihCKSAgV2FzdGV3YXRlciB2cyBleHBvc3VyZSBjb25jZW50cmF0aW9uIiwNCiAgICB4ID0gIkV4cG9zdXJlIGNvbmNlbnRyYXRpb24gKM68Zy9MKSoiLA0KICAgIHkgPSAiRW52aXJvbm1lbnRhbCBjb25jZW50cmF0aW9uICjOvGcvTCkqIiwNCiAgICBjYXB0aW9uID0gIipWYWx1ZXMgaW4gzrxnL0wgc3BhY2Ugb24gYSBsb2cgc2NhbGUiDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkNCiAgICAgIHNjYWxlczo6bGFiZWxfc2NpZW50aWZpYygpKGV4cCh4KSkNCiAgKSArDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBsYWJlbHMgPSBmdW5jdGlvbih4KQ0KICAgICAgc2NhbGVzOjpsYWJlbF9zY2llbnRpZmljKCkoZXhwKHgpKQ0KICApICsNCiAgdGhlbWVfZmV3KCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLA0KICAgIHBsb3QuY2FwdGlvbiAgICAgID0gZWxlbWVudF90ZXh0KA0KICAgICAgc2l6ZSAgID0gOCwNCiAgICAgIGhqdXN0ICA9IDAuNSwNCiAgICAgIG1hcmdpbiA9IG1hcmdpbih0ID0gNikNCiAgICApDQogICkNCmZpZ18xYg0KYGBgDQoNClNhdmluZyBmaWd1cmUgDQoNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZpZ193ZCwgIi4vZmlndXJlLTFiLnBkZiIpLA0KICAgICAgIHBsb3QgPSBmaWdfMWIsDQogICAgICAgd2lkdGggPSAyMTAsDQogICAgICAgaGVpZ2h0ID0gMjk3LzEuNSwgICMgU3BlY2lmeSB0aGUgaGVpZ2h0IG9mIHRoZSBwbG90DQogICAgICAgdW5pdHMgPSAibW0iKQ0KYGBgDQoNCg0KIyMjIyMgRmlnIFMxYg0KDQpQbG90IHN1cmZhY2Ugd2F0ZXIgY29uY2VudHJhdGlvbnMgYnkgeWVhciAgIA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02fQ0KdGV4dF9jb29yZCA8LSB3YXN0ZXdhdGVyX2NvbmMgJT4lDQogIHN1bW1hcmlzZSgNCiAgICB5X21heCA9IG1heCh2YWx1ZV9sb2csIG5hLnJtID0gVFJVRSksDQogICAgeF9tYXggPSBtYXgocmVsYXRpdmVfeWVhciwgbmEucm0gPSBUUlVFKSwNCiAgICB4X21pbiA9IG1pbihyZWxhdGl2ZV95ZWFyLCBuYS5ybSA9IFRSVUUpLA0KICAgIHhfbWVhbiA9IG1lYW4oYyh4X21heCwgeF9taW4pKQ0KICApDQoNCnlfY29vcmQgPC0gdGV4dF9jb29yZCAlPiUgcHVsbCh5X21heCkNCg0KeF9jb29yZCA8LSB0ZXh0X2Nvb3JkICU+JSBwdWxsKHhfbWluKQ0KDQoNCmZpZ19zMWIgPC0gZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoDQogICAgZGF0YSA9IGVzdGltYXRlX2RhdGFfeWVhcl8yLA0KICAgIGFlcyh4ID0gcmVsYXRpdmVfeWVhciwgeSA9IGVzdGltYXRlX18pLA0KICAgIGNvbG9yID0gIiM1QjQ4OUQiLA0KICAgIGxpbmV3aWR0aCA9IDENCiAgKSArDQogIGdlb21fcmliYm9uKA0KICAgIGRhdGEgPSBlc3RpbWF0ZV9kYXRhX3llYXJfMiwNCiAgICBhZXMoeCA9IHJlbGF0aXZlX3llYXIsIHltaW4gPSBsb3dlcl9fLCB5bWF4ID0gdXBwZXJfXyksDQogICAgZmlsbCA9ICIjNUI0ODlEIiwNCiAgICBhbHBoYSA9IDAuMQ0KICApICsNCiAgZ2VvbV9wb2ludCgNCiAgICBkYXRhID0gd2FzdGV3YXRlcl9jb25jLA0KICAgIGFlcyh4ID0gcmVsYXRpdmVfeWVhciwgeSA9IHZhbHVlX2xvZyksDQogICAgYWxwaGEgPSAwLjQsDQogICAgc2hhcGUgPSAyMSwNCiAgICBjb2xvciA9ICIjNUI0ODlEIiwNCiAgICBmaWxsID0gIiM1QjQ4OUQiDQogICkgKw0KICBhbm5vdGF0ZSgNCiAgICAidGV4dCIsDQogICAgeCA9IHhfY29vcmQsDQogICAgeSA9IHlfY29vcmQsDQogICAgbGFiZWwgPSBwYXN0ZTAoDQogICAgICAiQmF5ZXNpYW4gbG9nLWxvZyBHYXVzc2lhbiByZWdyZXNzaW9uIiwNCiAgICAgICJcbiIsDQogICAgICAiRWZmZWN0IGVzdGltYXRlOiAiLA0KICAgICAgcm91bmQoZXN0aW1hdGVfeWVhcl8yLCAzKSwNCiAgICAgICJcbiIsDQogICAgICAiOTUlQ3JJOiAiLA0KICAgICAgcm91bmQoTDk1Q0lfeWVhcl8yLCAzKSwNCiAgICAgICIgdG8gIiwNCiAgICAgIHJvdW5kKEg5NUNJX3llYXJfMiwgMykNCiAgICApLA0KICAgIGhqdXN0ID0gMCwNCiAgICB2anVzdCA9IDEsDQogICAgc2l6ZSA9IDQNCiAgKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiKEIpICBFeHBvc3VyZSBjb25jZW50cmF0aW9uIHZzIHllYXIgKHdhc3Rld2F0ZXIgY29tcG91bmRzKSIsDQogICAgeCA9ICJZZWFyIiwNCiAgICB5ID0gIkV4cG9zdXJlIGNvbmNlbnRyYXRpb24gKM68Zy9MKSoiLA0KICAgIGNhcHRpb24gPSAiKkNvbmNlbnRyYXRpb24gdmFsdWVzIGluIM68Zy9MIHNwYWNlIG9uIGEgbG9nIHNjYWxlIg0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIGxhYmVscyA9IGZ1bmN0aW9uKHgpDQogICAgICBzY2FsZXM6OmxhYmVsX3NjaWVudGlmaWMoKShleHAoeCkpDQogICkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkNCiAgICAgIHggKyAxOTkyDQogICkgKw0KdGhlbWVfZmV3KCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLA0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dCgNCiAgICAgIHNpemUgICA9IDgsDQogICAgICBoanVzdCAgPSAwLjUsDQogICAgICBtYXJnaW4gPSBtYXJnaW4odCA9IDYpDQogICAgKQ0KICApDQpmaWdfczFiDQpgYGANCg0KU2F2ZSB0aGlzIGZpZ3VyZSBmb3IgdGhlIE1TICAgDQoNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZpZ193ZCwgIi4vZmlndXJlLXMxYi5wZGYiKSwNCiAgICAgICBwbG90ID0gZmlnX3MxYiwNCiAgICAgICB3aWR0aCA9IDIxMCwNCiAgICAgICBoZWlnaHQgPSAyOTcvMS41LA0KICAgICAgIHVuaXRzID0gIm1tIikNCmBgYA0KDQoNCiMjIyMjIE91dGxpZXIgYXNzZW1zZW50DQoNCklzIHRoaXMgdHJlbmQgc3RpbGwgcHJlc2VudCBpZiB0aGUgZXh0cmVtZSBjb25jZW50cmF0aW9uIGlzIHJlbW92ZWQuIFRoZSBjb21wb3VuZCBpcyBzdHJlcHRvbXljaW4gKENBUzogNTctOTItMSkNCg0KYGBge3J9DQp3YXN0ZXdhdGVyX2NvbmMgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShkZXNjKGVmZmx1ZW50X21lZGlhbl9sb2cpKSAlPiUgDQogIGRwbHlyOjpzbGljZSgxKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY29tcG91bmRfY2FzX2NvcnJlY3RlZCwgY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQsIHZhbHVlKSAlPiUgDQogIGZsZXh0YWJsZSgpDQpgYGANCg0KIyMjIyMjIFJ1biBtb2RlbA0KDQpCdWlsZGluZyBhbiBpZGVudGljYWwgbW9kZWwgd2l0aG91dCBzdHJlcHRvbXljaW4gKG91dGxpZXIgcmVtb3ZlZDsgb3IpDQoNCmBgYHtyfQ0Kb3B0aW9ucyhicm1zLmJhY2tlbmQgPSAiY21kc3RhbnIiKQ0KZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZ19vciA8LSBicm0oZWZmX2NvbmNlbnRyYXRpb25fc3RyLA0KICAgICAgICAgICAgICAgZGF0YSA9IHdhc3Rld2F0ZXJfY29uYyAlPiUNCiAgICAgICAgICAgICAgICAgZHBseXI6OmZpbHRlcihjb21wb3VuZF9jYXNfY29ycmVjdGVkICE9ICI1Ny05Mi0xIiksDQogICAgICAgICAgICAgICBjb3JlcyA9IDQsDQogICAgICAgICAgICAgICBjaGFpbnMgPSA0LA0KICAgICAgICAgICAgICAgI3ByaW9yID0gbW9kX3ByaW9ycywgI3VzZSBkZWZhdWx0cw0KICAgICAgICAgICAgICAgd2FybXVwID0gMTAwMCwNCiAgICAgICAgICAgICAgIHNlZWQgPSAyMDI1MDQxOCwNCiAgICAgICAgICAgICAgIHRoaW4gPSAyLA0KICAgICAgICAgICAgICAgaXRlciA9IDgwMDAsDQogICAgICAgICAgICAgICBjb250cm9sID0gbGlzdChtYXhfdHJlZWRlcHRoID0gMjAsIGFkYXB0X2RlbHRhID0gMC45NSksDQogICAgICAgICAgICAgICBzYXZlX3BhcnMgPSBzYXZlX3BhcnMoYWxsPVRSVUUpLA0KICAgICAgICAgICAgICAgc2FtcGxlX3ByaW9yID0gVFJVRSwNCiAgICAgICAgICAgICAgIGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgJy4vZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZ19vcicpKQ0KYGBgDQoNCg0KUmVsb2FkIHRoZSBtb2RlbCBpZiBuZWNlc3NhcnkuICANCg0KYGBge3J9DQplZmZfY29uY2VudHJhdGlvbl9tb2RfbG9nX29yIDwtICByZWFkUkRTKGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgIi4vZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZ19vci5yZHMiKSkNCmBgYA0KDQpUaGUgZWZmZWN0IG9mIHllYXIgc2VlbXMgcm9idXN0IHRvIHRoZSBvdXRsaWVyLiAgDQoNCmBgYHtyfQ0KbW9kZWxfc3VtbWFyeV8yXzEgPC0gIGFzLmRhdGEuZnJhbWUoZml4ZWYoZWZmX2NvbmNlbnRyYXRpb25fbW9kX2xvZ19vciwgc3VtbWFyeSA9IFRSVUUpKSAlPiUgDQogIHJvd25hbWVzX3RvX2NvbHVtbigpICU+JSANCiAgY2xlYW5fbmFtZXMoKQ0KDQp0YWJsZV9pIDwtIG1vZGVsX3N1bW1hcnlfMl8xICU+JSANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuZG91YmxlKSwgfiByb3VuZCgueCwgMykpKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoInByZWRpY3RvciIgPSByb3duYW1lKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoQ3JJID0gcGFzdGUwKHEyXzUsICIgdG8gIiwgcTk3XzUpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QocHJlZGljdG9yLCBlc3RpbWF0ZSwgQ3JJKSAlPiUgDQogIGZsZXh0YWJsZSgpICU+JQ0KICBzZXRfaGVhZGVyX2xhYmVscygNCiAgICBwcmVkaWN0b3IgPSAiUHJlZGljdG9yIiwNCiAgICBlc3RpbWF0ZSA9ICJFc3RpbWF0ZSAob3V0bGllciByZW1vdmVkKSIsDQogICAgQ3JJID0gIjk1JSBDckkgKG91dGxpZXIgcmVtb3ZlZCkiDQogICkgJT4lDQogIGF1dG9maXQoKQ0KDQp0YWJsZV9pDQpgYGANCg0KDQoNCg0KIyMjIPCfk4ggQ29uY2VudHJhdGlvbiBmb2xkIGRpZmZlcmVuY2VzDQoNCkhlcmUgd2UgYXJlIHN1bW1hcmlzaW5nIGhvdyBtYW55IHRlc3Qgd2VyZSBiZWxvdyB0aGUgbWVkaWFuIHN1cmZhY2Ugd2F0ZXIgYW5kIGVmZmx1ZW50IGNvbmNlbnRyYXRpb25zIGFzIHdlbGwgYXMgdGhlIHVwcGVyIDk1JUNJLiBXZSBhcmUgYWxzbyBjYWxjdWxhdGluZyB0aGUgbWVhbi1mb2xkIGRpZmZlcmVuY2UgaW4gY29uY2VudHJhdGlvbnMuDQoNCmBgYHtyfQ0KY29uY19zdW1tYXJ5X292ZXJhbGwgPC0gYWxsX2RhdGFiYXNlc193aXRoX3N1bSAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKA0KICAgIGVpcGFhYl9uID0gbGVuZ3RoKHVuaXF1ZV9yb3dfaWQpLA0KICAgIG5fY29tcG91bmRzX3N3ID0gbGVuZ3RoKHVuaXF1ZShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZFshaXMubmEoc3VyZmFjZXdhdGVyX21lZGlhbildKSksDQogICAgbl9jb21wb3VuZHNfZWYgPSBsZW5ndGgodW5pcXVlKGNvbXBvdW5kX25hbWVfY29ycmVjdGVkWyFpcy5uYShlZmZsdWVudF9tZWRpYW4pXSkpLA0KICAgIG5fc3cgPSBzdW0oIWlzLm5hKHVuZGVyX21lZF9zdXJmKSksDQogICAgbGVzc19tZWRfc3cgPSBzdW0odW5kZXJfbWVkX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgbGVzc19oY2lfc3cgPSBzdW0odW5kZXJfaGNpX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgcGVyY2VudF9sZXNzX21lZF9zdyA9IChsZXNzX21lZF9zdyAvIG5fc3cpICogMTAwLA0KICAgIHBlcmNlbnRfbGVzc19oaWNfc3cgPSAobGVzc19oY2lfc3cgLyBuX3N3KSAqIDEwMCwNCiAgICBuX2VmID0gc3VtKCFpcy5uYSh1bmRlcl9tZWRfZWZmbCkpLA0KICAgIGxlc3NfbWVkX2VmID0gc3VtKHVuZGVyX21lZF9lZmZsLCBuYS5ybSA9IFRSVUUpLA0KICAgIGxlc3NfaGNpX2VmID0gc3VtKHVuZGVyX2hjaV9lZmZsLCBuYS5ybSA9IFRSVUUpLA0KICAgIHBlcmNlbnRfbGVzc19tZWRfZWYgPSAobGVzc19tZWRfZWYgLyBuX2VmKSAqIDEwMCwNCiAgICBwZXJjZW50X2xlc3NfaGljX2VmID0gKGxlc3NfaGNpX2VmIC8gbl9lZikgKiAxMDAsDQogICAgDQogICAgIyBNZWRpYW4gdmFsdWVzDQogICAgZmRpZmZfbWVkX3N3ID0gbWVkaWFuKGRpZmZfbWVkX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgZmRpZmZfaGNpX3N3ID0gbWVkaWFuKGRpZmZfaGljX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgZmRpZmZfbWVkX2VmID0gbWVkaWFuKGRpZmZfbWVkX2VmZmwsIG5hLnJtID0gVFJVRSksDQogICAgZmRpZmZfaGNpX2VmID0gbWVkaWFuKGRpZmZfaGljX2VmZmwsIG5hLnJtID0gVFJVRSksDQogICAgDQogICAgIyBTdGFuZGFyZCBEZXZpYXRpb24NCiAgICBzZF9mZGlmZl9tZWRfc3cgPSBzZChkaWZmX21lZF9zdXJmLCBuYS5ybSA9IFRSVUUpLA0KICAgIHNkX2ZkaWZmX2hjaV9zdyA9IHNkKGRpZmZfaGljX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgc2RfZmRpZmZfbWVkX2VmID0gc2QoZGlmZl9tZWRfZWZmbCwgbmEucm0gPSBUUlVFKSwNCiAgICBzZF9mZGlmZl9oY2lfZWYgPSBzZChkaWZmX2hpY19lZmZsLCBuYS5ybSA9IFRSVUUpDQogICkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyhldmVyeXRoaW5nKCksIH4gaWZlbHNlKGlzLm5hbiguKSwgTkEsIC4pKSkgICMgQ29udmVydCBOYU5zIHRvIE5Bcw0KYGBgDQoNCg0KIyMjIyBSZXN1bHRzDQoNCkZvciB0aGUgNzA2IGV4cG9zdXJlcyB0byBjb21wb3VuZHMgd2l0aCBzdXJmYWNlIHdhdGVyIGRldGVjdGlvbnMgKGluIFVCQSwgTk9STUFOLCBvciBXaWxrc29uKSwgb25seSAxOC43MCUgYW5kIDM3Ljk2JSBlbXBsb3llZCB0ZXN0IGNvbmNlbnRyYXRpb25zIGxvd2VyIHRoZW4gbWVkaWFuIGFuZCB1cHBlciA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgb2Ygc3VyZmFjZSB3YXRlciBjb25jZW50cmF0aW9ucywgcmVzcGVjdGl2ZWx5LiBGdXJ0aGVyLCBmb3IgdGhlIDcxNCBleHBvc3VyZXMgdG8gY29tcG91bmRzIHdpdGggd2FzdGV3YXRlciBkZXRlY3Rpb25zLCBvbmx5IDIzLjM5JSBhbmQgNTMuMDglIGVtcGxveWVkIHRlc3QgY29uY2VudHJhdGlvbnMgbG93ZXIgdGhlbiBtZWRpYW4gYW5kIHVwcGVyIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCBvZiB3YXN0ZXdhdGVyLiBPbiBhdmVyYWdlLCBjb25jZW50cmF0aW9ucyB1c2VkIGluIGJlaGF2aW91cmFsIGVjb3RveGljb2xvZ3kgd2VyZSA0MyB0aW1lcyBoaWdoZXIgdGhlbiBtZWRpYW4gc3VyZiB3YXRlciBjb25jZW50cmF0aW9ucywgYW5kIDEwIHRpbWVzIGhpZ2hlciB0aGVuIHRob3NlIGluIGVmZmx1ZW50LiANCg0KYGBge3J9DQpzdXJmIDwtIGNvbmNfc3VtbWFyeV9vdmVyYWxsICU+JSANCiAgZHBseXI6Om11dGF0ZShmZGlmZl9tZWRfc3cgPSByb3VuZChmZGlmZl9tZWRfc3csMiksDQogICAgICAgICAgICAgICAgZmRpZmZfaGNpX3N3ID0gcm91bmQoZmRpZmZfaGNpX3N3KSkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KG5fc3csIGxlc3NfbWVkX3N3LCBsZXNzX2hjaV9zdywgZmRpZmZfbWVkX3N3LCBmZGlmZl9oY2lfc3csIHBlcmNlbnRfbGVzc19tZWRfc3csIHBlcmNlbnRfbGVzc19oaWNfc3csIG5fY29tcG91bmRzX3N3KSAlPiUgDQogIGRwbHlyOjpyZW5hbWUodG90YWxfbiA9IG5fc3csIGxlc3NfbWVkID0gbGVzc19tZWRfc3csIGxlc3NfaGNpID0gbGVzc19oY2lfc3csIGZkaWZmX21lZCA9IGZkaWZmX21lZF9zdywgZmRpZmZfaGNpID0gZmRpZmZfaGNpX3N3LCBwZXJjZW50X2xlc3NfbWVkID0gcGVyY2VudF9sZXNzX21lZF9zdywgcGVyY2VudF9sZXNzX2hpYyA9IHBlcmNlbnRfbGVzc19oaWNfc3csIGNvbXBvdW5kcyA9IG5fY29tcG91bmRzX3N3KSAlPiUgDQogIGRwbHlyOjptdXRhdGUobWF0cml4ID0gIlN1cmZhY2Ugd2F0ZXIiKQ0KDQplZmZsdWVudCA8LSBjb25jX3N1bW1hcnlfb3ZlcmFsbCAlPiUgDQogICAgZHBseXI6Om11dGF0ZShmZGlmZl9tZWRfZWYgPSByb3VuZChmZGlmZl9tZWRfZWYsIDIpLA0KICAgICAgICAgICAgICAgIGZkaWZmX2hjaV9lZiA9IHJvdW5kKGZkaWZmX2hjaV9lZiwgMikpICU+JSANCiAgZHBseXI6OnNlbGVjdChuX2VmLCBsZXNzX21lZF9lZiwgbGVzc19oY2lfZWYsIGZkaWZmX21lZF9lZiwgZmRpZmZfaGNpX2VmLCBwZXJjZW50X2xlc3NfbWVkX2VmLCBwZXJjZW50X2xlc3NfaGljX2VmLCBuX2NvbXBvdW5kc19lZikgJT4lIA0KICBkcGx5cjo6cmVuYW1lKHRvdGFsX24gPSBuX2VmLCBsZXNzX21lZCA9IGxlc3NfbWVkX2VmLCBsZXNzX2hjaSA9IGxlc3NfaGNpX2VmLCBmZGlmZl9tZWQgPSBmZGlmZl9tZWRfZWYsIGZkaWZmX2hjaSA9IGZkaWZmX2hjaV9lZiwgcGVyY2VudF9sZXNzX21lZCA9IHBlcmNlbnRfbGVzc19tZWRfZWYsIHBlcmNlbnRfbGVzc19oaWMgPSBwZXJjZW50X2xlc3NfaGljX2VmLCBjb21wb3VuZHMgPSBuX2NvbXBvdW5kc19lZikgJT4lIA0KICBkcGx5cjo6bXV0YXRlKG1hdHJpeCA9ICJFZmZsdWVudCIpDQoNCm92ZXJhbGxfc3VtbWFyeV90YWJsZSA8LSByYmluZChzdXJmLCBlZmZsdWVudCkgJT4lIA0KICBkcGx5cjo6c2VsZWN0KG1hdHJpeCwgZXZlcnl0aGluZygpKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoDQogICAgcGVyY2VudF9sZXNzX21lZCA9IHJvdW5kKHBlcmNlbnRfbGVzc19tZWQsIDIpLA0KICAgIHBlcmNlbnRfbGVzc19oaWMgPSByb3VuZChwZXJjZW50X2xlc3NfaGljLCAyKQ0KICApICU+JSANCiAgZHBseXI6OnJlbmFtZSgNCiAgICBNYXRyaXggPSBtYXRyaXgsDQogICAgYFRvdGFsIHRlc3RzYCA9IHRvdGFsX24sDQogICAgYFRvdGFsIGNvbXBvdW5kc2AgPSBjb21wb3VuZHMsDQogICAgYERvc2UgbGVzcyB0aGFuIG1lZGlhbmAgPSBsZXNzX21lZCwNCiAgICBgRG9zZSBsZXNzIHRoYW4gdXBwZXIgOTUlQ3JJYCA9IGxlc3NfaGNpLA0KICAgIGBNZWFuLWZvbGQgZGlmZmVyZW5jZSBmcm9tIG1lZGlhbmAgPSBmZGlmZl9tZWQsDQogICAgYE1lYW4tZm9sZCBkaWZmZXJlbmNlIGZyb20gdXBwZXIgOTUlQ3JJYCA9IGZkaWZmX2hjaSwNCiAgICBgUGVyY2VudCBsZXNzIHRoYW4gbWVkaWFuICglKWAgPSBwZXJjZW50X2xlc3NfbWVkLA0KICAgIGBQZXJjZW50IGxlc3MgdGhhbiB1cHBlciA5NSVDckkgKCUpYCA9IHBlcmNlbnRfbGVzc19oaWMNCiAgKSAlPiUNCiAgZmxleHRhYmxlKCkgJT4lDQogIGNvbGZvcm1hdF9udW0oaiA9ICJUb3RhbCB0ZXN0cyIsIGRpZ2l0cyA9IDApICU+JSAgIyBGb3JtYXQgdG90YWwgdGVzdHMgYXMgd2hvbGUgbnVtYmVycw0KICBhbGlnbihhbGlnbiA9ICJjZW50ZXIiLCBwYXJ0ID0gImFsbCIpICU+JQ0KICBhdXRvZml0KCkNCg0Kb3ZlcmFsbF9zdW1tYXJ5X3RhYmxlDQpgYGANCg0KYGBge3J9DQpzYXZlX2FzX2RvY3gob3ZlcmFsbF9zdW1tYXJ5X3RhYmxlLCBwYXRoID0gcGFzdGUwKGZpZ193ZCwgIi4vdGFibGUtczYtMS5kb2N4IikpDQpgYGANCg0KDQpOb3cgd2Ugd2lsbCBtYWtlIGEgc3VtbWFyeSBmb3IgZWFjaCBjb21wb3VuZA0KDQpgYGB7cn0NCnNhbXBsZV9jb3VudHMgPC0gYWxsX2RhdGFiYXNlc193aXRoX3N1bSAlPiUgDQogIGRwbHlyOjpzZWxlY3QoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQsIHN1cmZhY2V3YXRlcl9uLCBlZmZsdWVudF9uKSAlPiUgDQogIGdyb3VwX2J5KGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSAlPiUgDQogIGRwbHlyOjpzbGljZSgxKSAlPiUgDQogIHVuZ3JvdXAoKQ0KDQp1bmRlcl9sZXZlbHMgPC0gYWxsX2RhdGFiYXNlc193aXRoX3N1bSAlPiUgDQogIGRwbHlyOjpncm91cF9ieShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkgJT4lIA0KICBkcGx5cjo6cmVmcmFtZShlaXBhYWJfbiA9IGxlbmd0aCh1bmlxdWVfcm93X2lkKSwNCiAgICAgICAgICAgICAgICAgbl9zdyA9IHN1bSghaXMubmEodW5kZXJfbWVkX3N1cmYpKSwNCiAgICAgICAgICAgICAgICAgbGVzc19tZWRfc3cgPSBzdW0odW5kZXJfbWVkX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgIGxlc3NfaGNpX3N3ID0gc3VtKHVuZGVyX2hjaV9zdXJmLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBwcm9wX2xlc3NfbWVkX3N3ID0gbGVzc19tZWRfc3cvbl9zdywNCiAgICAgICAgICAgICAgICAgcHJvcF9sZXNzX2hpY19zdyA9IGxlc3NfaGNpX3N3L25fc3csDQogICAgICAgICAgICAgICAgIG5fZWYgPSBzdW0oIWlzLm5hKHVuZGVyX21lZF9lZmZsKSksDQogICAgICAgICAgICAgICAgIGxlc3NfbWVkX2VmID0gc3VtKHVuZGVyX21lZF9lZmZsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBsZXNzX2hjaV9lZiA9IHN1bSh1bmRlcl9oY2lfZWZmbCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgcHJvcF9sZXNzX21lZF9lZiA9IGxlc3NfbWVkX2VmL25fZWYsDQogICAgICAgICAgICAgICAgIHByb3BfbGVzc19oaWNfZWYgPSBsZXNzX2hjaV9lZi9uX2VmLA0KICAgICAgICAgICAgICAgICBmZGlmZl9tZWRfc3cgPSBtZWRpYW4oZGlmZl9tZWRfc3VyZiwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgZmRpZmZfaGNpX3N3ID0gbWVkaWFuKGRpZmZfaGljX3N1cmYsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgIGZkaWZmX21lZF9lZiA9IG1lZGlhbihkaWZmX21lZF9lZmZsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBmZGlmZl9oY2lfZWYgPSBtZWRpYW4oZGlmZl9oaWNfZWZmbCwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgKSAlPiUgDQogIGRwbHlyOjpsZWZ0X2pvaW4oLiwgc2FtcGxlX2NvdW50cywgYnkgPSAiY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQiKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfiBpZmVsc2UoaXMubmFuKC4pLCBOQSwgLikpKQ0KYGBgDQoNCg0KYGBge3J9DQp1bmRlcl9sZXZlbHMNCmBgYA0KDQojIyMjIFN1cmZhY2Ugd2F0ZXINCg0KIyMjIyMgRmlnIDINCg0KTGV0cyBwbG90IHRoZSBtZWFuLWZvbGQgZGlmZmVyZW5jZSBCdWlsZGluZyB0aGUgcGxvdCBpbiB0d28gaGFsdmVzIHNvIHRoYXQgaXQgY2FuIGZpdCBpbiBvdXIgbWFudXNjcmlwdCAgIA0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZScsIGVjaG89RkFMU0V9DQpwbG90X3N3X2RhdGEgPC0gdW5kZXJfbGV2ZWxzICU+JSANCiAgZHBseXI6OmZpbHRlcighaXMubmEoZmRpZmZfbWVkX3N3KSkgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShkZXNjKGZkaWZmX21lZF9zdykpICU+JSANCiAgZHBseXI6Om11dGF0ZSgNCiAgICBvcmRlciA9IDE6bnJvdyguKSwNCiAgICBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCA9IHNlbnRlbmNlX2Nhc2UoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpLA0KICAgIGNvbXBvdW5kX25hbWVfc2hvcnQgPSBpZl9lbHNlKA0KICAgICAgbmNoYXIoYXMuY2hhcmFjdGVyKGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSkgPiAxMywgDQogICAgICBzdHJfdHJ1bmMoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQsIDE2LCBlbGxpcHNpcyA9ICIuLi4iKSwgDQogICAgICBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZA0KICAgICksDQogICAgY29tcG91bmRfbmFtZV9zaG9ydCA9IGZhY3Rvcihjb21wb3VuZF9uYW1lX3Nob3J0LCBsZXZlbHMgPSByZXYoY29tcG91bmRfbmFtZV9zaG9ydCkpLCAjIEZpeGVkIHRoZSBwYXJlbnRoZXNlcw0KICAgIGVpcGFhYl9uX2NhdCA9IGNhc2Vfd2hlbigNCiAgICBlaXBhYWJfbiA9PSAxIH4gIjEiLA0KICAgIGVpcGFhYl9uID49IDIgJiBlaXBhYWJfbiA8PSA1IH4gIjIgdG8gNSIsDQogICAgZWlwYWFiX24gPj0gNiAmIGVpcGFhYl9uIDw9IDEwIH4gIjYgdG8gMTAiLA0KICAgIGVpcGFhYl9uID4gMTAgfiAiMTArIiwNCiAgICBUUlVFIH4gIkVSUk9SIiAgIyBUaGlzIGNhdGNoZXMgdW5leHBlY3RlZCB2YWx1ZXMNCiAgICApLA0KICAgIGVpcGFhYl9uX2NhdCA9IGZhY3RvcihlaXBhYWJfbl9jYXQsIGxldmVscyA9IGMoIjEiLCAiMiB0byA1IiwgIjYgdG8gMTAiLCAiMTArIikpDQogICkNCg0KdmxpbmVfZGF0YV9sZWZ0IDwtIGRhdGEuZnJhbWUoDQogIHhpbnRlcmNlcHQgPSAxMF4oLTE6MyksICAjIEluY2x1ZGUgMC4xICgxMF4tMSkNCiAgeGxhYnMgPSBwYXN0ZTAoIngiLCBhcy5jaGFyYWN0ZXIoMTBeKC0xOjMpKSkgICMgRm9ybWF0IGxhYmVscyBpbiBzY2llbnRpZmljIG5vdGF0aW9uDQopDQoNCnZsaW5lX2RhdGFfcmlnaHQgPC0gZGF0YS5mcmFtZSgNCiAgeGludGVyY2VwdCA9IDEwXigzOjgpLCAgIyBVc2UgcmF3IHZhbHVlcyAobm90IGxvZy10cmFuc2Zvcm1lZCkNCiAgeGxhYnMgPSBwYXN0ZTAoIngiLCBhcy5jaGFyYWN0ZXIoMTBeKDM6OCkpKSAgIyBLZWVwIGxhYmVscyBhcyBzY2llbnRpZmljIG5vdGF0aW9uDQopDQoNCm1lZGlhbiA8LSBjb25jX3N1bW1hcnlfb3ZlcmFsbCAlPiUgDQogIGRwbHlyOjpwdWxsKGZkaWZmX21lZF9zdykgJT4lIA0KICBsb2coKQ0KDQpuX2hhbGYgPC0gY2VpbGluZyhucm93KHBsb3Rfc3dfZGF0YSkgLyAyKQ0KDQpwbG90X3N3X2RhdGFfcmlnaHQ8LSBwbG90X3N3X2RhdGEgJT4lIA0KICBkcGx5cjo6c2xpY2UoMTpuX2hhbGYpDQoNCnBsb3Rfc3dfZGF0YV9sZWZ0IDwtIHBsb3Rfc3dfZGF0YSAlPiUgDQogIGRwbHlyOjpzbGljZSgobl9oYWxmICsgMSk6bnJvdyhwbG90X3N3X2RhdGEpKQ0KDQpwbG90X2xlZnQgPC0gcGxvdF9zd19kYXRhX2xlZnQgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBmZGlmZl9tZWRfc3csIHkgPSBjb21wb3VuZF9uYW1lX3Nob3J0LCBmaWxsID0gZWlwYWFiX25fY2F0KSkgKyAgDQogIGdlb21fdmxpbmUoZGF0YSA9IHZsaW5lX2RhdGFfbGVmdCwgYWVzKHhpbnRlcmNlcHQgPSB4aW50ZXJjZXB0KSwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3VyID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB2bGluZV9kYXRhX2xlZnQsIGFlcyh4ID0geGludGVyY2VwdCwgeSA9ICJBbWl0cmlwdHlsaW5lIiwgDQogICAgICAgICAgICAgbGFiZWwgPSB4bGFicyksIGluaGVyaXQuYWVzID0gRkFMU0UsIGFuZ2xlID0gOTAsIA0KICAgICAgICAgICAgIHZqdXN0ID0gLTAuNSwgaGp1c3QgPSAxLCBzaXplID0gMykgKw0KICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBzaGFwZSA9IDIxLCBzaXplID0gMikgKw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICANCiAgIyBTZXQgZXhwbGljaXQgYnJlYWtzIGZvciBjb25zaXN0ZW50IDEwLWZvbGQgdGljayBtYXJrcw0KICBzY2FsZV94X2xvZzEwKA0KICAgIGJyZWFrcyA9IDEwXigtMTo4KSwgIA0KICAgIGxhYmVscyA9IHNjaWVudGlmaWNfZm9ybWF0KCkNCiAgKSArICANCiAgDQogIGxhYnMoDQogICAgc3VidGl0bGUgPSAiVGVzdGVkIGNvbmNlbnRyYXRpb25zIHZzIG1lZGlhbiBzdXJmYWNlIHdhdGVyIiwNCiAgICB4ID0gIk1lYW4gZm9sZCBkaWZmZXJlbmNlIG9mIHRlc3RlZCBhbmQgbWVkaWFuIHN1cmZhY2Ugd2F0ZXIgY29uY2VudHJhdGlvbnMiLA0KICAgIHkgPSAiQ29tcG91bmRzIiwNCiAgICBmaWxsID0gIkVJUEFBQiBkYXRhIg0KICApICsNCiAgdGhlbWUoDQogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgbGVnZW5kLmp1c3RpZmljYXRpb24gPSAicmlnaHQiDQogICkNCg0KDQpwbG90X3JpZ2h0IDwtIHBsb3Rfc3dfZGF0YV9yaWdodCAlPiUgDQogIGdncGxvdChhZXMoeCA9IGZkaWZmX21lZF9zdywgeSA9IGNvbXBvdW5kX25hbWVfc2hvcnQsIGZpbGwgPSBlaXBhYWJfbl9jYXQpKSArICANCiAgZ2VvbV92bGluZShkYXRhID0gdmxpbmVfZGF0YV9yaWdodCwgYWVzKHhpbnRlcmNlcHQgPSB4aW50ZXJjZXB0KSwgDQogICAgICAgICAgICAgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3VyID0gImJsYWNrIiwgYWxwaGEgPSAwLjUpICsNCiAgZ2VvbV90ZXh0KGRhdGEgPSB2bGluZV9kYXRhX3JpZ2h0LCBhZXMoeCA9IHhpbnRlcmNlcHQsIHkgPSAiUmlzcGVyaWRvbmUiLCANCiAgICAgICAgICAgICBsYWJlbCA9IHhsYWJzKSwgaW5oZXJpdC5hZXMgPSBGQUxTRSwgYW5nbGUgPSA5MCwgDQogICAgICAgICAgICAgdmp1c3QgPSAtMC41LCBoanVzdCA9IDEsIHNpemUgPSAzKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNoYXBlID0gMjEsIHNpemUgPSAyKSArDQogIA0KICAjIFJlbW92ZSB0aWNrIGF0IDEwMC1mb2xkIGRpZmZlcmVuY2UgKDEwXjIpIHdoaWxlIGtlZXBpbmcgMTAtZm9sZCBzdGVwcw0KICBzY2FsZV94X2xvZzEwKA0KICAgIGJyZWFrcyA9IGMoMTBeKC0xOjEpLCAxMF4oMzo4KSksICANCiAgICBsYWJlbHMgPSBzY2llbnRpZmljX2Zvcm1hdCgpDQogICkgKyAgDQogIGxhYnMoDQogICAgeCA9ICJNZWFuIGZvbGQgZGlmZmVyZW5jZSBvZiB0ZXN0ZWQgYW5kIG1lZGlhbiBzdXJmYWNlIHdhdGVyIGNvbmNlbnRyYXRpb25zIiwNCiAgICB5ID0gIkNvbXBvdW5kcyIsDQogICAgZmlsbCA9ICJFSVBBQUIgZGF0YSINCiAgKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICANCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KYGBgDQoNCg0KKipGaWcgMioqIFRoZSBtZWRpYW4gZm9sZCBkaWZmZXJlbmNlIGluIHRlc3RlZCBjb25jZW50cmF0aW9ucyBpbiBFSVBBQUIgYW5kIG1lZGlhbiBzdXJmYWNlIHdhdGVyIGNvbmNlbnRyYXRpb24gKGkuZS4gdGVzdGVkIGRvc2UgLyBtZWRpYW4gc3VyZmFjZSB3YXRlciBjb25jZW50cmF0aW9uKSANCg0KYGBge3IsIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTV9DQpwbG90X2xlZnQNCmBgYA0KDQpgYGB7ciwgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9NX0NCnBsb3RfcmlnaHQNCmBgYA0KDQoNCioqKk5vdGU6KioqICp0aGUgc2l6ZSBvZiB0aGUgcG9pbnRzIHJlcHJlc2VudCB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2UgaW4gRUlQQUFCLCBhbmQgY29sb3VyIG9mIHRoZSBwb2ludCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgZGV0ZWN0aW9ucyBpbiB0aGUgZW52aXJvbm1lbnRhbCBkYXRhYmFzZXMqIA0KDQoNClNhdmUgdGhlIGZpZ3VyZQ0KDQpgYGB7cn0NCmludmlzaWJsZShmaWdfMiA8LSBwbG90X2xlZnQgfCBwbG90X3JpZ2h0KQ0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZpZ193ZCwgIi4vZmlndXJlLTIucGRmIiksDQogICAgICAgcGxvdCA9IGZpZ18yLA0KICAgICAgIHdpZHRoID0gMjEwLA0KICAgICAgIGhlaWdodCA9IDI5Ny8xLjI1LCAgIyBTcGVjaWZ5IHRoZSBoZWlnaHQgb2YgdGhlIHBsb3QNCiAgICAgICB1bml0cyA9ICJtbSIpDQpgYGANCg0KRm9yIHN1cmZhY2Ugd2F0ZXJzIHRoZXJlIHdlcmUgMTM5IGNvbXBvdW5kcyANCg0KYGBge3J9DQpucm93KHBsb3Rfc3dfZGF0YSkNCmBgYA0KDQoNCiMjIyMgV2FzdGUgd2F0ZXIgDQoNCiMjIyMjIEZpZyBTMg0KDQpMZXQncyBwbG90IG1lYW4tZm9sZCBkaWZmZXJlbmNlIGZvIGVmZmx1ZW50ICAgIA0KDQpCdWlsZGluZyB0aGUgcGxvdCBpbiB0d28gaGFsdmVzIHNvIHRoYXQgaXQgY2FuIGZpdCBpbiBvdXIgbWFudXNjcmlwdCAgIA0KDQpgYGB7ciwgcmVzdWx0cz0naGlkZSd9DQpwbG90X2VmX2RhdGEgPC0gdW5kZXJfbGV2ZWxzICU+JSANCiAgZHBseXI6OmZpbHRlcighaXMubmEoZmRpZmZfbWVkX2VmKSkgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShkZXNjKGZkaWZmX21lZF9lZikpICU+JSANCiAgZHBseXI6Om11dGF0ZSgNCiAgICBvcmRlciA9IDE6bnJvdyguKSwNCiAgICBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCA9IHNlbnRlbmNlX2Nhc2UoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpLA0KICAgIGNvbXBvdW5kX25hbWVfc2hvcnQgPSBpZl9lbHNlKA0KICAgICAgbmNoYXIoYXMuY2hhcmFjdGVyKGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSkgPiAxMywgDQogICAgICBzdHJfdHJ1bmMoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQsIDE2LCBlbGxpcHNpcyA9ICIuLi4iKSwgDQogICAgICBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZA0KICAgICksDQogICAgY29tcG91bmRfbmFtZV9zaG9ydCA9IGZhY3Rvcihjb21wb3VuZF9uYW1lX3Nob3J0LCBsZXZlbHMgPSByZXYoY29tcG91bmRfbmFtZV9zaG9ydCkpLCAjIEZpeGVkIHRoZSBwYXJlbnRoZXNlcw0KICAgIGNvbXBvdW5kX25hbWVfc2hvcnQgPSBmYWN0b3IoY29tcG91bmRfbmFtZV9zaG9ydCwgbGV2ZWxzID0gcmV2KGNvbXBvdW5kX25hbWVfc2hvcnQpKSwgIyBGaXhlZCB0aGUgcGFyZW50aGVzZXMNCiAgICBlaXBhYWJfbl9jYXQgPSBjYXNlX3doZW4oDQogICAgICBlaXBhYWJfbiA9PSAxIH4gIjEiLA0KICAgICAgZWlwYWFiX24gPj0gMiAmIGVpcGFhYl9uIDw9IDUgfiAiMiB0byA1IiwNCiAgICAgIGVpcGFhYl9uID49IDYgJiBlaXBhYWJfbiA8PSAxMCB+ICI2IHRvIDEwIiwNCiAgICAgIGVpcGFhYl9uID4gMTAgfiAiMTArIiwNCiAgICAgIFRSVUUgfiAiRVJST1IiICAjIFRoaXMgY2F0Y2hlcyB1bmV4cGVjdGVkIHZhbHVlcw0KICAgICksDQogICAgZWlwYWFiX25fY2F0ID0gZmFjdG9yKGVpcGFhYl9uX2NhdCwgbGV2ZWxzID0gYygiMSIsICIyIHRvIDUiLCAiNiB0byAxMCIsICIxMCsiKSkNCiAgKQ0KDQp2bGluZV9kYXRhX2xlZnQgPC0gZGF0YS5mcmFtZSh4aW50ZXJjZXB0ID0gbG9nKDEwXigtMToyKSksDQogICAgICAgICAgICAgICAgICAgICAgICAgeGxhYnMgPSBhcy5jaGFyYWN0ZXIoMTBeKC0xOjIpKSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKHhsYWJzID0gcGFzdGUwKCLDlyIseGxhYnMpKQ0KDQp2bGluZV9kYXRhX3JpZ2h0IDwtIGRhdGEuZnJhbWUoeGludGVyY2VwdCA9IGxvZygxMF4oMjo3KSksDQogICAgICAgICAgICAgICAgICAgICAgICAgeGxhYnMgPSBhcy5jaGFyYWN0ZXIoMTBeKDI6NykpKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoeGxhYnMgPSBwYXN0ZTAoIsOXIix4bGFicykpDQoNCg0Kbl9oYWxmIDwtIGNlaWxpbmcobnJvdyhwbG90X2VmX2RhdGEpIC8gMikNCg0KcGxvdF9lZl9kYXRhX3JpZ2h0IDwtIHBsb3RfZWZfZGF0YSAlPiUgDQogIGRwbHlyOjpzbGljZSgxOm5faGFsZikNCg0KcGxvdF9lZl9kYXRhX2xlZnQgPC0gcGxvdF9lZl9kYXRhICU+JSANCiAgZHBseXI6OnNsaWNlKChuX2hhbGYgKyAxKTpucm93KHBsb3RfZWZfZGF0YSkpDQoNCnBsb3RfbGVmdCA8LSBwbG90X2VmX2RhdGFfbGVmdCAlPiUgDQogIGdncGxvdChhZXMoeCA9IGxvZyhmZGlmZl9tZWRfZWYpLCB5ID0gY29tcG91bmRfbmFtZV9zaG9ydCwgZmlsbCA9IGVpcGFhYl9uX2NhdCkpICsNCiAgZ2VvbV92bGluZShkYXRhID0gdmxpbmVfZGF0YV9sZWZ0LCBhZXMoeGludGVyY2VwdCA9IHhpbnRlcmNlcHQpLCANCiAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvdXIgPSAiYmxhY2siLCBhbHBoYSA9IDAuNSkgKw0KICBnZW9tX3RleHQoZGF0YSA9IHZsaW5lX2RhdGFfbGVmdCwgYWVzKHggPSB4aW50ZXJjZXB0LCB5ID0gIkFtaXRyaXB0eWxpbmUiLCBsYWJlbCA9IHhsYWJzKSwgDQogICAgICAgICAgICBpbmhlcml0LmFlcyA9IEZBTFNFLCBhbmdsZSA9IDkwLCB2anVzdCA9IC0wLjUsIGhqdXN0ID0gMSwgc2l6ZSA9IDMpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2hhcGUgPSAyMSwgc2l6ZSA9IDIpICsNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgDQogICMgQWRqdXN0ZWQgbG9nIHNjYWxlIHdpdGggMTAtZm9sZCBpbmNyZW1lbnRzDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBicmVha3MgPSBsb2coMTBeKC0xOjIpKSwgICMgRXhwbGljaXRseSBzZXQgbG9nLXNjYWxlIGJyZWFrcw0KICAgIGxhYmVscyA9IGZ1bmN0aW9uKHgpIGZvcm1hdChleHAoeCksIHNjaWVudGlmaWMgPSBUUlVFKQ0KICApICsNCg0KICBsYWJzKA0KICAgIHN1YnRpdGxlID0gIlRlc3RlZCBjb25jZW50cmF0aW9ucyB2cyBtZWRpYW4gd2FzdGUgd2F0ZXIiLA0KICAgIHggPSAiTG9nIG1lYW4gZm9sZCBkaWZmZXJlbmNlIG9mIHRlc3RlZCBhbmQgbWVkaWFuIHdhc3RlIHdhdGVyIGNvbmNlbnRyYXRpb25zIiwNCiAgICB5ID0gIkNvbXBvdW5kcyIsDQogICAgZmlsbCA9ICJFSVBBQUIgZGF0YSINCiAgKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gInJpZ2h0Ig0KICApDQoNCg0KcGxvdF9yaWdodCA8LSBwbG90X2VmX2RhdGFfcmlnaHQgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBsb2coZmRpZmZfbWVkX2VmKSwgeSA9IGNvbXBvdW5kX25hbWVfc2hvcnQsIGZpbGwgPSBlaXBhYWJfbl9jYXQpKSArDQogIGdlb21fdmxpbmUoZGF0YSA9IHZsaW5lX2RhdGFfcmlnaHQsIGFlcyh4aW50ZXJjZXB0ID0geGludGVyY2VwdCksIA0KICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG91ciA9ICJibGFjayIsIGFscGhhID0gMC41KSArDQogIGdlb21fdGV4dChkYXRhID0gdmxpbmVfZGF0YV9yaWdodCwgYWVzKHggPSB4aW50ZXJjZXB0LCB5ID0gIlJpc3Blcmlkb25lIiwgbGFiZWwgPSB4bGFicyksIA0KICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBGQUxTRSwgYW5nbGUgPSA5MCwgdmp1c3QgPSAtMC41LCBoanVzdCA9IDEsIHNpemUgPSAzKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUsIHNoYXBlID0gMjEsIHNpemUgPSAyKSArDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIA0KICAjIEFkanVzdGVkIGxvZyBzY2FsZTogcmVtb3ZlcyAxMF4yIHdoaWxlIGtlZXBpbmcgMTBeMyB0byAxMF43DQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBicmVha3MgPSBsb2coYygxMF4oMzo3KSkpLCAgIyBFeHBsaWNpdGx5IHNldHRpbmcgdGlja3Mgd2l0aG91dCAxMF4yDQogICAgbGFiZWxzID0gZnVuY3Rpb24oeCkgZm9ybWF0KGV4cCh4KSwgc2NpZW50aWZpYyA9IFRSVUUpDQogICkgKw0KDQogIHRoZW1lKA0KICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksICAjIFJlbW92ZXMgYXhpcyBsYWJlbHMNCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSINCiAgKQ0KYGBgDQoNCioqRmlnIFMyKio6IFRoZSBtZWRpYW4gZm9sZCBkaWZmZXJlbmNlIGluIHRlc3RlZCBjb25jZW50cmF0aW9ucyBpbiBFSVBBQUIgYW5kIG1lZGlhbiB3YXN0ZSB3YXRlciBjb25jZW50cmF0aW9uIChpLmUuIHRlc3RlZCBkb3NlIC8gbWVkaWFuIHdhc3RlIHdhdGVyIGNvbmNlbnRyYXRpb24pIA0KDQpgYGB7ciwgZmlnLmhlaWdodD05LCBmaWcud2lkdGg9NX0NCnBsb3RfbGVmdA0KYGBgDQoNCg0KYGBge3IsIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTV9DQpwbG90X3JpZ2h0DQpgYGANCg0KDQoqKipOb3RlOioqKiAqdGhlIHNpemUgb2YgdGhlIHBvaW50cyByZXByZXNlbnQgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlIGluIEVJUEFBQiwgYW5kIGNvbG91ciBvZiB0aGUgcG9pbnQgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIGRldGVjdGlvbnMgaW4gdGhlIGVudmlyb25tZW50YWwgZGF0YWJhc2VzKiANCg0KDQpTYXZlIHRoZSBmaWd1cmUNCg0KYGBge3J9DQppbnZpc2libGUoZmlnX3MyIDwtIHBsb3RfbGVmdCB8IHBsb3RfcmlnaHQpDQpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoZmlnX3dkLCAiLi9maWd1cmUtczIucGRmIiksDQogICAgICAgcGxvdCA9IGZpZ19zMiwNCiAgICAgICB3aWR0aCA9IDIxMCwNCiAgICAgICBoZWlnaHQgPSAyOTcvMS4yNSwNCiAgICAgICB1bml0cyA9ICJtbSIpDQpgYGANCg0KIyMjIFN1bW1hcnkgdGFibGUNCg0KTG9va2luZyBhdCB0aGUgbnVtYmVyIG9mIGNvbXBvdW5kcyBpbiB0aGUgZGF0YWJhc2UgdGhhdCBoYXZlIGRhdGEgYXQgY29uY2VudHJhdGlvbnMgYmVsb3cgbWVkaWFuIGFuZCB1cHBlciA5NSBDSQ0KDQpUaGUgcGVyY2VudGFnZSBvZiBjb21wb3VuZHMgaW4gdGhlIEVJUEFBQiBkYXRhYmFzZSB0aGF0IGhhdmUgZGF0YSBhdCBjb25jZW50cmF0aW9ucyBsZXNzIHRoZW4gbWVkaWFuIGFuZCB1cHBlciA5NSUgY3JlZGlibGUgaW50ZXJ2YWwgb2Ygc3VyZmFjZSB3YXRlciBhbmQgd2FzdGV3YXRlciBjb25jZW50cmF0aW9ucw0KDQpgYGB7cn0NCmNvbXBvdW5kX3N1bV90YWJsZV9zdXJmIDwtIHVuZGVyX2xldmVscyAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKHRvdGFsX2NvbXBvdW5kcyA9ICBzdW0oIWlzLm5hKHByb3BfbGVzc19tZWRfc3cpKSwNCiAgICAgICAgICAgICAgICAgY291bnRfbGVzc19tZWRfc3cgPSBzdW0ocHJvcF9sZXNzX21lZF9zdyA9PSAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBjb3VudF9sZXNzX2hpY19zdyA9IHN1bShwcm9wX2xlc3NfaGljX3N3ID09IDAsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgIHBlcmNlbnRfbGVzc19tZWRfc3cgPSAoY291bnRfbGVzc19tZWRfc3cvdG90YWxfY29tcG91bmRzKSoxMDAsDQogICAgICAgICAgICAgICAgIHBlcmNlbnRfbGVzc19oaWNfc3cgPSAoY291bnRfbGVzc19oaWNfc3cvdG90YWxfY29tcG91bmRzKSoxMDAsDQogICAgICAgICAgICAgICAgICJQZXJjZW50IHdpdGggbm8gZGF0YSB1bmRlciBtZWRpYW4gY29uY2VudHJhdGlvbiIgPSBwYXN0ZTAocm91bmQocGVyY2VudF9sZXNzX21lZF9zdywgMiksICIlIiwgIiAoIiwgY291bnRfbGVzc19tZWRfc3csICIgb2YgIiwgdG90YWxfY29tcG91bmRzLCAiKSIpLA0KICAgICAgICAgICAgICAgICAiUGVyY2VudCB3aXRoIG5vIGRhdGEgdW5kZXIgdXBwZXIgOTUgQ0kgY29uY2VudHJhdGlvbiIgPSBwYXN0ZTAocm91bmQocGVyY2VudF9sZXNzX2hpY19zdywgMiksICIlIiwgIiAoIiwgY291bnRfbGVzc19oaWNfc3csICIgb2YgIiwgdG90YWxfY29tcG91bmRzLCAiKSIpDQogICAgICAgICAgICAgICAgICkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKE1hdHJpeCA9ICJTdXJmYWNlIHdhdGVyIikgJT4lIA0KICBkcGx5cjo6c2VsZWN0KE1hdHJpeCwgICJQZXJjZW50IHdpdGggbm8gZGF0YSB1bmRlciBtZWRpYW4gY29uY2VudHJhdGlvbiIsICJQZXJjZW50IHdpdGggbm8gZGF0YSB1bmRlciB1cHBlciA5NSBDSSBjb25jZW50cmF0aW9uIikNCg0KY29tcG91bmRfc3VtX3RhYmxlX3dhc3RlIDwtIHVuZGVyX2xldmVscyAlPiUgDQogIGRwbHlyOjpyZWZyYW1lKHRvdGFsX2NvbXBvdW5kcyA9ICBzdW0oIWlzLm5hKHByb3BfbGVzc19tZWRfZWYpKSwNCiAgICAgICAgICAgICAgICAgY291bnRfbGVzc19tZWRfZWYgPSBzdW0ocHJvcF9sZXNzX21lZF9lZiA9PSAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBjb3VudF9sZXNzX2hpY19lZiA9IHN1bShwcm9wX2xlc3NfaGljX2VmID09IDAsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgIHBlcmNlbnRfbGVzc19tZWRfZWYgPSAoY291bnRfbGVzc19tZWRfZWYvdG90YWxfY29tcG91bmRzKSoxMDAsDQogICAgICAgICAgICAgICAgIHBlcmNlbnRfbGVzc19oaWNfZWYgPSAoY291bnRfbGVzc19oaWNfZWYvdG90YWxfY29tcG91bmRzKSoxMDAsDQogICAgICAgICAgICAgICAgICJQZXJjZW50IHdpdGggbm8gZGF0YSB1bmRlciBtZWRpYW4gY29uY2VudHJhdGlvbiIgPSBwYXN0ZTAocm91bmQocGVyY2VudF9sZXNzX21lZF9lZiwgMiksICIlIiwgIiAoIiwgY291bnRfbGVzc19tZWRfZWYsICIgb2YgIiwgdG90YWxfY29tcG91bmRzLCAiKSIpLA0KICAgICAgICAgICAgICAgICAiUGVyY2VudCB3aXRoIG5vIGRhdGEgdW5kZXIgdXBwZXIgOTUgQ0kgY29uY2VudHJhdGlvbiIgPSBwYXN0ZTAocm91bmQocGVyY2VudF9sZXNzX2hpY19lZiwgMiksICIlIiwgIiAoIiwgY291bnRfbGVzc19oaWNfZWYsICIgb2YgIiwgdG90YWxfY29tcG91bmRzLCAiKSIpDQogICAgICAgICAgICAgICAgICkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKE1hdHJpeCA9ICJXYXN0ZXdhdGVyIikgJT4lIA0KICBkcGx5cjo6c2VsZWN0KE1hdHJpeCwgICJQZXJjZW50IHdpdGggbm8gZGF0YSB1bmRlciBtZWRpYW4gY29uY2VudHJhdGlvbiIsICJQZXJjZW50IHdpdGggbm8gZGF0YSB1bmRlciB1cHBlciA5NSBDSSBjb25jZW50cmF0aW9uIikNCg0KY29tcG91bmRfc3VtX3RhYmxlIDwtICByYmluZChjb21wb3VuZF9zdW1fdGFibGVfc3VyZiwgY29tcG91bmRfc3VtX3RhYmxlX3dhc3RlKQ0KDQpjb21wb3VuZF9zdW1fdGFibGUgJT4lIA0KICBndCgpICU+JSANCiAgZm10X251bWJlcigNCiAgICBjb2x1bW5zID0gYygiUGVyY2VudCB3aXRoIG5vIGRhdGEgdW5kZXIgbWVkaWFuIGNvbmNlbnRyYXRpb24iLCAiUGVyY2VudCB3aXRoIG5vIGRhdGEgdW5kZXIgdXBwZXIgOTUgQ0kgY29uY2VudHJhdGlvbiIpLCANCiAgICBkZWNpbWFscyA9IDANCiAgKSAlPiUgDQogIGNvbHNfYWxpZ24oDQogICAgYWxpZ24gPSAiY2VudGVyIiwgDQogICAgY29sdW1ucyA9IGV2ZXJ5dGhpbmcoKQ0KICApDQpgYGANCg0KDQojIyBPY2N1cmFuY2UNCg0KIyMjIE9jY3VyYW5jZSBzdW1tYXJ5IGRhdGEgDQoNCk1ha2luZyBhIHN1bW1hcnkgZGF0YSBmcmFtZSBmb3IgZWFjaCBjb21wb3VuZCwgKiphbGxfc3VyZmFjZXdhdGVyKiosIHRoYXQgaW5jbHVkZXM6ICgxKSB0aGUgbnVtYmVyIG9mIHNhbXBsZXMsICgyKSB0aGUgbnVtYmVyIG9mIHBvc2l0aXZlIGRldGVjdGlvbnMgKGJhc2VkIG9uIHNpbmdsZSBzYW1wbGVzLCBub3Qgc3VtbWFyeSBzdGF0aXN0aWNzKSwgKDMpIHRoZSByYW5rIG9yZGVyIG9mIGRldGVjdGlvbnMsICg0KSB0aGUgcGVyY2VudGFnZSBvZiBwb3NpdGl2ZSBkZXRlY3Rpb25zIChvbmx5IGZvciBjb21wb3VuZHMgd2l0aCBtb3JlIHRoZW4gMTAgc2FtcGxlIHVuaXRzKSwgYW5kLCAoNSkgdGhlIHJhbmsgb3JkZXIgb2YgcGVyY2VudGFnZSBvZiBwb3NpdGl2ZSBkZXRlY3Rpb24uICANCg0KYGBge3J9DQphbGxfc3VyZmFjZXdhdGVyIDwtIGFsbF9kYXRhYmFzZXMgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNvdXJjZSAhPSAiZWlwYWFiIiwgbWF0cml4X2dyb3VwID09ICJzdXJmYWNld2F0ZXIiKSAlPiUgDQogIGRwbHlyOjpncm91cF9ieShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkgJT4lIA0KICBkcGx5cjo6cmVmcmFtZShjb21iaW5kX3NhbXBsZXMgPSBzdW0obl91bml0c19lc3QsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgIGNvbWJpbmRfcm93cyA9IGxlbmd0aCh1bmlxdWVfcm93X2lkKSwNCiAgICAgICAgICAgICAgICAgY29tYmluZF9zaW5nbGVzID0gc3VtKGNvbmNlbnRyYXRpb25fdHlwZSA9PSAic2luZ2xlIiwgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgICAgICAgY29tYmluZF9kZXRlY3Rpb24gPSBzdW0odmFsdWUgPiAwLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBjb21iaW5kX3NpbmdsZV9kZXRlY3Rpb24gPSBzdW0odmFsdWUgPiAwICYgY29uY2VudHJhdGlvbl90eXBlID09ICJzaW5nbGUiLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgICAgICAgICBjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uID0gaWZfZWxzZShjb21iaW5kX3NpbmdsZXMgPiAxMCwgKGNvbWJpbmRfc2luZ2xlX2RldGVjdGlvbi9jb21iaW5kX3NpbmdsZXMpKjEwMCwgTkEpDQogICAgICAgICAgICAgICAgICkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKA0KICAgIGNvbWJpbmRfcmFua19zYW1wbGVzID0gcmFuaygtY29tYmluZF9zYW1wbGVzLCB0aWVzLm1ldGhvZCA9ICJtYXgiLCBuYS5sYXN0ID0gImtlZXAiKSwNCiAgICBjb21iaW5kX3JhbmtfZGV0ZWN0aW9uID0gcmFuaygtY29tYmluZF9kZXRlY3Rpb24sIHRpZXMubWV0aG9kID0gIm1heCIsIG5hLmxhc3QgPSAia2VlcCIpLA0KICAgIGNvbWJpbmRfcmFua19kZXRlY3Rpb25fcGVyY2VudCA9IHJhbmsoLWNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24sIHRpZXMubWV0aG9kID0gIm1heCIsIG5hLmxhc3QgPSAia2VlcCIpDQogICkNCmBgYA0KDQpUaGVyZSdzIGEgdG90YWwgb2YgMTY1MCBjb21wb3VuZHMgaW4gdGhpcyBzdXJmYWNlIHdhdGVyIHN1bW1hcnkgICANCg0KDQpgYGB7cn0NCmFsbF9zdXJmYWNld2F0ZXIgJT4lIA0KICBkcGx5cjo6ZGlzdGluY3QoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpICU+JSANCiAgbnJvdyguKQ0KYGBgDQoNCk1ha2luZyBvY2N1cnJlbmNlIGNvdW50IGluIEVJUEFBQiwg8J+TiiAqKmVpcGFhYl9jb3VudHMqKi4gICANCg0KYGBge3J9DQphbGxfc3VyZmFjZXdhdGVyX2xpc3QgPC0gYWxsX3N1cmZhY2V3YXRlciAlPiUgDQogIGRwbHlyOjpkaXN0aW5jdChjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkgJT4lIA0KICBkcGx5cjo6cHVsbChjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkNCg0KZWlwYWFiX2NvdW50cyA8LSBhbGxfZGF0YWJhc2VzICU+JSANCiAgZHBseXI6OmZpbHRlcihzb3VyY2UgPT0gImVpcGFhYiIgJiBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCAlaW4lIGFsbF9zdXJmYWNld2F0ZXJfbGlzdCkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpICU+JSANCiAgZHBseXI6OnJlZnJhbWUoZWlwYWFiX24gPSBzdW0obl91bml0c19lc3QsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgIGVpcGFhYl9uX2xvZyA9IGxvZyhlaXBhYWJfbikpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZHBseXI6Om11dGF0ZShlaXBhYWJfcmFuayA9IHJhbmsoLWVpcGFhYl9uLCB0aWVzLm1ldGhvZCA9ICJtYXgiLCBuYS5sYXN0ID0gImtlZXAiKSkgJT4lIA0KICB0aWR5cjo6Y29tcGxldGUoLiwgY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpDQpgYGANCg0KQ29tYmluaW5nIHRoZSBkYXRhIHRvZ2V0aGVyIHRvIG1ha2Ug8J+TiiAqKnRlc3Rfc3VyZmFjZXdhdGVyKiogICANCg0KDQpgYGB7cn0NCnRlc3Rfc3VyZmFjZXdhdGVyIDwtIGxlZnRfam9pbihhbGxfc3VyZmFjZXdhdGVyLCBlaXBhYWJfY291bnRzLCBieSA9ICJjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCIpICU+JSANCiAgZHBseXI6Om11dGF0ZShyYW5rX3NhbXBsZXNfZGlmID0gZWlwYWFiX3JhbmstY29tYmluZF9yYW5rX3NhbXBsZXMsDQogICAgICAgICAgICAgICAgcmFua19zYW1wbGVzX2RpZiA9IGlmX2Vsc2UoaXMubmEoZWlwYWFiX24pLCBOQSwgIHJhbmtfc2FtcGxlc19kaWYpLA0KICAgICAgICAgICAgICAgIHJhbmtfZGV0ZWN0aW9uX2RpZiA9IGVpcGFhYl9yYW5rLWNvbWJpbmRfcmFua19kZXRlY3Rpb24sDQogICAgICAgICAgICAgICAgcmFua19kZXRlY3Rpb25fcGVyY2VudF9kaWYgPSBlaXBhYWJfcmFuay1jb21iaW5kX3JhbmtfZGV0ZWN0aW9uX3BlcmNlbnQNCiAgICAgICAgICAgICAgICApICU+JSANCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhyYW5rX2RldGVjdGlvbl9kaWYpLCBkZXNjKGNvbWJpbmRfZGV0ZWN0aW9uKSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKHJhbmtfZGV0ZWN0aW9uX2RpZiA9IDE6bnJvdyguKSkgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShkZXNjKHJhbmtfZGV0ZWN0aW9uX3BlcmNlbnRfZGlmKSwgZGVzYyhjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uKSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKHJhbmtfZGV0ZWN0aW9uX3BlcmNlbnRfZGlmID0gMTpucm93KC4pKQ0KDQp1YmFfcmFua19kZXRlY3Rpb25fbWF4IDwtIHRlc3Rfc3VyZmFjZXdhdGVyICU+JSANCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb21iaW5kX3JhbmtfZGV0ZWN0aW9uKSkgJT4lIA0KICBkcGx5cjo6c2xpY2UoMSkgJT4lIA0KICBkcGx5cjo6cHVsbChjb21iaW5kX3JhbmtfZGV0ZWN0aW9uKQ0KDQp1YmFfcmFua19kZXRlY3Rpb25fcGVyY2VudF9tYXggPC0gdGVzdF9zdXJmYWNld2F0ZXIgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShkZXNjKGNvbWJpbmRfcmFua19kZXRlY3Rpb25fcGVyY2VudCkpICU+JSANCiAgZHBseXI6OnNsaWNlKDEpICU+JSANCiAgZHBseXI6OnB1bGwoY29tYmluZF9yYW5rX2RldGVjdGlvbl9wZXJjZW50KQ0KYGBgDQoNCkZpZ3VyaW5nIG91dCB3aGF0IHRoZSBtYXggcmFuayBvY2N1cnJlbmNlIGFuZCBwb3NpdGl2ZSBkZXRlY3Rpb25zIGNvdWxkIGJlLiAgICANCg0KYGBge3J9DQp1YmFfcmFua19kZXRlY3Rpb25fbWF4IDwtIHRlc3Rfc3VyZmFjZXdhdGVyICU+JSANCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb21iaW5kX3JhbmtfZGV0ZWN0aW9uKSkgJT4lIA0KICBkcGx5cjo6c2xpY2UoMSkgJT4lIA0KICBkcGx5cjo6cHVsbChjb21iaW5kX3JhbmtfZGV0ZWN0aW9uKQ0KDQp1YmFfcmFua19kZXRlY3Rpb25fcGVyY2VudF9tYXggPC0gdGVzdF9zdXJmYWNld2F0ZXIgJT4lIA0KICBkcGx5cjo6YXJyYW5nZShkZXNjKGNvbWJpbmRfcmFua19kZXRlY3Rpb25fcGVyY2VudCkpICU+JSANCiAgZHBseXI6OnNsaWNlKDEpICU+JSANCiAgZHBseXI6OnB1bGwoY29tYmluZF9yYW5rX2RldGVjdGlvbl9wZXJjZW50KQ0KDQpwcmludChwYXN0ZTAoIlRoZSBtYXggcmFuayBpbiB0ZXJtcyBvZiB0aGUgbnVtYmVyIG9mIG9jY3VycmVuY2VzIGlzICIsIHViYV9yYW5rX2RldGVjdGlvbl9tYXgsICIuIFRoZSBtYXggcmFuayBmb3IgdGhlIHBlcmNlbnRhZ2Ugb2YgcG9zdGl2ZSBkZXRlY3Rpb25zIGlzICIsIHViYV9yYW5rX2RldGVjdGlvbl9wZXJjZW50X21heCkpDQpgYGANCg0KDQpUaGUgMTAgbW9zdCBjb21tb24gY29tcG91bmRzIGluIEVJUEFBQiB2ZXJzdXMgb2NjdXJyZW5jZSBhbmQgZGV0ZWN0aW9uIGZyZXF1ZW5jeSBpbiBVQkEgc3VyZmFjZSB3YXRlcnMNClVCQSBzYW1wbGVzLiAgDQoNCmBgYHtyfQ0KdGFibGVfdG9wX3N1cmZhY2V3YXRlciA8LSB0ZXN0X3N1cmZhY2V3YXRlciAlPiUNCiAgYXJyYW5nZShkZXNjKGVpcGFhYl9uKSkgJT4lDQogIHNsaWNlKDE6MTApICU+JQ0KICBtdXRhdGUoDQogICAgY29tcG91bmRfbmFtZSA9IHNlbnRlbmNlX2Nhc2UoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpLA0KICAgIGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24gPSByb3VuZChjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uLCAyKQ0KICApICU+JQ0KICBzZWxlY3QoDQogICAgY29tcG91bmRfbmFtZSwgZWlwYWFiX24sIGNvbWJpbmRfc2FtcGxlcywgY29tYmluZF9kZXRlY3Rpb24sDQogICAgY29tYmluZF9wZXJjZW50X2RldGVjdGlvbiwgZWlwYWFiX3JhbmssIGNvbWJpbmRfcmFua19kZXRlY3Rpb24sDQogICAgY29tYmluZF9yYW5rX2RldGVjdGlvbl9wZXJjZW50DQogICkgJT4lDQogIHJlbmFtZSgNCiAgICBOYW1lID0gY29tcG91bmRfbmFtZSwNCiAgICBgRUlQQUFCIHRlc3RzYCA9IGVpcGFhYl9uLA0KICAgIGBFbnZpcm9ubWVudGFsIHNhbXBsZXNgID0gY29tYmluZF9zYW1wbGVzLA0KICAgIGBBbGwgZGV0ZWN0aW9uc2AgPSBjb21iaW5kX2RldGVjdGlvbiwNCiAgICBgUHJlY2VudCBzaW5nbGUgZGV0ZWN0aW9uc2AgPSBjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uLA0KICAgIGBFSVBBQUIgcmFuayAoMeKAkzg3MClgID0gZWlwYWFiX3JhbmssDQogICAgYFJhbmsgdG90YWwgZGV0ZWN0aW9ucyAoMeKAkzE2NTQpYCA9IGNvbWJpbmRfcmFua19kZXRlY3Rpb24sDQogICAgYFJhbmsgIHBlcmNlbnQgZGV0ZWN0aW9ucyAoMeKAkzE0MjgpKmAgPSBjb21iaW5kX3JhbmtfZGV0ZWN0aW9uX3BlcmNlbnQNCiAgKSAlPiUNCiAgZmxleHRhYmxlKCkgJT4lDQogIGNvbGZvcm1hdF9udW0oaiA9IGMoIkVudmlyb25tZW50YWwgc2FtcGxlcyIsICJBbGwgZGV0ZWN0aW9ucyIpLCBkaWdpdHMgPSAwKSAlPiUNCiAgYWxpZ24oYWxpZ24gPSAiY2VudGVyIiwgcGFydCA9ICJhbGwiKSAlPiUNCiAgYXV0b2ZpdCgpDQoNCnRhYmxlX3RvcF9zdXJmYWNld2F0ZXINCmBgYA0KDQoqTm90ZTogdGhlIHJhbmsgZm9yIHRvdGFsIHBlcmNlbnRhZ2UgZGV0ZWN0aW9ucyBkb2VzIG5vdCByYW5nZSBmcm9tIDEgdG8gdGhlIG51bWJlciBvZiBjb21wb3VuZHMgKGkuZS4gMeKAkzE0MjgpIGJlY2F1c2UgcGVyY2VudGFnZSBkZXRlY3Rpb25zIHdlcmUgb25seSBjYWxjdWxhdGVkIHdoZXJlIHRoZSBudW1iZXIgb2Ygc2luZ2xlIHNhbXBsZXMgYXJlIGdyZWF0ZXIgdGhhbiB0ZW4qICAgDQoNClNhdmluZyB0aGUgdGFibGUgDQoNCmBgYHtyfQ0Kc2F2ZV9hc19kb2N4KHRhYmxlX3RvcF9zdXJmYWNld2F0ZXIsIHBhdGggPSBwYXN0ZTAoZmlnX3dkLCAiLi90YWJsZS1TNy5kb2N4IikpDQpgYGANCg0KIyMjIE1vZGVsaW5nDQoNCiMjIyBUb3RhbCBvY2NydWFuY2UgDQoNCkhlcmUgd2Ugd2lsbCBidWlsZCBhIHNpbXBsZSBCYXllc2lhbiBuZWdhdGl2ZSBiaW5vbWlhbCAobG9nLWxpbmspIHJlZ3Jlc3Npb24gbW9kZWwgdG8gbG9vayBhdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHR3byBwYXJhbWV0ZXJzLiANCg0KIyMjIyBNb2RlbCBzdHJ1Y3R1cmUNCg0KYGBge3J9DQp0ZXN0X3N1cmZhY2V3YXRlciAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKGVpcGFhYl9uKSkNCmBgYA0KDQoNCmBgYHtyfQ0KZGV0ZWN0aW9uX25lZ2Jpbl9zdHIgPC0gYmYoZWlwYWFiX24gfiBjb21iaW5kX2RldGVjdGlvbiwNCiAgICAgICAgICAgICAgIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCkpDQpgYGANCg0KVGhlc2UgYXJlIHRoZSBkZWZhdWx0IHByaW9ycy4gIA0KDQpgYGB7cn0NCnN1cHByZXNzV2FybmluZ3MoZ2V0X3ByaW9yKGRldGVjdGlvbl9uZWdiaW5fc3RyLCBkYXRhID0gdGVzdF9zdXJmYWNld2F0ZXIsIGZhbWlseSA9IG5lZ2Jpbm9taWFsKCkpKQ0KYGBgDQoNCiMjIyMgUnVuIG1vZGVsDQoNClJ1biBtb2RlbC4gVGhpcyBoYXMgYmVlbiBoYXNoZWQgb3V0IGFzIEkgaGF2ZSBzYXZlZCB0aGUgbW9kZWwsIGFuZCB3aWxsIHJlLWxvYWQgaXQgaW5zdGVhZCBvZiByZS1ydW5uaW5nIGl0LiANCg0KYGBge3J9DQpvcHRpb25zKGJybXMuYmFja2VuZCA9ICJjbWRzdGFuciIpDQpkZXRlY3Rpb25fbmVnYmluX21vZCA8LSBicm0oDQogIGRldGVjdGlvbl9uZWdiaW5fc3RyLA0KICBkYXRhID0gdGVzdF9zdXJmYWNld2F0ZXIsDQogIGNvcmVzID0gNCwNCiAgY2hhaW5zID0gNCwNCiAgI3ByaW9yID0gbW9kX3ByaW9ycywgI3VzZSBkZWZhdWx0cw0KICB3YXJtdXAgPSAxMDAwLA0KICBzZWVkID0gMjAyNTA0MTgsDQogIHRoaW4gPSAyLA0KICBpdGVyID0gODAwMCwNCiAgY29udHJvbCA9IGxpc3QobWF4X3RyZWVkZXB0aCA9IDIwLCBhZGFwdF9kZWx0YSA9IDAuOTUpLA0KICBzYXZlX3BhcnMgPSBzYXZlX3BhcnMoYWxsID0gVFJVRSksDQogIHNhbXBsZV9wcmlvciA9IFRSVUUsDQogIGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgJy4vZGV0ZWN0aW9uX25lZ2Jpbl9tb2QnKQ0KKQ0KYGBgDQoNCg0KIyMjIyBSZS1sb2FkIG1vZGVscw0KDQpSZWxvYWQgdGhlIG1vZGVsIGlmIG5lY2Vzc2FyeS4gIA0KDQpgYGB7cn0NCmRldGVjdGlvbl9uZWdiaW5fbW9kIDwtICByZWFkUkRTKGZpbGUgPSBwYXN0ZTAobW9kc193ZCwgIi4vZGV0ZWN0aW9uX25lZ2Jpbl9tb2QucmRzIikpDQpgYGANCg0KDQojIyMjIE1vZGVsIGRpYWdub3N0aWNzDQoNClRoZSBSMiB2YWx1ZSBpcyB2ZXJ5IGxvdywgYnV0IHNob3VsZCBiZSBmaW5lIGZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBpbnZlc3RpZ2F0aW9uLiAgDQoNCmBgYHtyfQ0KcGVyZm9ybWFuY2U6OnIyX2JheWVzKGRldGVjdGlvbl9uZWdiaW5fbW9kLCByb2J1c3QgPSBGQUxTRSwgY2kgPSAwLjk1KQ0KYGBgDQoNClRoZSBtb2RlIGdlbmVyYWxseSBmb2xsb3dzIHRoZSBvYnNlcnZlZCBkYXRhLCBidXQgaXNuJ3QgdGhlIGJlc3QgZml0LiBBZ2FpbiwgdGhpcyBzaG91bGQgYmUgZmluZSBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgaW52ZXN0aWdhdGlvbi4gIA0KDQpgYGB7cn0NCmNvbG9yX3NjaGVtZV9zZXQoInJlZCIpDQpicm1zOjpwcF9jaGVjayhkZXRlY3Rpb25fbmVnYmluX21vZCwgbmRyYXdzID0gNTAsIHR5cGUgPSAiZWNkZl9vdmVybGF5IikNCmBgYA0KDQpDaGVja2luZyBSLWhhdCB2YWx1ZXMgdG8gY29uZmlybSBtb2RlbCBjb252ZXJnZW5jZSBhbmQgRVNTIGZvIGVzdGltYXRlcy4gICANCg0KYGBge3J9DQpwcmludChzdW1tYXJ5KGRldGVjdGlvbl9uZWdiaW5fbW9kLCBwcm9iID0gMC45NSkpICNBbGwgUmhhdCA9IDEgKGdvb2QpLg0KYGBgDQoNCkRpYWdub3N0aWMgcGxvdHMgbG9vayBmaW5lLiBBIHNtYWxsZXIgc2hhcGUgdmFsdWUgaW5kaWNhdGVzIGdyZWF0ZXIgb3ZlcmRpc3BlcnNpb24gKHZhcmlhbmNlIGlzIG11Y2ggbGFyZ2VyIHRoYW4gdGhlIG1lYW4pLiAgDQoNCmBgYHtyfQ0KY29sb3Jfc2NoZW1lX3NldCgicmVkIikNCnBsb3QoZGV0ZWN0aW9uX25lZ2Jpbl9tb2QpDQpgYGANCg0KIyMjIyBNb2RlbCBlc3RpbWF0ZXMNCg0KUHVsbGluZyB0aGUgY29uZGl0aW9uYWwgZWZmZWN0IGVzdGltYXRlcyBmcm9tIHRoZSBtb2RlbC4gVGhlIGVzdGltYXRlIGNvcnJlc3BvbmRzIHRvIHRoZSBlZmZlY3Qgb2YgdGhlIHByZWRpY3RvciBvbiB0aGUgbG9nIG9mIHRoZSBleHBlY3RlZCByZXNwb25zZSB2YXJpYWJsZS4gQSAxLXVuaXQgaW5jcmVhc2UgKGkuZS4gMSBkZXRlY3Rpb24pIGluIHRoZSBudW1iZXIgb2YgZW52aXJvbm1lbnRhbCBkZXRlY3Rpb25zIGluY3JlYXNlcyB0aGUgZXhwZWN0ZWQgb2NjdXJyZW5jZSBpbiBFSVBBQUIgZGF0YWJhc2UgYnkgMC4wMTElIChpLmUuIChleHAoMC4wMDAxMTA1Njk2KS0xKcOXMTAwID0gMC4wMTEwNTc1NykuIEluIG90aGVyIHdvcmRzLCBmb3IgZWFjaCAxMDAwIGRldGVjdGlvbnMgd2Ugd291bGQgZXhwZWN0IGEgNSUgaW5jcmVhc2UgaW4gdGVzdCBpbiB0aGUgRUlQQUFCIGRhdGFiYXNlLiAgDQoNCmBgYHtyfQ0KZXNpdG1hdGVzXzMgPC0gY29uZGl0aW9uYWxfZWZmZWN0cyhkZXRlY3Rpb25fbmVnYmluX21vZCkNCmVzdGltYXRlX2RhdGFfMyA8LSBlc2l0bWF0ZXNfMyRjb21iaW5kX2RldGVjdGlvbg0KDQptb2RlbF9zdW1tYXJ5XzMgPC0gIGFzLmRhdGEuZnJhbWUoZml4ZWYoZGV0ZWN0aW9uX25lZ2Jpbl9tb2QsIHN1bW1hcnkgPSBUUlVFKSkgJT4lIA0KICByb3duYW1lc190b19jb2x1bW4oKSAlPiUgDQogIGNsZWFuX25hbWVzKCkNCg0KZXN0aW1hdGVfMyA8LSBtb2RlbF9zdW1tYXJ5XzMkZXN0aW1hdGVbMl0NCkw5NUNJXzMgPC0gbW9kZWxfc3VtbWFyeV8zJHEyXzVbMl0NCkg5NUNJXzMgPC0gbW9kZWxfc3VtbWFyeV8zJHE5N181WzJdDQplc3RpbWF0ZV9wZXJjZW50XzMgPC0gKGV4cChlc3RpbWF0ZV8zKS0xKSoxMDANCkw5NUNJX3BlcmNlbnRfMyA8LSAoZXhwKEw5NUNJXzMpLTEpKjEwMA0KSDk1Q0lfcGVyY2VudF8zIDwtIChleHAoSDk1Q0lfMyktMSkqMTAwDQoNCm1vZGVsX3N1bW1hcnlfM19mdCA8LSBtb2RlbF9zdW1tYXJ5XzMgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGFjcm9zcyh3aGVyZShpcy5kb3VibGUpLCB+IHJvdW5kKC54LCA1KSkpICU+JSANCiAgZHBseXI6OnJlbmFtZSgicHJlZGljdG9yIiA9IHJvd25hbWUpICU+JSANCiAgZHBseXI6Om11dGF0ZShDckkgPSBwYXN0ZTAocTJfNSwgIiB0byAiLCBxOTdfNSkpICU+JSANCiAgZHBseXI6OnNlbGVjdChwcmVkaWN0b3IsIGVzdGltYXRlLCBDckkpICU+JSANCiAgZmxleHRhYmxlKCkgJT4lDQogIHNldF9oZWFkZXJfbGFiZWxzKA0KICAgIHByZWRpY3RvciA9ICJQcmVkaWN0b3IiLA0KICAgIGVzdGltYXRlID0gIkVzdGltYXRlIiwNCiAgICBDckkgPSAiOTUlIENySSINCiAgKSAlPiUNCiAgYXV0b2ZpdCgpDQoNCm1vZGVsX3N1bW1hcnlfM19mdA0KYGBgDQoNCg0KYGBge3J9DQp0ZXN0X3N1cmZhY2V3YXRlciAlPiUgDQogIGZpbHRlcighaXMubmEoZWlwYWFiX24pKSAlPiUgIA0KICBkcGx5cjo6ZGlzdGluY3QoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpICU+JSANCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkpDQpgYGANCg0KDQojIyMjIyBGaWcgUzNhDQoNCkVhY2ggcGhhcm1hY2V1dGljYWwgb2NjdXJyZW5jZSBpbiB0aGUgYmVoYXZpb3VyYWwgZGF0YWJhc2UgcGxvdHRlZCBhZ2FpbnN0IHRoZSBudW1iZXIgb2YgcG9zaXRpdmUgZGV0ZWN0aW9ucyBpbiB0aGUgZW52aXJvbm1lbnRhbCBkYXRhYmFzZXMuIFRoZSBmaXZlIGNvbXBvdW5kcyBoaWdobGlnaHRlZCByZXByZXNlbnQgdGhvc2UgdGhhdCBhcmUgY3VycmVudGx5IHVuZGVycmVwcmVzZW50ZWQgaW4gdGhlIGJlaGF2aW91ciB0ZXN0IGRhdGFiYXNlIGJhc2VkIG9uIHRoZWlyIHJhbmsgZGV0ZWN0aW9ucyAoaS5lLiByYW5rIG9jY3VycmVuY2UgaW4gYmVoYXZpb3VyIHRlc3QgZGF0YWJhc2VzIHZzIHJhbmsgZGV0ZWN0aW9ucykuIFRoZSBwb2ludCBzaXplIHJlcHJlc2VudHMgdGhlIHRvdGFsIG51bWJlciBvZiBzYW1wbGVzIGFjcm9zcyBhbGwgZW52aXJvbm1lbnRhbCBkYXRhYmFzZXMuIA0KDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9Nn0NCnRleHRfY29vcmQgPC0gdGVzdF9zdXJmYWNld2F0ZXIgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGVpcGFhYl9uKSkgJT4lIA0KICBzdW1tYXJpc2UoeCA9IG1heChjb21iaW5kX2RldGVjdGlvbiwgbmEucm0gPSBUUlVFKS8yLA0KICAgICAgICAgICAgeSA9IG1heChlaXBhYWJfbiwgbmEucm0gPSBUUlVFKSkNCg0KeV9jb29yZCA8LSB0ZXh0X2Nvb3JkICU+JSBwdWxsKHkpDQoNCnhfY29vcmQgPC0gdGV4dF9jb29yZCAlPiUgcHVsbCh4KQ0KDQoNCmZpZ19zM2EgPC0gZ2dwbG90KCkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IGVzdGltYXRlX2RhdGFfMywgYWVzKHggPSBjb21iaW5kX2RldGVjdGlvbiwgeSA9IGVzdGltYXRlX18pLCBjb2xvciA9ICIjRTAxRjMzIiwgbGluZXdpZHRoID0gMSkgKw0KICBnZW9tX3JpYmJvbihkYXRhID0gZXN0aW1hdGVfZGF0YV8zLCBhZXMoeCA9IGNvbWJpbmRfZGV0ZWN0aW9uLCB5bWluID0gbG93ZXJfXywgeW1heCA9IHVwcGVyX18pLCBmaWxsID0gIiNFMDFGMzMiLCBhbHBoYSA9IDAuMSkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSB0ZXN0X3N1cmZhY2V3YXRlciAlPiUgZmlsdGVyKCFpcy5uYShlaXBhYWJfbikpLCBhZXMoeCA9IGNvbWJpbmRfZGV0ZWN0aW9uLCB5ID0gZWlwYWFiX24sIHNpemUgPSBjb21iaW5kX3NhbXBsZXMpLCBhbHBoYSA9IDAuNCwgc2hhcGUgPSAyMSwgY29sb3IgPSAiIzAxMDgwQSIsIGZpbGwgPSAiI0UwMUYzMyIpICsNCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSB4X2Nvb3JkLCANCiAgICB5ID0geV9jb29yZCwgDQogICAgbGFiZWwgPSBwYXN0ZTAoDQogICAgICAiQmF5ZXNpYW4gbmVnYXRpdmUgYmlub21pYWwgcmVncmVzc2lvbiIsICJcbiIsDQogICAgICAiRWZmZWN0IGVzdGltYXRlOiAiLCByb3VuZChlc3RpbWF0ZV9wZXJjZW50XzMsIDMpLCAiXG4iLA0KICAgICAgIjk1JUNySTogIiwgcm91bmQoTDk1Q0lfcGVyY2VudF8zLCAzKSwgIiB0byAiLCByb3VuZChIOTVDSV9wZXJjZW50XzMsIDMpKSwNCiAgICBoanVzdCA9IDAsIHZqdXN0ID0gMSwgc2l6ZSA9IDQNCiAgKSArDQogIGdlb21fdGV4dF9yZXBlbCgNCiAgICBkYXRhID0gdGVzdF9zdXJmYWNld2F0ZXIgJT4lIGZpbHRlcighaXMubmEoZWlwYWFiX24pLCByYW5rX2RldGVjdGlvbl9kaWYgJWluJSAxOjUpLA0KICAgIGFlcyh4ID0gY29tYmluZF9kZXRlY3Rpb24sIHkgPSBlaXBhYWJfbiwgbGFiZWwgPSBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCksIHNpemUgPSAzLCBib3gucGFkZGluZyA9IDAuNw0KICApICsNCiAgIyBBZGp1c3Qgc2NhbGVzDQogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSBjb2xvcnNwYWNlOjpkaXZlcmdlX2hjbCg3KSkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIlRSVUUiID0gIiMwMTA4MEEiLCAiRkFMU0UiID0gIiMwMTA4MEEiKSwgZ3VpZGUgPSAibm9uZSINCiAgICApICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJUZXN0IHZzIGVudmlyb25tZW50YWwgZGV0ZWN0aW9ucyIsDQogICAgeCA9ICJUb3RhbCBkZXRlY3Rpb25zIGluIGVudmlyb25tZW50YWwgZGF0YWJhc2VzIiwNCiAgICB5ID0gIk9jY3VycmVuY2UgaW4gYmVoYXZpb3VyYWwgZGF0YWJhc2UiLA0KICAgIGZpbGwgPSAiTnVtYmVyIG9mIHN1cmZhY2Ugd2F0ZXIgc2FtcGxlcyIsDQogICAgc2l6ZSA9ICJOdW1iZXIgb2Ygc3VyZmFjZSB3YXRlciBzYW1wbGVzIg0KICApICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImNlbnRlciINCiAgKQ0KDQpmaWdfczNhDQpgYGANCg0KVGhpcyBjb2RlIHNhdmVzIHRoZSBmaWd1cmUgYWJvdmUgYXMgUERGLiAgDQoNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZpZ193ZCwgIi4vZmlndXJlLXMzYS5wZGYiKSwNCiAgICAgICBwbG90ID0gZmlnX3MzYSwNCiAgICAgICB3aWR0aCA9IDIxMCwNCiAgICAgICBoZWlnaHQgPSAyOTcvMS41LCAgIyBTcGVjaWZ5IHRoZSBoZWlnaHQgb2YgdGhlIHBsb3QNCiAgICAgICB1bml0cyA9ICJtbSIpDQpgYGANCg0KDQojIyMgUGVyY2VudGFnZSBkZXRlY3Rpb25zDQoNCk1vZGVsIHN0cnVjdHVyZS4gIA0KDQpgYGB7cn0NCnBlcmNlbnRfZGV0ZWN0aW9uX25lZ2Jpbl9zdHIgPC0gYmYoZWlwYWFiX24gfiBjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uLA0KICAgICAgICAgICAgICAgZmFtaWx5ID0gbmVnYmlub21pYWwoKSkNCmBgYA0KDQoNCiMjIyMgUnVuIG1vZGVsDQoNClJ1biBtb2RlbC4gVGhpcyBoYXMgYmVlbiBoYXNoZWQgb3V0IGFzIEkgaGF2ZSBzYXZlZCB0aGUgbW9kZWwsIGFuZCB3aWxsIHJlLWxvYWQgaXQgaW5zdGVhZCBvZiByZS1ydW5uaW5nIGl0LiANCg0KYGBge3J9DQpvcHRpb25zKGJybXMuYmFja2VuZCA9ICJjbWRzdGFuciIpDQpwZXJjZW50X2RldGVjdGlvbl9uZWdiaW5fbW9kIDwtIGJybSgNCiAgcGVyY2VudF9kZXRlY3Rpb25fbmVnYmluX3N0ciwNCiAgZGF0YSA9IHRlc3Rfc3VyZmFjZXdhdGVyLA0KICBjb3JlcyA9IDQsDQogIGNoYWlucyA9IDQsDQogICNwcmlvciA9IG1vZF9wcmlvcnMsICN1c2UgZGVmYXVsdHMNCiAgd2FybXVwID0gMTAwMCwNCiAgc2VlZCA9IDIwMjUwNDE4LA0KICB0aGluID0gMiwNCiAgaXRlciA9IDgwMDAsDQogIGNvbnRyb2wgPSBsaXN0KG1heF90cmVlZGVwdGggPSAyMCwgYWRhcHRfZGVsdGEgPSAwLjk1KSwNCiAgc2F2ZV9wYXJzID0gc2F2ZV9wYXJzKGFsbCA9IFRSVUUpLA0KICBzYW1wbGVfcHJpb3IgPSBUUlVFLA0KICBmaWxlID0gcGFzdGUwKG1vZHNfd2QsICcuL3BlcmNlbnRfZGV0ZWN0aW9uX25lZ2Jpbl9tb2QnKQ0KKQ0KYGBgDQoNCg0KIyMjIyBSZS1sb2FkIG1vZGVscw0KDQpSZWxvYWQgdGhlIG1vZGVsIGlmIG5lY2Vzc2FyeS4gIA0KDQpgYGB7cn0NCnBlcmNlbnRfZGV0ZWN0aW9uX25lZ2Jpbl9tb2QgPC0gIHJlYWRSRFMoZmlsZSA9IHBhc3RlMChtb2RzX3dkLCAiLi9wZXJjZW50X2RldGVjdGlvbl9uZWdiaW5fbW9kLnJkcyIpKQ0KYGBgDQoNCg0KIyMjIyBNb2RlbCBkaWFnbm9zdGljcw0KDQpUaGUgUjIgdmFsdWUgaXMgdmVyeSBsb3csIGJ1dCBzaG91bGQgYmUgZmluZSBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgaW52ZXN0aWdhdGlvbi4gIA0KDQpgYGB7cn0NCnBlcmZvcm1hbmNlOjpyMl9iYXllcyhwZXJjZW50X2RldGVjdGlvbl9uZWdiaW5fbW9kLCByb2J1c3QgPSBGQUxTRSwgY2kgPSAwLjk1KQ0KYGBgDQoNCkNoZWNrIGZpdC4gIA0KDQpgYGB7cn0NCmNvbG9yX3NjaGVtZV9zZXQoInJlZCIpDQpicm1zOjpwcF9jaGVjayhwZXJjZW50X2RldGVjdGlvbl9uZWdiaW5fbW9kLCBuZHJhd3MgPSAxMDAsIHR5cGUgPSAiZWNkZl9vdmVybGF5IikNCmBgYA0KDQpDaGVja2luZyBSLWhhdCB2YWx1ZXMgdG8gY29uZmlybSBtb2RlbCBjb252ZXJnZW5jZS4gIA0KDQpgYGB7cn0NCnByaW50KHN1bW1hcnkocGVyY2VudF9kZXRlY3Rpb25fbmVnYmluX21vZCwgcHJvYiA9IDAuOTUpKSAjQWxsIFJoYXQgPSAxIChnb29kKS4NCmBgYA0KDQpDaGVjayBjaGFpbnMsIGFuZCBlc3RpbWF0ZXMuICANCg0KYGBge3J9DQpjb2xvcl9zY2hlbWVfc2V0KCJyZWQiKQ0KcGxvdChwZXJjZW50X2RldGVjdGlvbl9uZWdiaW5fbW9kKQ0KYGBgDQoNCiMjIyMgTW9kZWwgZXN0aW1hdGVzDQoNClB1bGxpbmcgdGhlIGNvbmRpdGlvbmFsIGVmZmVjdCBlc3RpbWF0ZXMgZnJvbSB0aGUgbW9kZWwuIFRoZSBlc3RpbWF0ZSBjb3JyZXNwb25kcyB0byB0aGUgZWZmZWN0IG9mIHRoZSBwcmVkaWN0b3Igb24gdGhlIGxvZyBvZiB0aGUgZXhwZWN0ZWQgcmVzcG9uc2UgdmFyaWFibGUuIEEgMS11bml0IGluY3JlYXNlIChpLmUuIDElKSBpbiB0aGUgcGVyY2VudGFnZSBvZiBlbnZpcm9ubWVudGFsIGRldGVjdGlvbnMgaW5jcmVhc2VzIHRoZSBleHBlY3RlZCBvY2N1cnJlbmNlIGluIEVJUEFBQiBkYXRhYmFzZSBieSAxLjE3JSAoaS5lLiAoZXhwKDAuMDExNjA0NzcpLTEpKjEwMCA9IDEuMTY3MjM3KS4gIA0KDQpgYGB7cn0NCmVzaXRtYXRlc180IDwtIGNvbmRpdGlvbmFsX2VmZmVjdHMocGVyY2VudF9kZXRlY3Rpb25fbmVnYmluX21vZCkNCmVzdGltYXRlX2RhdGFfNCA8LSBlc2l0bWF0ZXNfNCRjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uDQoNCm1vZGVsX3N1bW1hcnlfNCA8LSAgYXMuZGF0YS5mcmFtZShmaXhlZihwZXJjZW50X2RldGVjdGlvbl9uZWdiaW5fbW9kLCBzdW1tYXJ5ID0gVFJVRSkpICU+JSANCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lIA0KICBjbGVhbl9uYW1lcygpDQoNCmVzdGltYXRlXzQgPC0gbW9kZWxfc3VtbWFyeV80JGVzdGltYXRlWzJdDQpMOTVDSV80IDwtIG1vZGVsX3N1bW1hcnlfNCRxMl81WzJdDQpIOTVDSV80IDwtIG1vZGVsX3N1bW1hcnlfNCRxOTdfNVsyXQ0KZXN0aW1hdGVfcGVyY2VudF80IDwtIChleHAoZXN0aW1hdGVfNCktMSkqMTAwDQpMOTVDSV9wZXJjZW50XzQgPC0gKGV4cChMOTVDSV80KS0xKSoxMDANCkg5NUNJX3BlcmNlbnRfNCA8LSAoZXhwKEg5NUNJXzQpLTEpKjEwMA0KDQptb2RlbF9zdW1tYXJ5XzRfZnQgPC0gbW9kZWxfc3VtbWFyeV80ICU+JSANCiAgZHBseXI6Om11dGF0ZShhY3Jvc3Mod2hlcmUoaXMuZG91YmxlKSwgfiByb3VuZCgueCwgMykpKSAlPiUgDQogIGRwbHlyOjpyZW5hbWUoInByZWRpY3RvciIgPSByb3duYW1lKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoQ3JJID0gcGFzdGUwKHEyXzUsICIgdG8gIiwgcTk3XzUpKSAlPiUgDQogIGRwbHlyOjpzZWxlY3QocHJlZGljdG9yLCBlc3RpbWF0ZSwgQ3JJKSAlPiUgDQogIGZsZXh0YWJsZSgpICU+JQ0KICBzZXRfaGVhZGVyX2xhYmVscygNCiAgICBwcmVkaWN0b3IgPSAiUHJlZGljdG9yIiwNCiAgICBlc3RpbWF0ZSA9ICJFc3RpbWF0ZSIsDQogICAgQ3JJID0gIjk1JSBDckkiDQogICkgJT4lDQogIGF1dG9maXQoKQ0KDQptb2RlbF9zdW1tYXJ5XzRfZnQNCmBgYA0KDQpDb21wb3VuZHMgd2l0aCB0aGUgbW9zdCBlbnZpcm9ubWVudGFsIGRldGVjdGlvbnMgdGhhdCBhcmUgbm90IHByZXNlbnQgaW4gRUlQQUFCLiAgIA0KDQpgYGB7cn0NCnRhYmxlX1M1IDwtIHRlc3Rfc3VyZmFjZXdhdGVyICU+JQ0KICBmaWx0ZXIoaXMubmEoZWlwYWFiX24pKSAlPiUNCiAgYXJyYW5nZShkZXNjKGNvbWJpbmRfZGV0ZWN0aW9uKSkgJT4lDQogIHNsaWNlKDE6MTUpICU+JQ0KICBtdXRhdGUoY29tcG91bmRfbmFtZSA9IHNlbnRlbmNlX2Nhc2UoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpKSAlPiUNCiAgc2VsZWN0KA0KICAgIGNvbXBvdW5kX25hbWUsIGNvbWJpbmRfc2FtcGxlcywgY29tYmluZF9kZXRlY3Rpb24sIA0KICAgIGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24sIGNvbWJpbmRfcmFua19zYW1wbGVzLCBjb21iaW5kX3JhbmtfZGV0ZWN0aW9uDQogICkgJT4lDQogIHJlbmFtZSgNCiAgICBOYW1lID0gY29tcG91bmRfbmFtZSwNCiAgICBTYW1wbGVzID0gY29tYmluZF9zYW1wbGVzLA0KICAgIERldGVjdGlvbnMgPSBjb21iaW5kX2RldGVjdGlvbiwNCiAgICBgUGVyY2VudGFnZSBkZXRlY3Rpb25zYCA9IGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24sDQogICAgYFJhbmsgc2FtcGxlc2AgPSBjb21iaW5kX3Jhbmtfc2FtcGxlcywNCiAgICBgUmFuayBkZXRlY3Rpb25zYCA9IGNvbWJpbmRfcmFua19kZXRlY3Rpb24NCiAgKSAlPiUNCiAgZmxleHRhYmxlKCkgJT4lDQogIGNvbGZvcm1hdF9udW0oaiA9IGMoIlNhbXBsZXMiLCAiRGV0ZWN0aW9ucyIpLCBkaWdpdHMgPSAwKSAlPiUNCiAgYWxpZ24oYWxpZ24gPSAiY2VudGVyIiwgcGFydCA9ICJhbGwiKSAlPiUNCiAgYXV0b2ZpdCgpDQoNCnRhYmxlX1M1DQpgYGANCg0KIyMjIyMgRmlnIFMzYg0KDQpQaGFybWFjZXV0aWNhbCBvY2N1cnJlbmNlIGluIHRoZSBiZWhhdmlvdXJhbCB0ZXN0IGRhdGFiYXNlIHBsb3R0ZWQgYWdhaW5zdCB0aGUgcGVyY2VudGFnZSBvZiBwb3NpdGl2ZSBkZXRlY3Rpb25zIChpLmUuIHBvc2l0aXZlIGRldGVjdGlvbnMgcmVsYXRpdmUgdG8gdG90YWwgc2FtcGxlcyBmb3IgdGhhdCBjb21wb3VuZCkgaW4gZW52aXJvbm1lbnRhbCBkYXRhYmFzZXMuIFRoZSBmaXZlIGNvbXBvdW5kcyBoaWdobGlnaHRlZCByZXByZXNlbnQgdGhvc2UgdGhhdCBhcmUgY3VycmVudGx5IHVuZGVycmVwcmVzZW50ZWQgaW4gdGhlIGJlaGF2aW91ciB0ZXN0IGRhdGFiYXNlIGJhc2VkIG9uIHRoZWlyIHJhbmsgcGVyY2VudGFnZSBkZXRlY3Rpb25zIChpLmUuIHJhbmsgb2NjdXJyZW5jZSBpbiBiZWhhdmlvdXIgdGVzdCBkYXRhYmFzZXMgdnMgcmFuayBwZXJjZW50YWdlIGRldGVjdGlvbnMgaW4gZW52aXJvbm1lbnRhbCBkYXRhYmFzZXMpLiBUaGUgcG9pbnQgc2l6ZSByZXByZXNlbnRzIHRoZSB0b3RhbCBudW1iZXIgb2Ygc2FtcGxlcyBhY3Jvc3MgYWxsIGVudmlyb25tZW50YWwgZGF0YWJhc2VzLiAgIA0KDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02fQ0KdGV4dF9jb29yZCA8LSB0ZXN0X3N1cmZhY2V3YXRlciAlPiUgDQogIGZpbHRlcighaXMubmEoZWlwYWFiX24pKSAlPiUgDQogIHN1bW1hcmlzZSh4ID0gbWF4KGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24sIG5hLnJtID0gVFJVRSkvMiwNCiAgICAgICAgICAgIHkgPSBtYXgoZWlwYWFiX24sIG5hLnJtID0gVFJVRSkpDQoNCnlfY29vcmQgPC0gdGV4dF9jb29yZCAlPiUgcHVsbCh5KQ0KDQp4X2Nvb3JkIDwtIHRleHRfY29vcmQgJT4lIHB1bGwoeCkNCg0KZmlnX3MzYiA8LSBnZ3Bsb3QoKSArDQogIGdlb21fbGluZShkYXRhID0gZXN0aW1hdGVfZGF0YV80LCBhZXMoeCA9IGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24sIHkgPSBlc3RpbWF0ZV9fKSwgY29sb3IgPSAiIzFjNDU5NSIsIGxpbmV3aWR0aCA9IDEpICsNCiAgZ2VvbV9yaWJib24oZGF0YSA9IGVzdGltYXRlX2RhdGFfNCwgYWVzKHggPSBjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uLCB5bWluID0gbG93ZXJfXywgeW1heCA9IHVwcGVyX18pLCBmaWxsID0gIiMxYzQ1OTUiLCBhbHBoYSA9IDAuMSkgKw0KICBnZW9tX3BvaW50KGRhdGEgPSB0ZXN0X3N1cmZhY2V3YXRlciAlPiUgZmlsdGVyKCFpcy5uYShlaXBhYWJfbikpICU+JSBkcGx5cjo6YXJyYW5nZShyYW5rX2RldGVjdGlvbl9wZXJjZW50X2RpZikgJT4lIA0KICAgICAgICAgICAgICAgbXV0YXRlKGlzX3RvcDUgPSByYW5rX2RldGVjdGlvbl9wZXJjZW50X2RpZiAlaW4lIDE6NSksIA0KICAgICAgICAgICAgIGFlcyh4ID0gY29tYmluZF9wZXJjZW50X2RldGVjdGlvbiwgeSA9IGVpcGFhYl9uLCBzaXplID0gY29tYmluZF9zaW5nbGVzKSwgYWxwaGEgPSAwLjQsIHNoYXBlID0gMjEsIGNvbG9yID0gIiMwMTA4MEEiLCBmaWxsID0gIiMxQzQ3OTQiKSArDQogIGFubm90YXRlKA0KICAgICJ0ZXh0IiwNCiAgICB4ID0geF9jb29yZCwgDQogICAgeSA9IHlfY29vcmQsIA0KICAgIGxhYmVsID0gcGFzdGUwKA0KICAgICAgIkJheWVzaWFuIG5lZ2F0aXZlIGJpbm9taWFsIHJlZ3Jlc3Npb24iLCAiXG4iLA0KICAgICAgIkVmZmVjdCBlc3RpbWF0ZSAoJSk6ICIsIHJvdW5kKGVzdGltYXRlX3BlcmNlbnRfNCwgMyksICJcbiIsDQogICAgICAiOTUlQ3JJOiAiLCByb3VuZChMOTVDSV9wZXJjZW50XzQsIDMpLCAiIHRvICIsIHJvdW5kKEg5NUNJX3BlcmNlbnRfNCwgMykNCiAgICApLA0KICAgIGhqdXN0ID0gMCwgdmp1c3QgPSAxLCBzaXplID0gNA0KICApICsNCiAgZ2VvbV90ZXh0X3JlcGVsKA0KICAgIGRhdGEgPSB0ZXN0X3N1cmZhY2V3YXRlciAlPiUgZmlsdGVyKCFpcy5uYShlaXBhYWJfbiksIHJhbmtfZGV0ZWN0aW9uX3BlcmNlbnRfZGlmICVpbiUgMTo1KSwNCiAgICBhZXMoeCA9IGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24sIHkgPSBlaXBhYWJfbiwgbGFiZWwgPSBjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCksIHNpemUgPSAzLCBib3gucGFkZGluZyA9IDAuNw0KICAgICkgKw0KICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvdXJzID0gY29sb3JzcGFjZTo6ZGl2ZXJnZV9oY2woNykpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCgNCiAgICB2YWx1ZXMgPSBjKCJUUlVFIiA9ICIjMDEwODBBIiwgIkZBTFNFIiA9ICIjMDEwODBBIiksIGd1aWRlID0gIm5vbmUiKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGVzdCB2cyBwZXJjZW50YWdlIGRldGVjdGlvbnMiLA0KICAgIHggPSAiUGVyY2VudGFnZSBkZXRlY3Rpb25zIGluIGVudmlyb25tZW50YWwgZGF0YWJhc2VzIiwNCiAgICB5ID0gIk9jY3VycmVuY2UgaW4gYmVoYXZpb3VyYWwgZGF0YWJhc2UiLA0KICAgIHNpemUgPSAiTnVtYmVyIG9mIHNpbmdsZSBzYW1wbGVzIg0KICApICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImNlbnRlciINCiAgKQ0KDQpmaWdfczNiDQpgYGANCg0KU2F2ZSB0aGUgZmlndXJlIA0KDQpgYGB7cn0NCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChmaWdfd2QsICIuL2ZpZ3VyZS1zM2IucGRmIiksDQogICAgICAgcGxvdCA9IGZpZ19zM2IsDQogICAgICAgd2lkdGggPSAyMTAsDQogICAgICAgaGVpZ2h0ID0gMjk3LzEuNSwgICMgU3BlY2lmeSB0aGUgaGVpZ2h0IG9mIHRoZSBwbG90DQogICAgICAgdW5pdHMgPSAibW0iKQ0KYGBgDQoNCg0KSGVyZSBhcmUgY29tcG91bmQgdGhhdCBhcmUgbm90IGluIEVJUEFBQiBkYXRhYmFzZSwgYnV0IGhhdmUgaGlnaCBwb3NpdGl2ZSBkZXRlY3Rpb24gcmF0ZXMgaW4gdGhlIGVudmlyb25tZW50LiAgDQoNCmBgYHtyfQ0KZ2FwX3RiIDwtIHRlc3Rfc3VyZmFjZXdhdGVyICU+JQ0KICBmaWx0ZXIoaXMubmEoZWlwYWFiX24pLCAhaXMubmEoY29tYmluZF9wZXJjZW50X2RldGVjdGlvbikpICU+JQ0KICBhcnJhbmdlKGRlc2MoY29tYmluZF9wZXJjZW50X2RldGVjdGlvbikpICU+JQ0KICBzbGljZSgxOjIwKSAlPiUNCiAgbXV0YXRlKA0KICAgIGNvbXBvdW5kX25hbWUgPSBzZW50ZW5jZV9jYXNlKGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSwNCiAgICBjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uID0gcm91bmQoY29tYmluZF9wZXJjZW50X2RldGVjdGlvbiwgMikNCiAgKSAlPiUNCiAgc2VsZWN0KGNvbXBvdW5kX25hbWUsIGNvbWJpbmRfc2luZ2xlcywgY29tYmluZF9zaW5nbGVfZGV0ZWN0aW9uLCBjb21iaW5kX3BlcmNlbnRfZGV0ZWN0aW9uKSAlPiUNCiAgcmVuYW1lKA0KICAgIE5hbWUgPSBjb21wb3VuZF9uYW1lLA0KICAgIGBTaW5nbGUgc2FtcGxlc2AgPSBjb21iaW5kX3NpbmdsZXMsDQogICAgYFNpbmdsZSBkZXRlY3Rpb25zYCA9IGNvbWJpbmRfc2luZ2xlX2RldGVjdGlvbiwNCiAgICBgUGVyY2VudCBkZXRlY3Rpb25zYCA9IGNvbWJpbmRfcGVyY2VudF9kZXRlY3Rpb24NCiAgKSAlPiUNCiAgZmxleHRhYmxlKCkgJT4lDQogIGFsaWduKGFsaWduID0gImNlbnRlciIsIHBhcnQgPSAiYWxsIikgJT4lDQogIGF1dG9maXQoKQ0KDQpnYXBfdGINCmBgYA0KDQpTYXZpbmcgdGhlIHRhYmxlIA0KDQpgYGB7cn0NCnNhdmVfYXNfZG9jeChnYXBfdGIsIHBhdGggPSBwYXN0ZTAoZmlnX3dkLCAiLi9nYXBfdGIuZG9jeCIpKQ0KYGBgDQoNCg0KIyMjIFRlbXBvcmFsIHRyZW5kcw0KDQpIZXJlIHdlIHdpbGwgbG9vayBhdCBnZW5lcmFsIHRlbXBvcmFsIHRyZW5kcyB1c2luZyB0aGUgRUlQQUFCLCBVQkEgYW5kIE5PUk1BTiBkYXRhLiAgDQoNCmBgYHtyfQ0KdGVtcG9yYWxfdHJlbmRzIDwtIGFsbF9kYXRhYmFzZXMgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKHNvdXJjZSAhPSAid2lsa3NvbiIpICU+JSANCiAgZHBseXI6Om11dGF0ZShncm91cF90eXBlID0gaWZfZWxzZShzb3VyY2UgPT0gImVpcGFhYiIsICJ0ZXN0IiwgImVudmlyb25tZW50YWwiKSkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoZ3JvdXBfdHlwZSwgeWVhcikgJT4lIA0KICBkcGx5cjo6cmVmcmFtZSgNCiAgICBuX3NhbXBsZXMgPSBzdW0obl91bml0c19lc3QsIG5hLnJtID0gVFJVRSksDQogICAgbl9jb21wb3VuZHMgPSBsZW5ndGgodW5pcXVlKGNvbXBvdW5kX25hbWVfY29ycmVjdGVkKSksDQogICAgcmVsX2NvbXBvdW5kcyA9IG5fY29tcG91bmRzIC8gbl9zYW1wbGVzLA0KICAgIGNvbXBvdW5kX2xpc3QgPSBsaXN0KHVuaXF1ZShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkpDQogICkgJT4lIA0KICB0aWR5cjo6Y29tcGxldGUoZ3JvdXBfdHlwZSwgeWVhcikgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoZ3JvdXBfdHlwZSkgJT4lIA0KICBkcGx5cjo6bXV0YXRlKGNvbXBvdW5kX2xpc3QgPSBwdXJycjo6bWFwKGNvbXBvdW5kX2xpc3QsIH4gaWYgKGlzLm51bGwoLngpKSBjaGFyYWN0ZXIoMCkgZWxzZSAueCksDQogICAgICAgICAgICAgICAgIGN1bXVsYXRpdmVfY29tcG91bmRzID0gcHVycnI6OmFjY3VtdWxhdGUoY29tcG91bmRfbGlzdCwgfiB1bmlvbigueCwgLnkpKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHB1cnJyOjptYXBfaW50KGxlbmd0aCksDQogICAgICAgICAgICAgICAgbl9zYW1wbGVzX3ogPSBzY2FsZShuX3NhbXBsZXMpKSAlPiUgDQogIGRwbHlyOjp1bmdyb3VwKCkNCmBgYA0KDQpMZXQncyBsb29rIGF0IHRoZSB0b3RhbCBudW1iZXIgb2Ygc2FtcGxlcyBwZXIgeWVhciBpbiB0aGVzZSBjb21iaW5lZCBkYXRhYmFzZXMgICANCg0KYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTh9DQp0ZW1wb3JhbF90cmVuZHMgJT4lIA0KICAjZHBseXI6OmZpbHRlcih5ZWFyID4gMTk5OSAmIHllYXIgPCAyMDIxKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBuX3NhbXBsZXMpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgZmFjZXRfd3JhcCh+Z3JvdXBfdHlwZSwgc2NhbGVzID0gImZyZWUiLA0KICAgICAgICAgICAgIGxhYmVsbGVyID0gbGFiZWxsZXIoZ3JvdXBfdHlwZSA9IGMoDQogICAgICAgICAgICAgICAiZW52aXJvbm1lbnRhbCIgPSAiRW52aXJvbm1lbnRhbCBkYXRhIiwNCiAgICAgICAgICAgICAgICJ0ZXN0IiA9ICJCZWhhdmlvdXJhbCBlY290b3hpY29sb2d5IGRhdGEiKSkgDQogICAgICAgICAgICAgKSArDQogICAgbGFicygNCiAgICB0aXRsZSA9ICJEYXRhIG92ZXIgdGltZSIsDQogICAgeCA9ICJZZWFyIiwNCiAgICB5ID0gIlNhbXBsaW5nIGVmZm9ydCAodGVzdHMgb3Igd2F0ZXIgc2FtcGxlcykiDQogICAgKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiDQogICkNCmBgYA0KDQpGb3IgYSBjbGVhcmVyIHRlbXBvcmFsIHRyZW5kIHdlIGxvb2sgd2l0aGluIHRoZSBOT1JNQU4gYW5kIFVCQS1QSEFNQSBkYXRhYmFzZXMgc2VwYXJhdGVseS4gDQoNCk1ha2luZyBhIG5ldyBkYXRhc2V0IGp1c3QgZm9yIE5PUk1BTiBFTVBPREFUIGFuZCBQSEFSTVMtVUJBIGRhdGFiYXNlICAgDQoNCmBgYHtyfQ0KdWJhX25vcm1hbl90ZW1wb3JhbF90cmVuZHMgPC0gYWxsX2RhdGFiYXNlcyAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc291cmNlID09ICJ1YmEiIHwgc291cmNlID09ICJub3JtYW4iKSAlPiUgDQogIGRwbHlyOjpncm91cF9ieShzb3VyY2UsIHllYXIpICU+JSANCiAgZHBseXI6OnJlZnJhbWUoDQogICAgbl9zYW1wbGVzID0gc3VtKG5fdW5pdHNfZXN0LCBuYS5ybSA9IFRSVUUpLA0KICAgIG5fY29tcG91bmRzID0gbGVuZ3RoKHVuaXF1ZShjb21wb3VuZF9uYW1lX2NvcnJlY3RlZCkpLA0KICAgIHJlbF9jb21wb3VuZHMgPSBuX2NvbXBvdW5kcyAvIG5fc2FtcGxlcywNCiAgICBjb21wb3VuZF9saXN0ID0gbGlzdCh1bmlxdWUoY29tcG91bmRfbmFtZV9jb3JyZWN0ZWQpKQ0KICApICU+JSANCiAgdGlkeXI6OmNvbXBsZXRlKHNvdXJjZSwgeWVhcikgJT4lIA0KICBkcGx5cjo6ZmlsdGVyKCFpcy5uYSh5ZWFyKSkgJT4lIA0KICBkcGx5cjo6Z3JvdXBfYnkoc291cmNlKSAlPiUgDQogIGRwbHlyOjptdXRhdGUoY29tcG91bmRfbGlzdCA9IHB1cnJyOjptYXAoY29tcG91bmRfbGlzdCwgfiBpZiAoaXMubnVsbCgueCkpIGNoYXJhY3RlcigwKSBlbHNlIC54KSwNCiAgICAgICAgICAgICAgICAgY3VtdWxhdGl2ZV9jb21wb3VuZHMgPSBwdXJycjo6YWNjdW11bGF0ZShjb21wb3VuZF9saXN0LCB+IHVuaW9uKC54LCAueSkpICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHVycnI6Om1hcF9pbnQobGVuZ3RoKSwNCiAgICAgICAgICAgICAgICBuX3NhbXBsZXNfeiA9IHNjYWxlKG5fc2FtcGxlcykpICU+JSANCiAgZHBseXI6OnVuZ3JvdXAoKQ0KYGBgDQoNCkNsb3NlciBsb29rIGF0IFVCQQ0KDQpgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NX0NCnViYV9zYW1wbGVfeWVhciA8LSB1YmFfbm9ybWFuX3RlbXBvcmFsX3RyZW5kcyAlPiUgDQogIGRwbHlyOjpmaWx0ZXIoc291cmNlID09ICJ1YmEiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IG5fc2FtcGxlcykpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICAgIGxhYnMoDQogICAgdGl0bGUgPSAiRGF0YSBvdmVyIHRpbWUiLA0KICAgIHggPSAiWWVhciIsDQogICAgeSA9ICJTYW1wbGluZyBlZmZvcnQiDQogICAgKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiDQogICkNCnViYV9zYW1wbGVfeWVhcg0KYGBgDQoNClNhdmUgdGhpcyBmaWd1cmUgZm9yIHRoZSBNUyAgIA0KDQpgYGB7cn0NCmdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMChmaWdfd2QsICIuL3ViYV9zYW1wbGVfeWVhci5wZGYiKSwNCiAgICAgICBwbG90ID0gdWJhX3NhbXBsZV95ZWFyLA0KICAgICAgIHdpZHRoID0gMjEwLA0KICAgICAgIGhlaWdodCA9IDI5Ny8xLjUsDQogICAgICAgdW5pdHMgPSAibW0iKQ0KYGBgDQoNCg0KTm93IHRoZSBjdW11bGF0aXZlIG51bWJlciBvZiBjb21wb3VuZHMgb3ZlciB0aW1lLiAgDQoNCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD04fQ0KdGVtcG9yYWxfdHJlbmRzICU+JSANCiAgI2RwbHlyOjpmaWx0ZXIoeWVhciA+IDE5OTkgJiB5ZWFyIDwgMjAyMSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gY3VtdWxhdGl2ZV9jb21wb3VuZHMsIHNpemUgPSBuX3NhbXBsZXNfeikpICsNCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSkgKw0KICBmYWNldF93cmFwKH5ncm91cF90eXBlLCBzY2FsZXMgPSAiZnJlZSIsDQogICAgICAgICAgICAgbGFiZWxsZXIgPSBsYWJlbGxlcihncm91cF90eXBlID0gYygNCiAgICAgICAgICAgICAgICJlbnZpcm9ubWVudGFsIiA9ICJFbnZpcm9ubWVudGFsIGRhdGEiLA0KICAgICAgICAgICAgICAgInRlc3QiID0gIkJlaGF2aW91cmFsIGVjb3RveGljb2xvZ3kgZGF0YSIpKSANCiAgICAgICAgICAgICApICsNCiAgICBsYWJzKA0KICAgIHRpdGxlID0gIkN1bW11bGF0aXZlIGNvbXBvdW5kcyIsDQogICAgeCA9ICJZZWFyIiwNCiAgICB5ID0gIlRvdGFsIG51bWJlciBvZiBjb21wb3VuZHMiLA0KICAgIHNpemUgPSAiU2FtcGxlIGVmZm9ydCAoc2NhbGVkKSINCiAgICApICsNCiAgdGhlbWVfY2xlYW4oKSArDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLA0KICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImNlbnRlciINCiAgKQ0KYGBgDQoNCldpdGggYSBjbG9zZXIgbG9vayBhdCBVQkENCg0KYGBge3IsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTV9DQp1YmFfY3VtbXVsYXRpdmVfZmlnIDwtIHViYV9ub3JtYW5fdGVtcG9yYWxfdHJlbmRzICU+JSANCiAgZHBseXI6OmZpbHRlcihzb3VyY2UgPT0gInViYSIpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGN1bXVsYXRpdmVfY29tcG91bmRzLCBzaXplID0gbl9zYW1wbGVzX3opKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsNCiAgIyBmYWNldF93cmFwKH5zb3VyY2UsIHNjYWxlcyA9ICJmcmVlIiwNCiAgIyAgICAgICAgICAgIGxhYmVsbGVyID0gbGFiZWxsZXIoc291cmNlID0gYygNCiAgIyAgICAgICAgICAgICAgInViYSIgPSAiUEhBUk1TLVVCQSBkYXRhYmFzZSIsDQogICMgICAgICAgICAgICAgICJub3JtYW4iID0gIk5PUk1BTiBFTVBPREFUIGRhdGFiYXNlIikpIA0KICAjICAgICAgICAgICAgKSArDQogICAgbGFicygNCiAgICB0aXRsZSA9ICJDdW1tdWxhdGl2ZSBjb21wb3VuZHMiLA0KICAgIHggPSAiWWVhciIsDQogICAgeSA9ICJUb3RhbCBudW1iZXIgb2YgY29tcG91bmRzIiwNCiAgICBzaXplID0gIlNhbXBsZSBlZmZvcnQgKHNjYWxlZCkiDQogICAgKSArDQogIHRoZW1lX2NsZWFuKCkgKw0KICB0aGVtZSgNCiAgICBsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwNCiAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiDQogICkNCg0KdWJhX2N1bW11bGF0aXZlX2ZpZw0KYGBgDQoNCg0KU2F2ZSB0aGlzIGZpZ3VyZSBmb3IgdGhlIE1TICAgDQoNCmBgYHtyfQ0KZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKGZpZ193ZCwgIi4vdWJhX2N1bW11bGF0aXZlX2ZpZy5wZGYiKSwNCiAgICAgICBwbG90ID0gdWJhX2N1bW11bGF0aXZlX2ZpZywNCiAgICAgICB3aWR0aCA9IDIxMCwNCiAgICAgICBoZWlnaHQgPSAyOTcvMS41LA0KICAgICAgIHVuaXRzID0gIm1tIikNCmBgYA0KDQoNCg0KIyDimJXvuI8gRG9zZSBzZWxlY3Rpb24gZnVuY3Rpb24NCg0KYHNlbGVjdF9kb3NlcygpYCBHZW5lcmF0ZXMgYSBzZXF1ZW5jZSBvZiByZWNvbW1lbmRlZCBkb3NlcyBiYXNlZCBvbiBvYnNlcnZlZCBlbnZpcm9ubWVudGFsIGNvbmNlbnRyYXRpb24gZGF0YSwgdXNpbmcgYSBzcGVjaWZpZWQgbWVhc3VyZSBvZiBjZW50cmFsIHRlbmRlbmN5IChtZWFuLCBtZWRpYW4sIG9yIG1vZGUpIGFuZCBnZW9tZXRyaWMgc3BhY2luZy4gVGhlIGZ1bmN0aW9uIHNlbGVjdHMgdXAgdG8gbWF4X2Rvc2VzIHZhbHVlcyBzcGFjZWQgYWJvdmUgYW5kL29yIGJlbG93IHRoZSBjZW50cmFsIHZhbHVlLCBvcHRpb25hbGx5IGNvbnN0cmFpbmVkIHRvIG9ic2VydmVkIHZhbHVlcyBhbmQgbGltaXRzIG9mIHF1YW50aWZpY2F0aW9uIChMT1EpLiBJdCByZXR1cm5zIGEgZG9zZSB0YWJsZSwgYSBzdW1tYXJ5IG9mIHRoZSBkaXN0cmlidXRpb24sIGFuZCBzaWRlLWJ5LXNpZGUgcGxvdHMgc2hvd2luZyBkb3NlIHBsYWNlbWVudCBvbiBib3RoIHJhdyBhbmQgbG9nLXNjYWxlZCBkZW5zaXR5IGN1cnZlcy4gRGVzaWduZWQgdG8gc3VwcG9ydCB0cmFuc3BhcmVudCBhbmQgZGF0YS1pbmZvcm1lZCBkb3NlIHNlbGVjdGlvbiBmb3IgZWNvdG94aWNvbG9naWNhbCBzdHVkeSBkZXNpZ24uDQoNCg0KYGBge3J9DQpzZWxlY3RfZG9zZXMgPC0gZnVuY3Rpb24oZGF0YSwNCiAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBjZW50cmFsX3RlbmRlbmN5ID0gIm1lZGlhbiIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWluX3NwYWNpbmcgPSAzLjIsDQogICAgICAgICAgICAgICAgICAgICAgICAgbWF4X2Rvc2VzID0gNSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBMT1EgPSAwLjAwMDEsDQogICAgICAgICAgICAgICAgICAgICAgICAgZGlyZWN0aW9uID0gImJvdGgiLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0X3RvX29ic2VydmVkID0gRkFMU0UpIHsNCiAgbGlicmFyeShnZ3Bsb3QyKQ0KICBsaWJyYXJ5KGRwbHlyKQ0KICBsaWJyYXJ5KHBhdGNod29yaykNCg0KICB2YWx1ZXMgPC0gZGF0YVtbdmFyaWFibGVdXQ0KICB2YWx1ZXMgPC0gdmFsdWVzWyFpcy5uYSh2YWx1ZXMpICYgdmFsdWVzID4gMF0NCiAgaWYgKGxlbmd0aCh2YWx1ZXMpIDwgMykgc3RvcCgiTm90IGVub3VnaCBkYXRhIHBvaW50cy4iKQ0KDQogICMgQ2VudHJhbCB0ZW5kZW5jeQ0KICBjZW50ZXIgPC0gc3dpdGNoKHRvbG93ZXIoY2VudHJhbF90ZW5kZW5jeSksDQogICAgICAgICAgICAgICAgICAgIm1lYW4iID0gbWVhbih2YWx1ZXMpLA0KICAgICAgICAgICAgICAgICAgICJtZWRpYW4iID0gbWVkaWFuKHZhbHVlcyksDQogICAgICAgICAgICAgICAgICAgIm1vZGUiID0gew0KICAgICAgICAgICAgICAgICAgICAgZCA8LSBkZW5zaXR5KHZhbHVlcykNCiAgICAgICAgICAgICAgICAgICAgIGQkeFt3aGljaC5tYXgoZCR5KV0NCiAgICAgICAgICAgICAgICAgICB9LA0KICAgICAgICAgICAgICAgICAgIHN0b3AoIkludmFsaWQgY2VudHJhbF90ZW5kZW5jeSIpKQ0KDQogIHVwcGVyIDwtIHF1YW50aWxlKHZhbHVlcywgMC45NSkNCiAgbG93ZXIgPC0gcXVhbnRpbGUodmFsdWVzLCAwLjA1KQ0KICBtYXhfdmFsIDwtIG1heCh2YWx1ZXMpDQogIG1pbl92YWwgPC0gbWluKHZhbHVlcykNCg0KICBkb3NlcyA8LSBjKGNlbnRlcikNCiAgbGFiZWxzIDwtIGMoIkQxIikNCg0KICAjIEdlbmVyYXRlIGFib3ZlIGFuZCBiZWxvdyBkb3Nlcw0KICBhYm92ZSA8LSBjKCkNCiAgYmVsb3cgPC0gYygpDQoNCiAgIyBBYm92ZQ0KICB2YWwgPC0gY2VudGVyDQogIHdoaWxlIChsZW5ndGgoYWJvdmUpIDwgbWF4X2Rvc2VzKSB7DQogICAgdmFsIDwtIHZhbCAqIG1pbl9zcGFjaW5nDQogICAgaWYgKGxpbWl0X3RvX29ic2VydmVkICYmIHZhbCA+IG1heF92YWwpIGJyZWFrDQogICAgYWJvdmUgPC0gYyhhYm92ZSwgdmFsKQ0KICB9DQoNCiAgIyBCZWxvdw0KICB2YWwgPC0gY2VudGVyDQogIHdoaWxlIChsZW5ndGgoYmVsb3cpIDwgbWF4X2Rvc2VzKSB7DQogICAgdmFsIDwtIHZhbCAvIG1pbl9zcGFjaW5nDQogICAgaWYgKHZhbCA8PSBMT1EpIGJyZWFrDQogICAgaWYgKGxpbWl0X3RvX29ic2VydmVkICYmIHZhbCA8IG1pbl92YWwpIGJyZWFrDQogICAgYmVsb3cgPC0gYyhiZWxvdywgdmFsKQ0KICB9DQoNCiAgIyBBbHRlcm5hdGUgc2VsZWN0aW9uDQogIGRvc2VfbGlzdCA8LSBjKGNlbnRlcikNCiAgaSA8LSAxDQogIHdoaWxlIChsZW5ndGgoZG9zZV9saXN0KSA8IG1heF9kb3Nlcykgew0KICAgIGlmIChpIDw9IGxlbmd0aChhYm92ZSkpIGRvc2VfbGlzdCA8LSBjKGRvc2VfbGlzdCwgYWJvdmVbaV0pDQogICAgaWYgKGxlbmd0aChkb3NlX2xpc3QpID09IG1heF9kb3NlcykgYnJlYWsNCiAgICBpZiAoaSA8PSBsZW5ndGgoYmVsb3cpKSBkb3NlX2xpc3QgPC0gYyhkb3NlX2xpc3QsIGJlbG93W2ldKQ0KICAgIGlmIChsZW5ndGgoZG9zZV9saXN0KSA9PSBtYXhfZG9zZXMpIGJyZWFrDQogICAgaSA8LSBpICsgMQ0KICAgIGlmIChpID4gMjApIGJyZWFrDQogIH0NCg0KICBpZiAobGVuZ3RoKGRvc2VfbGlzdCkgPCBtYXhfZG9zZXMpIHsNCiAgICB3YXJuaW5nKCJVbmFibGUgdG8gc2VsZWN0IGVub3VnaCBkb3NlcyB3aXRoaW4gY29uc3RyYWludHMuIikNCiAgICByZXR1cm4oTkEpDQogIH0NCg0KICBkb3NlX2xpc3QgPC0gc29ydChkb3NlX2xpc3QpDQogIGRvc2VfZGYgPC0gZGF0YS5mcmFtZShkb3NlID0gcGFzdGUwKCJEIiwgc2VxX2Fsb25nKGRvc2VfbGlzdCkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBkb3NlX2xpc3QpDQoNCiAgIyBTdW1tYXJ5DQogIHN1bW1hcnlfdGJsIDwtIGRhdGEuZnJhbWUoDQogICAgY2VudHJhbCA9IGNlbnRlciwNCiAgICB1cHBlcl85NSA9IHVwcGVyLA0KICAgIGxvd2VyXzA1ID0gbG93ZXIsDQogICAgbWF4ID0gbWF4X3ZhbCwNCiAgICBtaW4gPSBtaW5fdmFsLA0KICAgIExPUSA9IExPUSwNCiAgICAgcm93Lm5hbWVzID0gTlVMTA0KICApDQoNCiAgIyBCYXNlIGRlbnNpdHkgZm9yIGdyZXkgZmlsbA0KICBkZW5zIDwtIGRlbnNpdHkodmFsdWVzKQ0KICBkZW5zX2RmIDwtIGRhdGEuZnJhbWUoeCA9IGRlbnMkeCwgeSA9IGRlbnMkeSkNCiAgZGVuc19kZiA8LSBkZW5zX2RmICU+JQ0KICAgIG11dGF0ZShpbl9jcmkgPSB4ID49IGxvd2VyICYgeCA8PSB1cHBlcikNCiAgDQogIA0KICBjYXB0aW9uX3RleHQgPC0gcGFzdGUoDQogICJEZW5zaXR5IHBsb3RzIHNob3cgdGhlIGRpc3RyaWJ1dGlvbiBvZiBvYnNlcnZlZCBlbnZpcm9ubWVudGFsIGRhdGEuXG4iLA0KICAiVGhlIGJsdWUgZGFzaGVkIGxpbmUgaW5kaWNhdGVzIHRoZSBzZWxlY3RlZCBtZWFzdXJlIG9mIGNlbnRyYWwgdGVuZGVuY3lcbiIsDQogICIobWVhbiwgbWVkaWFuLCBvciBtb2RlKS4gVGhlIHJlZCBkb3QtZGFzaCBsaW5lIG1hcmtzIHRoZSBMaW1pdCBvZlxuIiwNCiAgIlF1YW50aWZpY2F0aW9uIChMT1EpLCBkZWZ1bHQgMC4wMDAxLiBCbGFjayBwb2ludHMgcmVwcmVzZW50IHRoZSByZWNvbW1lbmRlZCBkb3Nlc1xuIiwNCiAgImJhc2VkIG9uIHNwYWNpbmcgZnJvbSB0aGUgY2VudHJhbCB2YWx1ZS4gVGhlIHNoYWRlZCBncmV5IHJlZ2lvbiBkZW5vdGVzXG4iLA0KICAidGhlIDk1JSBjcmVkaWJsZSBpbnRlcnZhbCAoQ3JJKSBvZiB0aGUgZGF0YSBkaXN0cmlidXRpb24uIiwgc2VwID0gIiIpDQoNCiAgIyBQbG90DQogIGJhc2VfcGxvdCA8LSBnZ3Bsb3QoKSArDQogICAgZ2VvbV9kZW5zaXR5KGRhdGEgPSBkYXRhLCBhZXNfc3RyaW5nKHggPSB2YXJpYWJsZSksIHNpemUgPSAxLjIpICsNCiAgICBnZW9tX3JpYmJvbihkYXRhID0gZGVuc19kZiwgYWVzKHggPSB4LCB5bWluID0gMCwgeW1heCA9IGlmZWxzZShpbl9jcmksIHksIE5BKSksDQogICAgICAgICAgICAgICAgZmlsbCA9ICJncmV5ODAiLCBhbHBoYSA9IDAuNSkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGNlbnRlciwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gTE9RLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkb3RkYXNoIikgKw0KICAgIGdlb21fcG9pbnQoZGF0YSA9IGRvc2VfZGYsIGFlcyh4ID0gdmFsdWUsIHkgPSAwKSwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKw0KICAgIGdlb21fdGV4dChkYXRhID0gZG9zZV9kZiwgYWVzKHggPSB2YWx1ZSwgeSA9IDAuMDEsIGxhYmVsID0gZG9zZSksIHZqdXN0ID0gLTEpICsNCiAgICBnZ3RpdGxlKCJEb3NlIFNlbGVjdGlvbiAoUmF3IFNjYWxlKSIpICsNCiAgICB0aGVtZV9taW5pbWFsKCkgKw0KICAgIGxhYnMoY2FwdGlvbiA9IGNhcHRpb25fdGV4dCkgKw0KICAgIHRoZW1lKHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplID0gOSwgaGp1c3QgPSAwLCBsaW5laGVpZ2h0ID0gMS4xKSkNCg0KDQogIGRlbnNfbG9nIDwtIGRlbnNpdHkobG9nMTAodmFsdWVzKSkNCiAgZGVuc19kZl9sb2cgPC0gZGF0YS5mcmFtZSh4ID0gMTBeZGVuc19sb2ckeCwgeSA9IGRlbnNfbG9nJHkpDQogIA0KICBsb2dfcGxvdCA8LSBnZ3Bsb3QoKSArDQogICAgZ2VvbV9saW5lKGRhdGEgPSBkZW5zX2RmX2xvZywgYWVzKHggPSB4LCB5ID0geSksIHNpemUgPSAxLjIpICsNCiAgICBhbm5vdGF0ZSgicmVjdCIsIHhtaW4gPSBsb3dlciwgeG1heCA9IHVwcGVyLCB5bWluID0gMCwgeW1heCA9IG1heChkZW5zX2RmX2xvZyR5KSwgDQogICAgICAgICAgICAgZmlsbCA9ICJncmV5ODAiLCBhbHBoYSA9IDAuNCkgKw0KICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGNlbnRlciwgY29sb3IgPSAiYmx1ZSIsIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxKSArDQogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gTE9RLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkb3RkYXNoIikgKw0KICAgIGdlb21fcG9pbnQoZGF0YSA9IGRvc2VfZGYsIGFlcyh4ID0gdmFsdWUsIHkgPSAwKSwgc2l6ZSA9IDMsIGNvbG9yID0gImJsYWNrIikgKw0KICAgIGdlb21fdGV4dChkYXRhID0gZG9zZV9kZiwgYWVzKHggPSB2YWx1ZSwgeSA9IDAuMDEgKiBtYXgoZGVuc19kZl9sb2ckeSksIGxhYmVsID0gZG9zZSksIHZqdXN0ID0gLTEpICsNCiAgICBzY2FsZV94X2xvZzEwKCkgKw0KICAgIGdndGl0bGUoIkRvc2UgU2VsZWN0aW9uIChMb2cgU2NhbGUpIikgKw0KICAgIHRoZW1lX21pbmltYWwoKQ0KDQogIHBsb3RzIDwtIHByaW50KGJhc2VfcGxvdCArIGxvZ19wbG90KQ0KICANCiAgcGxvdHMNCiAgDQogIGNvdmVyZWRfcHJvcCA8LSBtZWFuKHZhbHVlcyA+PSBtaW4oZG9zZV9saXN0KSAmIHZhbHVlcyA8PSBtYXgoZG9zZV9saXN0KSkNCnN1bW1hcnlfdGJsJGRpc3RyaWJ1dGlvbl9jb3ZlcmVkIDwtIGNvdmVyZWRfcHJvcA0KDQogIHJldHVybihsaXN0KGRvc2VzID0gZG9zZV9kZiwgc3VtbWFyeSA9IHN1bW1hcnlfdGJsLCBwbG90ID0gcGxvdHMpKQ0KfQ0KYGBgDQoNCiMjIEV4YW1wbGUgdXNlDQoNCkhlcmUncyBhbiBleGFtcGxlIGRhdGEgc2V0IGJhc2VkIG9uIG9uZSBvZiB0aGUgY29tcG91bmRzIGluIHRoZSBkYXRhc2V0LCBmbHVveGV0aW5lDQoNCmBgYHtyfQ0KZmx1b3hldGluZV9zdXJmYWN3YXRlcnMgPC0gYWxsX2RhdGFiYXNlcyAlPiUgDQogIGRwbHlyOjpmaWx0ZXIobWF0cml4X2dyb3VwID09ICJzdXJmYWNld2F0ZXIiICYgY29tcG91bmRfbmFtZSA9PSAiZmx1b3hldGluZSIpICU+JSANCiAgZHBseXI6OmFycmFuZ2UodmFsdWUpICU+JSANCiAgZHBseXI6OmZpbHRlcih2YWx1ZSA+IDApDQpgYGANCg0KQXBwbHkgdG8gY29uY2VudHJhdGlvbiBkYXRhDQoNCmBgYHtyfQ0KbXlfZG9zZXMgPC0gc2VsZWN0X2Rvc2VzKA0KICBkYXRhID0gZmx1b3hldGluZV9zdXJmYWN3YXRlcnMsDQogIHZhcmlhYmxlID0gInZhbHVlIiwNCiAgY2VudHJhbF90ZW5kZW5jeSA9ICJtZWRpYW4iLA0KICBtaW5fc3BhY2luZyA9IDUsDQogIG1heF9kb3NlcyA9IDMsDQogIExPUSA9IDAuMDAwMSwNCiAgZGlyZWN0aW9uID0gImJvdGgiLA0KICBsaW1pdF90b19vYnNlcnZlZCA9IEZBTFNFDQopDQpgYGA=