Replication Project Information

Replication project based on:
Titelman, N. and Lauderdale, B.E. (2021a) Can citizens guess how other citizens vote based on demographic characteristics? Political Science Research and Methods, 11(2), 254-274. Available at: https://doi.org/10.1017/psrm.2021.53

Replication method using replication materials made available by authors:
Titelman, N. and Lauderdale, B.E. (2021b) Replication data for: Can citizens guess how other citizens vote based on demographic characteristics? [dataset]. Harvard Dataverse. Available at: https://doi.org/10.7910/DVN/3OWF6C

Workplace setup

YAML settings

output:
  html_document:
    code_download: true
    toc: true
    toc_depth: 2
    toc_float:
      collapsed: false
      smooth_scroll: true

Global chunk settings

knitr::opts_chunk$set(
  echo = TRUE,
  cache = TRUE,
  comment = NA,
  warning = FALSE,
  message = FALSE,
  fig.align = "center"
)

Versions of used packages

This project uses the renv package for reproducibility. Run renv::restore().
This will install all packages with the correct versions as specified in the renv.lock file.

renv::restore()

Libraries

# libraries supplied by authors replication materials
library(matlib)
library(DescTools)
library(survey)
library(dplyr)
library(ggplot2)
library(ggthemes)
library(gridExtra)
library(plotrix)
library(plyr)
library(coefplot)
library(questionr)
library(psych)
library(snakecase) 
library(stargazer)
library(texreg)
library(tidyr)
library(forcats)
library(stringr)
library(kableExtra)
library(naniar)
library(papaja)

# libraries for replication extension
library(tidyverse)
library(sjPlot)
library(lme4)
library(lmerTest)
library(patchwork)

Environment

[1] "R version 4.5.2 (2025-10-31 ucrt)"

For more details on project environment please see Appendix 1.

Introduction

The ability of citizens to infer political preferences and behaviours from social information is key to understanding how individuals navigate social and political environments. Research suggests that citizens with greater political knowledge, engagement, and cognitive resources are better able to process political information and make accurate judgements about others’ preferences (Lewis-Beck and Stegmaier, 2011). However, whether these resources translate into accurate prediction of others’ behaviour remains an open empirical question.

Titelman and Lauderdale (2021a) demonstrate that citizens can, to an extent, predict others’ voting behaviour using demographic information, while further linking individual demographics like political knowledge and attention to facilitate this forecasting ability, with politically engaged respondents found to achieve greater accuracy in their study. They use experimental profiles where respondents predict their voting behaviour based on given profile demographics.

This replication project will assess part of this paper relating to Table 1 of Titelman and Lauderdale’s analysis (2021a, p. 270), where they claimed to have found that accuracy of voting behaviour increased when certain respondent-level demographic characteristics were present, specifically political attention. Though they do not overstate any causal claim, their choice of regression analysis does not account for possible unobserved respondent-level heterogeneity.

Following the dimensions of replication outlined by Freese and Peterson (2017), this study primarily focuses on robustness, alongside an assessment of verifiability. This project begins by assessing whether the available replication package can reproduce the papers original results, finding partial verifiability due to inconsistencies in the data-cleaning and analysis pipeline. Although the authors provide replication materials, key stages of data processing and transformation are not fully documented, limiting complete reproduction from raw data. While this study evaluates verifiability using the provided replication materials, it does not constitute a full end-to-end reproduction from raw data sources, limiting the extent to which the complete data generation process can be independently verified.

Repeatability and generalisability are not tested within this project, relying exclusively on the original cleaned datasets provided rather than collecting new data or re-running their experimental design, and remains confined to the original experimental context of the 2017 UK political climate. The extent to which these findings apply to other political contexts, time periods or populations remain open to question, and this project does not evaluate whether these findings would reproduce under repeated data collection.

The primary aim of this project is to assess robustness by analysing the same data under alternative modelling assumptions. By changing the modelling strategy, this replication tests whether the authors’ findings are sensitive to different modelling assumptions. There is an argument for their chosen modelling strategy ignoring repeated observations per respondent. While Titelman and Lauderdale (2021a, p. 259) consider the implications of their experimental design for causal interpretation, they do not address a separate but important issue arising from the structure of their data: each respondent evaluates multiple profiles. This generates repeated observations nested within individual respondents, violating the assumption of independent observations underlying their chosen OLS models and potentially leading to underestimated standard errors (Finch, 2026; Maas & Hox, 2005). If the original results are partly driven by clustered observations, it is expected that multilevel models increase standard errors and reduce the statistical significance of some coefficients (Bickel, 2007; Raudenbush & Bryk, 2002; Snijders & Bosker, 2012). Based on prior research on clustered data structures and multilevel modelling (Gelman & Hill, 2007; Maas & Hox, 2005), it is expected that accounting for respondent-level clustering will increase standard errors and reduce the statistical significance of some predictors observed in the original OLS models. Substantively, while political attention and education are expected to remain positively associated with forecasting accuracy, their estimated effects are likely to be smaller and less precise once unobserved individual-level heterogeneity is accounted for. Therefore, this study extends the original analysis by examining whether its conclusions remain robust when accounting for the clustered structure of the experimental design through a multilevel modelling approach.

Data and Methods

Data

This analysis uses experimental replication datasets provided by Titelman and Lauderdale (2021b), combining data from the 2017 British Election Study (BES) face-to-face survey (Fieldhouse et al., 2019), and an online YouGov experiment. The replication package contains layered datafiles CleanBrexit.RData and CleanParties.RData.

Retrospectively within these, contain imputed Brexit.BES.Clean and Parties.BES.Clean data of BES-sourced demographic profiles, and the final analysis datasets Brexit.Experiment.Clean and Parties.Experiment.Clean containing respondents’ demographic characteristics and their voting predictions combined with the imputed BES.Clean data. These .Experiment.Clean datasets contain repeated observations as respondents evaluated multiple demographic profiles.

The supplied .RData files were loaded and chunks of the authors’ original analysis script appendix.rmd were executed to recreate the required outcome variables (see Appendix 2), which were not included in the authors original cleaned datasets. Coding was chunked and referenced to signal authorship of appendix.rmd code (Titelman & Lauderdale, 2021c).

# load Brexit Datafile
load("Data/InputData/CleanBrexit.RData")

# load Party Datafile
load("Data/InputData/CleanParties.RData")

Verification checks confirmed consistency with the original data and model specification, including treatment of political attention as numeric and demographic variables as categorical factors. Exact reproduction of reported statistical significance levels was not possible due to discrepancies between the supplied replication materials and published outputs; however, coefficient direction, model specification, and variable construction were successfully reproduced sufficiently to conduct the robustness extension (see Appendix 2, Table A1).

# run original models for verification
# brier - brexit 
modelBrier_brex <- lm(BrierScore ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                       + respondent_Age + respondent_Education + respondent_Gender + respondent_Region, 
                       weights = Brexit.Experiment.Clean$respondent_weights,
                       data = Brexit.Experiment.Clean)

# brier - party 
modelBrier_party <- lm(BrierScore ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                       + respondent_Age + respondent_Education + respondent_Gender + respondent_Region, 
                       weights = Parties.Experiment.Clean$respondent_weights,
                       data = Parties.Experiment.Clean)



# dichot - brexit 
modelDicho_brex <- lm(Correct.Dich.Guess.Brex ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                      + respondent_Age + respondent_Education + respondent_Gender + respondent_Region,
                      weights = fitted_BESCoef_BrexExp$respondent_weights, data = fitted_BESCoef_BrexExp)

# dichot - Party 
modelDicho_party <- lm(Correct.Dich.Guess.Party ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                      + respondent_Age + respondent_Education + respondent_Gender + respondent_Region,
                      weights = fitted_BESCoef_PartyExp$respondent_weights, data = fitted_BESCoef_PartyExp)

Four analytical datasets were subsequently constructed (brexit_brier, brexit_dicho, parties_brier, and parties_dicho) containing the variables required for the replicated and extended models.

# Create new dataframes relevant for replication project 


# 1. BREXIT DATA: New dataframe extracting and renaming only the core analytical variables
brexit_brier <- Brexit.Experiment.Clean %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    brier_score         = BrierScore,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# 2. BREXIT & DICHOTOMOUS DATA: New dataframe for Dicho analysis
brexit_dicho <- fitted_BESCoef_BrexExp %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    dicho_score         = Correct.Dich.Guess.Brex,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# 3. PARTIES EXPERIMENT DATA: New dataframe extracting only the core analytical variables
parties_brier <- Parties.Experiment.Clean %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    brier_score         = BrierScore,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# 4. PARTIES & DICHOTOMOUS: New dataframe for dicho analysis
parties_dicho <- fitted_BESCoef_PartyExp %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    dicho_score         = Correct.Dich.Guess.Party,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

Variables were renamed and subsetted while preserving the original coding structure, factor levels, and reference categories. Both new brexit_ and parties_ datasets contain identical predictor variable information. Datasets are distinguished by containing either brier or dicho dependent variables.

# recode variables for modelling and interpretation

# ensure pol_attention is a numeric variable in all datasets 
brexit_brier$pol_attention  <- as.numeric(brexit_brier$pol_attention)
brexit_dicho$pol_attention <- as.numeric(brexit_dicho$pol_attention)
parties_brier$pol_attention   <- as.numeric(parties_brier$pol_attention)
parties_dicho$pol_attention <- as.numeric(parties_dicho$pol_attention)


# Ensure respondent_id is set up as a factor variable in all datasets
brexit_brier$respondent_id  <- as.factor(brexit_brier$respondent_id)
brexit_dicho$respondent_id <- as.factor(brexit_dicho$respondent_id)
parties_brier$respondent_id   <- as.factor(parties_brier$respondent_id)
parties_dicho$respondent_id  <- as.factor(parties_dicho$respondent_id)

The replicated model examines predictors of respondent forecasting accuracy using two dependent variables: the continuous Brier Score, where lower values indicate greater prediction accuracy, and the Correct Dichotomised Guess measure, which captures directional accuracy on a 0–0.5–1 scale.

Figure 1 shows that Brier scores are distributed similarly across both experimental groups, with most responses concentrated toward lower values, indicating better accuracy among respondents. However, the spike present in both groups suggest that respondents may systematically overestimate the accuracy of their guesses.

Figure 2 demonstrates that correct predictions were the most common outcome in both experiments, although a substantial proportion of guesses were incorrect.

Independent variables include political attention, 2017 general election vote, EU referendum vote, age, education, gender, and region; their summary statistics are displayed below in Table 1 and Table 2.

Table 1: Cross-Tab of Respondent Voter History for Brexit (Left) and Parties (Right) Samples
Remain Leave Did not vote
Conservative 528 1173 39
Dont know 48 42 18
Green 60 24 6
Labour 1062 474 72
Liberal Democrat 258 33 3
Other 12 9 3
Plaid Cymru 15 6 0
Scottish National Party (SNP) 75 30 0
UK Independence Party (UKIP) 6 87 0
Remain Leave Did not vote
Conservative 594 1146 27
Dont know 60 87 9
Green 72 18 0
Labour 1092 354 81
Liberal Democrat 261 57 12
Other 18 12 3
Plaid Cymru 12 12 0
Scottish National Party (SNP) 126 36 3
UK Independence Party (UKIP) 0 75 0

Table 1 demonstrates variation in respondents’ previous voting behaviour and referendum choices, showing the connection between political voting patterns across the sample.

# Brexit sample distribution
edu_brex <- prop.table(table(brexit_brier$education)) * 100

# Parties sample distribution
edu_parties <- prop.table(table(parties_brier$education)) * 100

edu_brex_str <- paste(names(edu_brex), round(edu_brex, 1), "%", collapse = "; ")
edu_parties_str <- paste(names(edu_parties), round(edu_parties, 1), "%", collapse = "; ")

tibble(
  `Sample` = c("Brexit", "Parties"),
  `Education (%)` = c(edu_brex_str, edu_parties_str)
)
# A tibble: 2 × 2
  Sample  `Education (%)`                                                       
  <chr>   <chr>                                                                 
1 Brexit  None 5.5 %; Level 1 3.9 %; Level 2 19.6 %; Level 3 18.3 %; Level 4 7.…
2 Parties None 4.9 %; Level 1 3.8 %; Level 2 18.8 %; Level 3 19 %; Level 4 8.8 …
Table 2: Additional Respondent Demographics Summary Stats
Sample Total (N) Unique IDs Mean Political Attention (0-10) Mean Age (Years) Gender (% Female)
Brexit 5082 1694 6.07 49.11 55.61
Parties 5064 1688 6.09 49.36 55.69

Table 2 summaries the demographic characteristics used as predictors in the models.

Methods

Titelman and Lauderdale (2021a) employ Ordinary Least Squares (OLS) linear regressions to estimate whether respondent attributes predict forecasting accuracy. While this strategy assumes independent observations, because each respondent evaluated multiple demographic profiles, observations may be nested within individuals. Treating these evaluations as completely independent rows violates this assumption by failing to account for respondent-level characteristics that may influence guessing accuracy across experiments. Ignoring clustered data structures can lead to biased standard errors and overconfident inference (Raudenbush & Bryk, 2002; Snijders & Bosker, 2012). Multilevel models are widely recommended in such settings because they explicitly model within- and between-unit variation, producing more reliable estimates of uncertainty (Gelman & Hill, 2007).

To address this limitation, this replication extends the original analysis using Linear Mixed-Effects Models.

Before estimating the multilevel models, the replicated OLS specifications were re-estimated using the reconstructed datasets for verifiability with the original analytical framework (Freese & Peterson, 2007) (see Appendix 2, Table A2). After listwise deletion and application of supplied respondent weights, original analysis observations retain 3,308 for the Brexit experiment, and 3,394 for the Party experiment.

# run original models with new subsetted datasets for comparison

########## Brier Score ###########
# brier brexit
original_brier_brex <- lm(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = brexit_brier)

# brier party
original_brier_party <- lm(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = parties_brier)


######## Dichotomised ###########
# dicho brexit
original_dicho_brex <- lm(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = brexit_dicho)

# dicho party
original_dicho_party <- lm(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = parties_dicho)

A random intercept was introduced for respondent ID (1 | respondent_id), modelling individual-level heterogeneity and separates between-respondent from within-respondent variation. Respondent identifiers were retained from the original data and converted into a factor variable to enable the multilevel specification. Investigation of respondent_id variables show 1,694 individual respondents in the Brexit experiment, and 1,688 in the party experiment. Four multilevel models were subsequently run.

# Run multilevel model with respondent ID's as nested variable 

###### Brier #######
# mixed brier brexit model
mixed_brier_brex <- lmer(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = brexit_brier)

# mixed brier party model
mixed_brier_party <- lmer(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = parties_brier)

###### dicho ######
# dicho brexit
mixed_dicho_brex <- lmer(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = brexit_dicho)

# dicho party
mixed_dicho_party <- lmer(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = parties_dicho)

Model fit was assessed using AIC differences and likelihood ratio tests comparing models with and without respondent-level random effects (Finch, 2026). Multilevel approaches were appropriate for clustered experimental data because they provide more accurate uncertainty estimates and reduce the risk of misleading inference arising from underestimated standard errors.

# comparing OLS model fit against Multilevel model fit
# Brier Score models
fit_test_brexit_brier <- anova(mixed_brier_brex, original_brier_brex)
fit_test_party_brier  <- anova(mixed_brier_party, original_brier_party)

# Dicho models
fit_test_brexit_dicho <- anova(mixed_dicho_brex, original_dicho_brex)
fit_test_party_dicho <- anova(mixed_dicho_party, original_dicho_party)
Data: brexit_brier
Models:
original_brier_brex: brier_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region
mixed_brier_brex: brier_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region + (1 | respondent_id)
                    npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
original_brier_brex   31 1646.5 1835.7 -792.24    1584.5                     
mixed_brier_brex      32 1597.3 1792.7 -766.66    1533.3 51.159  1  8.518e-13
                       
original_brier_brex    
mixed_brier_brex    ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Data: parties_brier
Models:
original_brier_party: brier_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region
mixed_brier_party: brier_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region + (1 | respondent_id)
                     npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
original_brier_party   31 1480.8 1670.8 -709.40    1418.8                     
mixed_brier_party      32 1419.3 1615.5 -677.65    1355.3 63.498  1  1.605e-15
                        
original_brier_party    
mixed_brier_party    ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Data: brexit_dicho
Models:
original_dicho_brex: dicho_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region
mixed_dicho_brex: dicho_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region + (1 | respondent_id)
                    npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
original_dicho_brex   31 4802.2 4991.4 -2370.1    4740.2                     
mixed_dicho_brex      32 4781.8 4977.2 -2358.9    4717.8 22.323  1  2.304e-06
                       
original_dicho_brex    
mixed_dicho_brex    ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Data: parties_dicho
Models:
original_dicho_party: dicho_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region
mixed_dicho_party: dicho_score ~ pol_attention + past_vote_2017 + eu_ref_vote + age + education + gender + region + (1 | respondent_id)
                     npar    AIC    BIC  logLik -2*log(L)  Chisq Df Pr(>Chisq)
original_dicho_party   31 5049.8 5239.9 -2493.9    4987.8                     
mixed_dicho_party      32 5039.2 5235.3 -2487.6    4975.2 12.638  1   0.000378
                        
original_dicho_party    
mixed_dicho_party    ***
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Results

The multilevel model reveals that forecasting accuracy is partly an individual-level trait. Incorporating a random intercept for individual respondents reveals that several demographic associations were less robust once respondent-level clustering was accounted for. When interpreting Table 4 results, it is important to note that a negative brier coefficient and a positive dichotomised coefficient represent improved respondent accuracy.

Table 3: Test Fit Comparisons across Modeling Structures
Model Context & Comparison OLS AIC Multilevel AIC Chi-Square Significance (p-value)
Brexit Experiment: Brier Score 1646.5 1597.3 51.159 ***
Parties Experiment: Brier Score 1480.8 1419.3 63.498 ***
Brexit Experiment: Dichotomised Score 4802.2 4781.8 22.323 ***
Parties Experiment: Dichotomised Score 5049.8 5039.2 12.638 ***
Table 4. Multi-level Predictors of Respondent Characteristics Predicting Guess Accuracy (Brier Score and Correct Dichotomised Guess) Across Brexit and Party Experiments.
 
Brier Score
Brexit Exp.
Brier Score
Party Exp.
Correct Dichotomised Guess
Brexit Exp.
Correct Dichotomised Guess
Party Exp.
Coefficient Estimates Standard Error Estimates Standard Error Estimates Standard Error Estimates Standard Error
Intercept 0.404 *** 0.050 0.341 *** 0.053 0.459 *** 0.079 0.525 *** 0.086
Political Attention -0.007 * 0.003 -0.008 ** 0.003 0.007 0.004 0.011 ** 0.004
Party Vote: Labour 0.001 0.014 -0.004 0.014 0.021 0.023 0.005 0.023
Party Vote: Liberal
Democrat
-0.029 0.024 0.020 0.023 0.049 0.038 -0.011 0.037
Party Vote: SNP 0.046 0.042 -0.030 0.039 -0.077 0.066 0.068 0.064
Party Vote: Plaid Cymru -0.096 0.116 0.005 0.077 0.095 0.183 -0.157 0.124
Party Vote: UKIP 0.070 0.042 0.048 0.045 -0.032 0.067 -0.052 0.073
Party Vote: Green -0.008 0.046 -0.036 0.040 0.014 0.072 0.043 0.065
Party Vote: Other 0.072 0.076 -0.007 0.071 -0.151 0.121 0.000 0.114
Party Vote: Don’t Know -0.014 0.042 -0.025 0.040 0.033 0.066 0.031 0.064
EU Ref. Vote: Leave 0.014 0.014 0.019 0.013 -0.001 0.022 -0.018 0.022
EU Ref. Vote: Did Not
Vote
0.037 0.032 0.037 0.028 -0.010 0.051 -0.022 0.045
Age -0.000 0.000 -0.000 0.000 0.001 0.001 -0.000 0.001
Education Level: 1 -0.099 * 0.039 -0.045 0.042 0.172 ** 0.061 0.008 0.068
Education Level: 2 -0.033 0.028 0.006 0.030 0.044 0.044 -0.030 0.049
Education Level: 3 -0.084 ** 0.030 -0.014 0.031 0.105 * 0.046 -0.002 0.050
Education Level: 4 -0.096 ** 0.033 0.015 0.033 0.131 * 0.052 -0.065 0.054
Education Level: 5 and
Above
-0.080 ** 0.028 -0.003 0.030 0.104 * 0.044 -0.032 0.049
Education Level: Other -0.083 ** 0.029 -0.002 0.031 0.127 ** 0.046 -0.053 0.050
Gender: Female 0.001 0.012 -0.012 0.012 -0.006 0.019 0.002 0.019
Region: North West 0.042 0.034 0.023 0.034 -0.107 * 0.053 0.027 0.056
Region: Yorkshire and the
Humber
0.017 0.035 0.016 0.036 -0.064 0.055 0.055 0.058
Region: East Midlands -0.014 0.036 0.018 0.036 -0.024 0.057 0.009 0.058
Region: West Midlands 0.043 0.036 0.009 0.036 -0.103 0.057 0.047 0.059
Region: East of England 0.002 0.035 0.025 0.036 -0.055 0.054 -0.036 0.057
Region: London 0.050 0.034 0.046 0.034 -0.100 0.054 -0.013 0.056
Region: South East 0.029 0.033 0.012 0.034 -0.084 0.052 0.028 0.055
Region: South West 0.026 0.035 0.015 0.035 -0.074 0.056 0.037 0.057
Region: Wales -0.005 0.041 0.003 0.040 -0.103 0.064 0.044 0.066
Region: Scotland 0.013 0.039 0.042 0.038 -0.008 0.061 -0.029 0.061
Random Effects
σ2 0.08 0.07 0.21 0.21
τ00 0.01 respondent_id 0.01 respondent_id 0.02 respondent_id 0.01 respondent_id
ICC 0.11 0.12 0.08 0.06
N 1173 respondent_id 1208 respondent_id 1173 respondent_id 1208 respondent_id
Observations 3308 3394 3308 3394
Marginal R2 / Conditional R2 0.021 / 0.128 0.009 / 0.127 0.014 / 0.089 0.009 / 0.069
  • p<0.05   ** p<0.01   *** p<0.001

The random effect estimates indicate clustering in the data, as reflected in the interclass correlation coefficient (ICC), showing that between 6% and 12% of total variance is attributed to respondent-level clustering (Table 4). This variance indicates that treating repeated respondent guesses as independent observations violates OLS assumptions. The correct dichotomised guess variance (6-8%) is, however, smaller than the random effects for respondents Brier scores (11-12%). This is substantively important when considering Titealman and Lauderdale’s (2021a, p. 271) claims on “wisdom of crowds” versus individual inaccuracy, where respondents on aggregate are more accurate that individual brier accuracy scores. The variance suggests that, even when accounting for unobserved individual differences, this crowd wisdom argument is less affected by random effects. When considering individual responses, forecasting ability can not be entirely explained by observable characteristics. Some respondents appear to have a general tendency to make more accurate predictions across voting scenarios, potentially reflecting unmeasured characteristics.

Table 3 also shows an improved fit, with AIC reductions ranging from 10.6 - 63.5 points and significant likelihood ratio tests (p < .001) across all four multilevel specifications.

Comparing to the original OLS framework (see Appendix, Table A2), political attention is shown to be a highly robust predictor of forecasting ability, achieving high significance across both experiments. Under the multilevel specification however, coefficients reduce across all for models, including a global reduction in significance (Table 4). Though strength and significance decreases, the general strength and trend remains, finding the parties experiment continuing to yield the most accurate and significant results. This suggests that, while politically attentive citizens do appear to have an advantage in guessing others’ voting behaviour, especially for infering partisan alignment, when accounting for nested data, this advantage may be smaller than Titelman & Lauderdale (2021a) initially estimated. While political engagement likely improves exposure to political information and awareness of electoral patterns, part of this apparent relationship may reflect broader individual differences among highly engaged respondents.

Concurrently, education coefficients remained directionally consistent but several categories showed reduced statistical certainty (Table 4). In the Brexit OLS baseline, higher tiers of education operate as highly significant predictors of accuracy, consistently achieving high significance (p < 0.001) (Appendix Table A2). Under the multilevel estimation (Table 4), however, education coefficients show a reduction in respondent accuracy, falling once high significance thresholds (p < 0.001). Similar to the comparative findings on political attention, the trend remains the same despite losing power and significance. While higher educational attainment may provide resources that facilitate understanding political behaviour, this suggest that education alone does not appear to fully explain why some citizens are better at predicting other voters.

Conversely to respondents level of political attention most improving in the parties experiment, higher education appears to improve the accuracy of respondents guesses for Brexit voting. This consistent trend substantiates Titelman and Lauderdale’s (2021a) findings that increased cognitive resources improve guessing ability, where greater attention to politics appears to aid more when inferring partisan alignment, and higher education links more to understanding the cleavages of the Brexit referendum. However, while these characteristics appear to still matter, their effects are smaller and less certain than originally stated.

Furthermore, some subgroup differences disappear, suggesting demographic characteristics were capturing individual differences rather than true group effects. For example, UKIP voters appeared less accurate in the original model (Appendix Table A1), but after accounting for respondent-level tendencies, this difference was no longer distinguishable from random variation (Table 4). These findings suggest that some apparent differences between groups may not represent genuine differences in citizens’ ability to understand political behaviour, but instead reflect differences between individuals who happened to be concentrated within those groups.

These findings qualify Titelman and Lauderdale’s (2021a) argument that political sophistication enhances prediction of political differences to an extent, while suggesting a more nuanced story that individual-level capacities beyond measured demographic resources also contribute to this accuracy. The difference between marginal and conditional R² in Table 4 (eg, Brexit - 0.021/0.128 and 0.014/0.089) indicates that unmeasured respondent-level characteristics explain considerably more variation than the measured demographic predictors alone. Therefore, while demographic factors contribute to forecasting accuracy, much of the variation appears to occur at the individual level rather than being explained by observable characteristics.

Conclusion

This study aimed to assess the robustness of Titelman and Lauderdale’s (2021a) findings by examining whether their conclusions regarding respondent characteristic predictors of forecasting accuracy remain consistent under alternative modelling assumptions. While the original analysis suggests that politically attentive and informed respondents are better able to predict others’ voting behaviour, this replication has demonstrated these findings to be somewhat sensitive to model specification.

First, the replication confirmed partial verifiability of the original results. Although exact statistical significance outputs could not be fully reported due to inconsistencies in replication materials, the coefficients retained consistency with the published findings. This supports the authors’ central claim that political attention is positively associated with forecasting accuracy for both probabilistic and directional accuracy.

However, the extension to a multilevel modelling framework highlights an important limitations in the original approach. By accounting for the clustered structure of the data, where multiple observations are nested within individual respondents, the analysis provides a more appropriate representation of the experimental data-generating process. Consistent with expectations from prior methodological literature (Gelman & Hill, 2007; Maas & Hox, 2005), the inclusions of respondent-level random effects resulted in increased standard errors and reduced statistical significance for all predictors. This suggests that the original OLS models may have overstated the precision of their estimates by failing to account for within-respondent correlation.

Several limitations of this replication should be acknowledged. First, the analysis relies entirely on the authors’ cleaned datasets, rather than raw data: either non-imputed BES data, or collecting new experimental data. This restricts the ability to fully assess the transparency and completeness of the data preparation process. Second, the study does not test generalisability, as it is confined to the original UK-based experimental context. Finally, while the multilevel approach improves model specification, it does not resolve other potential sources of bias, such as the use of Multiple Imputation by Chained Equations (MICE) in Titelman and Lauderdles’s (2021a, p. 258) original hidden data preparation process, which introduces additional modelling assumptions into the construction of the profile data.

Importantly, the extension does not overturn the substantive conclusions made by Titelman & Lauderdale (2021a). Instead, it refines their interpretation, where demographic and political resource variables remain some significant predictors of forecasting ability, but they explain less variance once unobserved individuals differences are taken into account.

Overall, this study highlights the value of modelling assumptions in shaping quantitative research. While Titelman and Lauderdale’s (2021a) core findings are broadly supported, they are less robust than initially presented when accounting for the hierarchical structure of the data. This suggests that citizens’ ability to infer people’s voting behaviour is shaped by both political resources and unobserved individual capabilities. This highlights the value of replication and robustness checks in empirical research indicated by Freese and Peterson (2017), particularly when working with layered experimental designs. Future research could extend this analysis by applying similar models to different datasets, or by incorporating additional respondent-level characteristics to better capture the mechanisms underlying political forcasting accuracy.

References

Reference List

Bickel, R. (2007) Multilevel analysis for applied research: It’s just regression! 1st edn. New York: Guilford Press.

Fieldhouse, E., Green, J., Evans, G., Schmitt, H., van der Eijk, C., Mellon, J., Prosser, C. (2019) British Election Study, 2017: Face-to-Face Post-Election Survey [data collection]. UK Data Service. Available at: https://doi.org/10.5255/UKDA-SN-8418-1.

Finch, H. (2026) ‘Using multilevel modeling to understand nested data’, Perspectives on Early Childhood Psychology and Education, 8(2), Article 4. Available at: https://doi.org/10.58948/2834-8257.1076.

Freese, J. and Peterson, D. (2017) ‘Replication in social science’, Annual Review of Sociology, 43, pp. 147–165. Available at: https://doi.org/10.1146/annurev-soc-060116-053450.

Gelman, A. and Hill, J. (2007) Data analysis using regression and multilevel/hierarchical models. Cambridge: Cambridge University Press.

Lewis-Beck, M. S. and Stegmaier, M. (2011) Citizen forecasting: can UK voters see the future?. Electoral Studies 30, pp. 264–268.

Maas, C.J.M. and Hox, J.J. (2005) ‘Sufficient sample sizes for multilevel modeling’, Methodology, 1(3), pp. 86–92. Available at: https://doi.org/10.1027/1614-1881.1.3.86.

Raudenbush, S.W. and Bryk, A.S. (2002) Hierarchical linear models: Applications and data analysis methods. 2nd edn. Thousand Oaks, CA: Sage Publications.

Snijders, T.A.B. and Bosker, R.J. (2012) Multilevel analysis: An introduction to basic and advanced multilevel modeling. 2nd edn. London: Sage Publications.

Titelman, N. and Lauderdale, B.E. (2021a) ‘Can citizens guess how other citizens vote based on demographic characteristics?’, Political Science Research and Methods, 11(2), pp. 254–274. Available at: https://doi.org/10.1017/PSRM.2021.53.

Titelman, N. and Lauderdale, B.E. (2021b) Replication data for: Can citizens guess how other citizens vote based on demographic characteristics? [Dataset]. Harvard Dataverse. Available at: https://doi.org/10.7910/DVN/3OWF6C.

Titelman, N. and Lauderdale, B.E. (2021c) Replication data for: Can citizens guess how other citizens voted based on demographic characteristics? [data set]. Harvard Dataverse, V1; appendix.rmd [fileName]. Available at: https://doi.org/10.7910/DVN/3OWF6C.

Appendix

Appendix 1. Environment (full information)

# Detailed information of environment
sessionInfo()
R version 4.5.2 (2025-10-31 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 26200)

Matrix products: default
  LAPACK version 3.12.1

locale:
[1] LC_COLLATE=English_United Kingdom.utf8 
[2] LC_CTYPE=English_United Kingdom.utf8   
[3] LC_MONETARY=English_United Kingdom.utf8
[4] LC_NUMERIC=C                           
[5] LC_TIME=English_United Kingdom.utf8    

time zone: Europe/London
tzcode source: internal

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

other attached packages:
 [1] patchwork_1.3.2   lmerTest_3.2-1    lme4_1.1-38       sjPlot_2.9.0     
 [5] lubridate_1.9.5   purrr_1.2.1       readr_2.1.6       tibble_3.3.1     
 [9] tidyverse_2.0.0   papaja_0.1.5      tinylabels_0.2.5  naniar_1.1.0     
[13] kableExtra_1.4.1  stringr_1.6.0     forcats_1.0.1     tidyr_1.3.2      
[17] texreg_1.39.5     stargazer_5.2.3   snakecase_0.11.1  psych_2.6.5      
[21] questionr_0.8.2   coefplot_1.2.9    plyr_1.8.9        plotrix_3.8-14   
[25] gridExtra_2.3.1   ggthemes_5.2.0    ggplot2_4.0.3     dplyr_1.2.1      
[29] survey_4.5        survival_3.8-3    Matrix_1.7-4      DescTools_0.99.60
[33] matlib_1.0.1      knitr_1.51        rmarkdown_2.30   

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3  rstudioapi_0.18.0   jsonlite_2.0.0     
  [4] datawizard_1.3.0    magrittr_2.0.4      farver_2.1.2       
  [7] nloptr_2.2.1        fs_1.6.6            vctrs_0.7.1        
 [10] minqa_1.2.8         base64enc_0.1-6     effectsize_1.0.1   
 [13] htmltools_0.5.9     haven_2.5.5         cellranger_1.1.0   
 [16] sjmisc_2.8.11       Formula_1.2-6       sass_0.4.10        
 [19] bslib_0.10.0        htmlwidgets_1.6.4   rootSolve_1.8.2.4  
 [22] cachem_1.1.0        mime_0.13           lifecycle_1.0.5    
 [25] pkgconfig_2.0.3     sjlabelled_1.2.0    R6_2.6.1           
 [28] fastmap_1.2.0       rbibutils_2.4.1     shiny_1.14.0       
 [31] numDeriv_2016.8-1.1 digest_0.6.39       Exact_3.3          
 [34] useful_1.2.7        textshaping_1.0.4   labeling_0.4.3     
 [37] timechange_0.4.0    httr_1.4.7          abind_1.4-8        
 [40] compiler_4.5.2      proxy_0.4-29        withr_3.0.2        
 [43] S7_0.2.1            carData_3.0-6       DBI_1.2.3          
 [46] performance_0.16.0  highr_0.11          MASS_7.3-65        
 [49] gld_2.6.8           tools_4.5.2         otel_0.2.0         
 [52] httpuv_1.6.17       visdat_0.6.0        glue_1.8.0         
 [55] nlme_3.1-168        promises_1.5.0      reshape2_1.4.5     
 [58] generics_0.1.4      gtable_0.3.6        labelled_2.16.0    
 [61] tzdb_0.5.0          class_7.3-23        data.table_1.18.2.1
 [64] lmom_3.2            hms_1.1.4           xml2_1.5.2         
 [67] car_3.1-5           pillar_1.11.1       later_1.4.8        
 [70] mitools_2.4         splines_4.5.2       lattice_0.22-9     
 [73] renv_1.2.3          tidyselect_1.2.1    miniUI_0.1.2       
 [76] reformulas_0.4.4    svglite_2.2.2       xfun_0.56          
 [79] expm_1.0-0          stringi_1.8.7       yaml_2.3.12        
 [82] boot_1.3-32         evaluate_1.0.5      codetools_0.2-20   
 [85] cli_3.6.5           xtable_1.8-8        parameters_0.28.3  
 [88] systemfonts_1.3.1   Rdpack_2.6.6        jquerylib_0.1.4    
 [91] Rcpp_1.1.2          readxl_1.4.5        parallel_4.5.2     
 [94] rgl_1.3.36          bayestestR_0.17.0   viridisLite_0.4.3  
 [97] mvtnorm_1.3-3       scales_1.4.0        e1071_1.7-17       
[100] insight_1.4.6       rlang_1.3.0         mnormt_2.1.2       

Appendix 2. Entire R code used in this project

# Key Libraries for replication

# rmarkdown
library(rmarkdown)
library(knitr)

knitr::opts_chunk$set(
  echo = TRUE,
  cache = TRUE,
  comment = NA,
  warning = FALSE,
  message = FALSE,
  fig.align = "center"
)
renv::restore()
# libraries supplied by authors replication materials
library(matlib)
library(DescTools)
library(survey)
library(dplyr)
library(ggplot2)
library(ggthemes)
library(gridExtra)
library(plotrix)
library(plyr)
library(coefplot)
library(questionr)
library(psych)
library(snakecase) 
library(stargazer)
library(texreg)
library(tidyr)
library(forcats)
library(stringr)
library(kableExtra)
library(naniar)
library(papaja)

# libraries for replication extension
library(tidyverse)
library(sjPlot)
library(lme4)
library(lmerTest)
library(patchwork)

version[['version.string']]
# load Brexit Datafile
load("Data/InputData/CleanBrexit.RData")

# load Party Datafile
load("Data/InputData/CleanParties.RData")
# Run Titelman & Lauderdale's `appendix.rmd` code to create outcome variables neccesary for running models


###########################################################
########## [Titelman & Lauderdale (2021c) code] ###########
###################### Start: ############################# 

################## START CHUNK 3 ##########################

# The following chunk allows for comparisons of mean guess by profile attribute with the same mean in the BES

# Predicting the (weighted) mean guess by level of each attribute

MeansAge<-lm(profile_Leave~profile_AgeGroup, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
levels(Brexit.Experiment.Clean$profile_AgeGroup)
new.dat <- data.frame(profile_AgeGroup=c("18-29", "30-44", "45-59", "60-74", "75 +"))
MeansAge1<-data.frame(new.dat, predict(MeansAge, newdata = new.dat, interval = 'confidence'))
MeansAgeBES<-lm(profile_Leave~profile_AgeGroup, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansAgeBES2<-data.frame(new.dat, predict(MeansAgeBES, newdata = new.dat, interval = 'confidence'))

MeansAgeBES2$Probabilities<-"BES"
MeansAge1$Probabilities<-"Guess"
MeansAge.Bind<-bind_rows(MeansAge1, MeansAgeBES2)

MeansReligion<-lm(profile_Leave~profile_Religion, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Religion=c("Roman Catholic","Church of England/Anglican/Episcopal",
                                         "Hindu","Islam",
                                         "Methodist","Presbyterian/Church of Scotland",
                                         "No religion","Unknown" ))
MeansReligion1<-data.frame(new.dat, predict(MeansReligion, newdata = new.dat, interval = 'confidence'))
MeansReligion1$profile_Religion<-factor(MeansReligion1$profile_Religion, levels=MeansReligion1$profile_Religion[order(MeansReligion1$fit)])
MeansReligionBES<-lm(profile_Leave~profile_Religion, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansReligionBES2<-data.frame(new.dat, predict(MeansReligionBES, newdata = new.dat, interval = 'confidence'))

MeansReligionBES2$Probabilities<-"BES"
MeansReligion1$Probabilities<-"Guess"
MeansReligion.Bind<-bind_rows(MeansReligion1, MeansReligionBES2)


MeansEthnicity<-lm(profile_Leave~profile_Ethnicity, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Ethnicity=c("Asian","Black",                            
                                          "Mixed","White",                            
                                          "Unknown"))
MeansEthnicity1<-data.frame(new.dat, predict(MeansEthnicity, newdata = new.dat, interval = 'confidence'))
MeansEthnicity1$profile_Ethnicity<-factor(MeansEthnicity1$profile_Ethnicity, levels=MeansEthnicity1$profile_Ethnicity[order(MeansEthnicity1$fit)])
MeansEthnicityBES<-lm(profile_Leave~profile_Ethnicity, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansEthnicityBES2<-data.frame(new.dat, predict(MeansEthnicityBES, newdata = new.dat, interval = 'confidence'))


MeansEthnicityBES2$Probabilities<-"BES"
MeansEthnicity1$Probabilities<-"Guess"
MeansEthnicity.Bind<-bind_rows(MeansEthnicity1, MeansEthnicityBES2)


MeansGender<-lm(profile_Leave~profile_Gender, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Gender=c("Male", "Female"))
MeansGender1<-data.frame(new.dat, predict(MeansGender, newdata = new.dat, interval = 'confidence'))
MeansGenderBES<-lm(profile_Leave~profile_Gender, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansGenderBES2<-data.frame(new.dat, predict(MeansGenderBES, newdata = new.dat, interval = 'confidence'))

MeansGenderBES2$Probabilities<-"BES"
MeansGender1$Probabilities<-"Guess"
MeansGender.Bind<-bind_rows(MeansGender1, MeansGenderBES2)

MeansEducation2<-lm(profile_Leave~profile_Education, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Education=c('Holds a university degree', 'Does not hold a university degree'))
MeansEducation3<-data.frame(new.dat, predict(MeansEducation2, newdata = new.dat, interval = 'confidence'))
MeansEducationBES<-lm(profile_Leave~profile_Education, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansEducationBES2<-data.frame(new.dat, predict(MeansEducationBES, newdata = new.dat, interval = 'confidence'))

MeansEducationBES2$Probabilities<-"BES"
MeansEducation3$Probabilities<-"Guess"
MeansEducation.Bind<-bind_rows(MeansEducation3, MeansEducationBES2)

MeansRegion<-lm(profile_Leave~profile_Region, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Region=factor(c("London", "North East",
                                              "North West","Scotland",
                                              "South East","South West",
                                              "the East Midlands","the East of England",
                                              "Wales","West Midlands",
                                              "Yorkshire & Humber")))
MeansRegion1<-data.frame(new.dat, predict(MeansRegion, newdata = new.dat, interval = 'confidence'))
MeansRegion1$profile_Region<-factor(MeansRegion1$profile_Region, levels=MeansRegion1$profile_Region[order(MeansRegion1$fit)])
MeansRegionBES<-lm(profile_Leave~profile_Region, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansRegionBES2<-data.frame(new.dat, predict(MeansRegionBES, newdata = new.dat, interval = 'confidence'))
MeansRegionBES2$Probabilities<-"BES"
MeansRegion1$Probabilities<-"Guess"
MeansRegion.Bind<-bind_rows(MeansRegion1, MeansRegionBES2)


MeansRegion.Bind<-bind_rows(MeansRegion1, MeansRegionBES2)
Realvote<-Brexit.Experiment.Clean %>%
  dplyr::  group_by(profile_Region) %>%
  dplyr::   summarise(fit = mean(profile_regional_real_Leave, na.rm=T))
Realvote$Probabilities<-"Real"

MeansRegion.Bind2<-bind_rows(MeansRegion.Bind, Realvote)
MeansRegion.Bind2$Probabilities<-factor(MeansRegion.Bind2$Probabilities,
                                        levels=c("BES"[order(1)],"Real"[order(2)],"Guess"[order(3)]))


MeansIncome<-lm(profile_Leave~profile_AnnualHouseholdIncome, data=Brexit.Experiment.Clean, w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_AnnualHouseholdIncome=factor(c("Less than 5,199"[order(1)],
                                                             "Between 5,200 and 15,599"[order(2)],
                                                             "Between 15,600 and 25,999"[order(3)],
                                                             "Between 26,000 and 36,399"[order(4)],
                                                             "Between 36,400 and 44,999"[order(5)],
                                                             "Between 45,000 and 59,999"[order(6)],
                                                             "Between 60,000 and 99,999"[order(7)],
                                                             "Greater than 100,000"[order(8)] )))

MeansIncome2<-data.frame(new.dat, predict(MeansIncome, newdata = new.dat, interval = 'confidence'))
MeansIncomeBES<-lm(profile_Leave~profile_AnnualHouseholdIncome, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansIncomeBES2<-data.frame(new.dat, predict(MeansIncomeBES, newdata = new.dat, interval = 'confidence'))

MeansIncomeBES2$Probabilities<-"BES"
MeansIncome2$Probabilities<-"Guess"
MeansIncome.Bind<-bind_rows(MeansIncome2, MeansIncomeBES2)

MeansIncome.Bind$profile_AnnualHouseholdIncome<-factor(MeansIncome.Bind$profile_AnnualHouseholdIncome,
                                                       levels = c("Less than 5,199"[order(1)],
                                                                  "Between 5,200 and 15,599"[order(2)],
                                                                  "Between 15,600 and 25,999"[order(3)],
                                                                  "Between 26,000 and 36,399"[order(4)],
                                                                  "Between 36,400 and 44,999"[order(5)],
                                                                  "Between 45,000 and 59,999"[order(6)],
                                                                  "Between 60,000 and 99,999"[order(7)],
                                                                  "Greater than 100,000"[order(8)] ))

MeansClass<-lm(profile_Leave~profile_SubClass, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_SubClass=c("Working class", "Middle class"))
MeansClass1<-data.frame(new.dat, predict(MeansClass, newdata = new.dat, interval = 'confidence'))
MeansClassBES<-lm(profile_Leave~profile_SubClass, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansClassBES2<-data.frame(new.dat, predict(MeansClassBES, newdata = new.dat, interval = 'confidence'))

MeansClassBES2$Probabilities<-"BES"
MeansClass1$Probabilities<-"Guess"
MeansClass.Bind<-bind_rows(MeansClass1, MeansClassBES2)

MeansFamClass<-lm(profile_Leave~profile_SubFamClass, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_SubFamClass=c("Family working class", "Family middle class"))
MeansFamClass1<-data.frame(new.dat, predict(MeansFamClass, newdata = new.dat, interval = 'confidence'))
MeansFamClassBES<-lm(profile_Leave~profile_SubFamClass, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansFamClassBES2<-data.frame(new.dat, predict(MeansFamClassBES, newdata = new.dat, interval = 'confidence'))

MeansFamClassBES2$Probabilities<-"BES"
MeansFamClass1$Probabilities<-"Guess"
MeansFamClass.Bind<-bind_rows(MeansFamClass1, MeansFamClassBES2)


MeansHomeStatus<-lm(profile_Leave~profile_HomeStatus, data=Brexit.Experiment.Clean,  w=Brexit.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_HomeStatus=c("Rents home", "Owns home"))
MeansHomeStatus1<-data.frame(new.dat, predict(MeansHomeStatus, newdata = new.dat, interval = 'confidence'))
MeansHomeStatusBES<-lm(profile_Leave~profile_HomeStatus, data=Brexit.BES.Clean,  w=Brexit.BES.Clean$profile_weights)
MeansHomeStatusBES2<-data.frame(new.dat, predict(MeansHomeStatusBES, newdata = new.dat, interval = 'confidence'))

MeansHomeStatusBES2$Probabilities<-"BES"
MeansHomeStatus1$Probabilities<-"Guess"
MeansHomeStatus.Bind<-bind_rows(MeansHomeStatus1, MeansHomeStatusBES2)


#Parties

MeansAge<-lm(profile_Con~profile_AgeGroup, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
levels(Parties.Experiment.Clean$profile_AgeGroup)
new.dat <- data.frame(profile_AgeGroup=c("18-29", "30-44", "45-59", "60-74", "75 +"))
MeansAge1<-data.frame(new.dat, predict(MeansAge, newdata = new.dat, interval = 'confidence'))
MeansAgeBES<-lm(profile_Con~profile_AgeGroup, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansAgeBES2<-data.frame(new.dat, predict(MeansAgeBES, newdata = new.dat, interval = 'confidence'))

MeansAgeBES2$Probabilities<-"BES"
MeansAge1$Probabilities<-"Guess"
MeansAgeP.Bind<-bind_rows(MeansAge1, MeansAgeBES2)

MeansEducationP<-lm(profile_Con~profile_Education, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Education=c('Holds a university degree', 'Does not hold a university degree'))
MeansEducationP2<-data.frame(new.dat, predict(MeansEducationP, newdata = new.dat, interval = 'confidence'))
MeansEducationPBES<-lm(profile_Con~profile_Education, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansEducationPBES2<-data.frame(new.dat, predict(MeansEducationPBES, newdata = new.dat, interval = 'confidence'))

MeansEducationPBES2$Probabilities<-"BES"
MeansEducationP2$Probabilities<-"Guess"
MeansEducationP.Bind<-bind_rows(MeansEducationP2, MeansEducationPBES2)


MeansIncomeP<-lm(profile_Con~profile_AnnualHouseholdIncome, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame((profile_AnnualHouseholdIncome=factor(c("Less than 5,199"[order(1)],
                                                              "Between 5,200 and 15,599"[order(2)],
                                                              "Between 15,600 and 25,999"[order(3)],
                                                              "Between 26,000 and 36,399"[order(4)],
                                                              "Between 36,400 and 44,999"[order(5)],
                                                              "Between 45,000 and 59,999"[order(6)],
                                                              "Between 60,000 and 99,999"[order(7)],
                                                              "Greater than 100,000"[order(8)]))))

MeansIncomeP2<-data.frame(new.dat, predict(MeansIncomeP, newdata = new.dat, interval = 'confidence'))
MeansIncomePBES<-lm(profile_Con~profile_AnnualHouseholdIncome, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansIncomePBES2<-data.frame(new.dat, predict(MeansIncomePBES, newdata = new.dat, interval = 'confidence'))


MeansIncomePBES2$Probabilities<-"BES"
MeansIncomeP2$Probabilities<-"Guess"
MeansIncomeP.Bind<-bind_rows(MeansIncomeP2, MeansIncomePBES2)

MeansIncomeP.Bind$profile_AnnualHouseholdIncome<-factor(MeansIncome.Bind$profile_AnnualHouseholdIncome,
                                                        levels = c("Less than 5,199"[order(1)],
                                                                   "Between 5,200 and 15,599"[order(2)],
                                                                   "Between 15,600 and 25,999"[order(3)],
                                                                   "Between 26,000 and 36,399"[order(4)],
                                                                   "Between 36,400 and 44,999"[order(5)],
                                                                   "Between 45,000 and 59,999"[order(6)],
                                                                   "Between 60,000 and 99,999"[order(7)],
                                                                   "Greater than 100,000"[order(8)] ))



MeansRegionP<-lm(profile_Con~profile_Region, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Region=factor(c("London", "North East",
                                              "North West","Scotland",
                                              "South East","South West",
                                              "the East Midlands","the East of England",
                                              "Wales","West Midlands",
                                              "Yorkshire & Humber")))
MeansRegionP1<-data.frame(new.dat, predict(MeansRegionP, newdata = new.dat, interval = 'confidence'))
MeansRegionP1$profile_Region<-factor(MeansRegionP1$profile_Region, levels=MeansRegionP1$profile_Region[order(MeansRegionP1$fit)])
MeansRegionPBES<-lm(profile_Con~profile_Region, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansRegionPBES2<-data.frame(new.dat, predict(MeansRegionPBES, newdata = new.dat, interval = 'confidence'))


MeansRegionPBES2$Probabilities<-"BES"
MeansRegionP1$Probabilities<-"Guess"
MeansRegionP.Bind<-bind_rows(MeansRegionP1, MeansRegionPBES2)
Realvote<-Parties.Experiment.Clean %>%
  dplyr::  group_by(profile_Region) %>%
  dplyr::   summarise(fit = mean(profile_regional_real_Con, na.rm=T))
Realvote$Probabilities<-"Real"

MeansRegionP2.Bind<-bind_rows(MeansRegionP.Bind, Realvote)
MeansRegionP2.Bind$Probabilities<-factor(MeansRegion.Bind2$Probabilities,
                                         levels=c("BES"[order(1)],"Real"[order(2)],"Guess"[order(3)]))


MeansClassP<-lm(profile_Con~profile_SubClass, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_SubClass=c("Working class", "Middle class"))
MeansClassP1<-data.frame(new.dat, predict(MeansClassP, newdata = new.dat, interval = 'confidence'))
MeansClassPBES<-lm(profile_Con~profile_SubClass, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansClassPBES2<-data.frame(new.dat, predict(MeansClassPBES, newdata = new.dat, interval = 'confidence'))

MeansClassPBES2$Probabilities<-"BES"
MeansClassP1$Probabilities<-"Guess"
MeansClassP.Bind<-bind_rows(MeansClassP1, MeansClassPBES2)


MeansFamClassP<-lm(profile_Con~profile_SubFamClass, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_SubFamClass=c("Family working class", "Family middle class"))
MeansFamClassP1<-data.frame(new.dat, predict(MeansFamClassP, newdata = new.dat, interval = 'confidence'))
MeansFamClassPBES<-lm(profile_Con~profile_SubFamClass, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansFamClassPBES2<-data.frame(new.dat, predict(MeansFamClassPBES, newdata = new.dat, interval = 'confidence'))

MeansFamClassPBES2$Probabilities<-"BES"
MeansFamClassP1$Probabilities<-"Guess"
MeansFamClassP.Bind<-bind_rows(MeansFamClassP1, MeansFamClassPBES2)

MeansHomeStatusP<-lm(profile_Con~profile_HomeStatus, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_HomeStatus=c("Rents home", "Owns home"))
MeansHomeStatusP1<-data.frame(new.dat, predict(MeansHomeStatusP, newdata = new.dat, interval = 'confidence'))
MeansHomeStatusPBES<-lm(profile_Con~profile_HomeStatus, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansHomeStatusPBES2<-data.frame(new.dat, predict(MeansHomeStatusPBES, newdata = new.dat, interval = 'confidence'))

MeansHomeStatusPBES2$Probabilities<-"BES"
MeansHomeStatusP1$Probabilities<-"Guess"
MeansHomeStatusP.Bind<-bind_rows(MeansHomeStatusP1, MeansHomeStatusPBES2)


MeansReligionP<-lm(profile_Con~profile_Religion, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Religion=c("Roman Catholic","Church of England/Anglican/Episcopal",
                                         "Hindu","Islam",
                                         "Methodist","Presbyterian/Church of Scotland",
                                         "No religion","Unknown" ))
MeansReligionP1<-data.frame(new.dat, predict(MeansReligionP, newdata = new.dat, interval = 'confidence'))
MeansReligionP1$profile_Religion<-factor(MeansReligionP1$profile_Religion, levels=MeansReligionP1$profile_Religion[order(MeansReligionP1$fit)])
MeansReligionPBES<-lm(profile_Con~profile_Religion, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansReligionPBES2<-data.frame(new.dat, predict(MeansReligionPBES, newdata = new.dat, interval = 'confidence'))

MeansReligionPBES2$Probabilities<-"BES"
MeansReligionP1$Probabilities<-"Guess"
MeansReligionP.Bind<-bind_rows(MeansReligionP1, MeansReligionPBES2)


MeansEthnicityP<-lm(profile_Con~profile_Ethnicity, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat<-data.frame(profile_Ethnicity=factor(c("Asian","Black",                            
                                               "Mixed","White",                            
                                               "Unknown")))
MeansEthnicityP1<-data.frame(new.dat, predict(MeansEthnicityP, newdata = new.dat, interval = 'confidence'))
MeansEthnicityP1$profile_Ethnicity<-factor(MeansEthnicityP1$profile_Ethnicity, levels=MeansEthnicityP1$profile_Ethnicity[order(MeansEthnicityP1$fit)])
MeansEthnicityPBES<-lm(profile_Con~profile_Ethnicity, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansEthnicityPBES2<-data.frame(new.dat, predict(MeansEthnicityPBES, newdata = new.dat, interval = 'confidence'))

MeansEthnicityPBES2$Probabilities<-"BES"
MeansEthnicityP1$Probabilities<-"Guess"
MeansEthnicityP.Bind<-bind_rows(MeansEthnicityP1, MeansEthnicityPBES2)


MeansGenderP<-lm(profile_Con~profile_Gender, data=Parties.Experiment.Clean,  w=Parties.Experiment.Clean$respondent_weights)
new.dat <- data.frame(profile_Gender=c("Male", "Female"))
MeansGenderP1<-data.frame(new.dat, predict(MeansGenderP, newdata = new.dat, interval = 'confidence'))
MeansGenderPBES<-lm(profile_Con~profile_Gender, data=Parties.BES.Clean,  w=Parties.BES.Clean$profile_weights)
MeansGenderPBES2<-data.frame(new.dat, predict(MeansGenderPBES, newdata = new.dat, interval = 'confidence'))

MeansGenderPBES2$Probabilities<-"BES"
MeansGenderP1$Probabilities<-"Guess"
MeansGenderP.Bind<-bind_rows(MeansGenderP1, MeansGenderPBES2)

################ END CHUNK 3 ############################

############### START CHUNK 4 ###########################

#The following chunk allows for a comparison between BES and guesses, modelling all profile attributes together. Models are estaimted in logit and linear form. Linear model lead to invalid predictions for the binary vote choice for some profiles


# Brexit Linear model, predicting guessed probability of leave with porfiles attributes as predictors
GuessedLeave<- lm(profile_Leave ~ profile_Gender+profile_AgeGroup+profile_Education+profile_Region+profile_SubClass+profile_SubFamClass+profile_HomeStatus+profile_Religion+profile_Ethnicity+profile_AnnualHouseholdIncome, weights=Brexit.Experiment.Clean$respondent_weights, data = Brexit.Experiment.Clean)
BESLeave<- lm(profile_Leave ~ profile_Gender+profile_AgeGroup+profile_Education+profile_Region+profile_SubClass+profile_SubFamClass+profile_HomeStatus+profile_Religion+profile_Ethnicity+profile_AnnualHouseholdIncome, weights=Brexit.BES.Clean$profile_weights, data = Brexit.BES.Clean)

# Brexit Logit model
Brexit.Experiment.Clean$LeaveProp <- Brexit.Experiment.Clean$profile_Leave/100

#Brexit.Sep is a dataset created to ease later combination
Brexit.Sep<- Brexit.Experiment.Clean %>%
  select(LeaveProp, starts_with("profile"), respondent_weights)
colnames(Brexit.Sep)<-paste0(colnames(Brexit.Sep), "_")

GuessedLeaveLogit0 <- glm("LeaveProp_ ~ profile_Gender_ +profile_AgeGroup_ +profile_Education_ +profile_Region_ +profile_SubClass_ +profile_SubFamClass_ +profile_HomeStatus_ +profile_Religion_ +profile_Ethnicity_ +profile_AnnualHouseholdIncome_", weights=Brexit.Sep$respondent_weights_, data = Brexit.Sep, family=quasibinomial(link="logit"))

# Same regression with Brexit.Experiment.Clean dataset (guesses model)
GuessedLeaveLogit<-glm(LeaveProp ~ profile_Gender +profile_AgeGroup +profile_Education +profile_Region +profile_SubClass +profile_SubFamClass +profile_HomeStatus +profile_Religion +profile_Ethnicity +profile_AnnualHouseholdIncome, weights=Brexit.Experiment.Clean$respondent_weights, data = Brexit.Experiment.Clean, family=quasibinomial(link="logit"))


# Same regression with Brexit.BES.Clean dataset (BES model)
Brexit.BES.Clean$Leave.BES.Prop <- Brexit.BES.Clean$profile_Leave/100

#Brexit.Sep.BES is a dataset created to ease later combination
Brexit.Sep.BES<- Brexit.BES.Clean %>%
  select(Leave.BES.Prop, starts_with("profile"))
colnames(Brexit.Sep.BES)<-paste0(colnames(Brexit.Sep.BES), "_")

BESLeaveLogit0 <- glm("Leave.BES.Prop_ ~ profile_Gender_ +profile_AgeGroup_ +profile_Education_ +profile_Region_ +profile_SubClass_ +profile_SubFamClass_ +profile_HomeStatus_ +profile_Religion_ +profile_Ethnicity_ +profile_AnnualHouseholdIncome_", weights=Brexit.Sep.BES$profile_weights_, data = Brexit.Sep.BES, family=quasibinomial(link="logit"))

# Same regression with Brexit.BES.Clean dataset (BES model)
BESLeaveLogit <- glm(Leave.BES.Prop ~ profile_Gender +profile_AgeGroup +profile_Education +profile_Region +profile_SubClass +profile_SubFamClass +profile_HomeStatus +profile_Religion +profile_Ethnicity +profile_AnnualHouseholdIncome, weights=Brexit.BES.Clean$profile_weights, data = Brexit.BES.Clean, family=quasibinomial(link="logit"))


# Parties Linear models
GuessedConservative <- lm(profile_Con ~ profile_Gender+profile_AgeGroup+profile_Education+profile_Region+profile_SubClass+profile_SubFamClass+profile_HomeStatus+profile_Religion+profile_Ethnicity+profile_AnnualHouseholdIncome ,weights = Parties.Experiment.Clean$respondent_weights,data = Parties.Experiment.Clean)
RealConservative<- lm(profile_Con ~ profile_Gender+profile_AgeGroup+profile_Education+profile_Region+profile_SubClass+profile_SubFamClass+profile_HomeStatus+profile_Religion+profile_Ethnicity+profile_AnnualHouseholdIncome, weights=Parties.BES.Clean$profile_weights, data = Parties.BES.Clean)

# Parties Logstics models
Parties.Experiment.Clean$ConProp <- Parties.Experiment.Clean$profile_Con/100

# Party.Sep dataset created to to ease combination later
Party.Sep<- Parties.Experiment.Clean %>%
  select(ConProp, starts_with("profile"), respondent_weights)
colnames(Party.Sep)<-paste0(colnames(Party.Sep), "_")

GuessedConservativeLogit0 <- glm("ConProp_ ~ profile_Gender_ +profile_AgeGroup_ +profile_Education_ +profile_Region_ +profile_SubClass_ +profile_SubFamClass_ +profile_HomeStatus_ +profile_Religion_ +profile_Ethnicity_ +profile_AnnualHouseholdIncome_", weights=Party.Sep$respondent_weights_, data = Party.Sep, family=quasibinomial(link="logit"))

# Same regression with Parties.Experiment.Clean (guesses)
GuessedConservativeLogit <- glm(ConProp ~ profile_Gender +profile_AgeGroup  +profile_Education  +profile_Region  +profile_SubClass  +profile_SubFamClass  +profile_HomeStatus  +profile_Religion  +profile_Ethnicity  +profile_AnnualHouseholdIncome , weights=Parties.Experiment.Clean$respondent_weights, data = Parties.Experiment.Clean, family=quasibinomial(link="logit"))

#Same regression with BES model
Parties.BES.Clean$profile_Con.Prop <- Parties.BES.Clean$profile_Con/100
Party.Sep.BES<- Parties.BES.Clean %>%
  select(starts_with("profile"))
colnames(Party.Sep.BES)<-paste0(colnames(Party.Sep.BES), "_")

RealConservativeLogit0 <- glm("profile_Con.Prop_ ~ profile_Gender_ +profile_AgeGroup_ +profile_Education_ +profile_Region_ +profile_SubClass_ +profile_SubFamClass_ +profile_HomeStatus_ +profile_Religion_ +profile_Ethnicity_ +profile_AnnualHouseholdIncome_", weights=Party.Sep.BES$profile_weights_, data = Party.Sep.BES, family=quasibinomial(link="logit"))

RealConservativeLogit <- glm(profile_Con.Prop ~ profile_Gender +profile_AgeGroup +profile_Education +profile_Region +profile_SubClass +profile_SubFamClass +profile_HomeStatus +profile_Religion +profile_Ethnicity +profile_AnnualHouseholdIncome, weights=Parties.BES.Clean$profile_weights_, data = Parties.BES.Clean, family=quasibinomial(link="logit"))

############## END CHUNK 4 #############################

############## START CHUNK 5 ###########################

#This chunk allows for comparisons between the predicted probabilities of profiles voting in a certain way, implied by the BES model and the Guesses model.

#Brexit experiment
fitted_BES_Brex<-predict(BESLeaveLogit, newdata=Brexit.BES.Clean, type="response")
predicted_Experiment_Brex <- predict(GuessedLeaveLogit , newdata=Brexit.BES.Clean,type="response")
fitted_BES_Brexit<-data.frame("profile"= c(1:1695), fitted_BES_Brex)
predicted_Experiment_Brexit<-data.frame("profile"= c(1:1695), predicted_Experiment_Brex)
Relation.Models.Brexit<-merge(fitted_BES_Brexit, predicted_Experiment_Brexit, by="profile")


#Parties experiment
fitted_BES_Con<- predict(RealConservativeLogit, newdata=Parties.BES.Clean,type="response")
predicted_Experiment_Con <- predict(GuessedConservativeLogit, newdata=Parties.BES.Clean,type="response")

fitted_BES_Conservative<-data.frame("profile"= c(1:1362), fitted_BES_Con)
predicted_Experiment_Conservative<-data.frame("profile"= c(1:1362), predicted_Experiment_Con)


Relation.Models.Con<-merge(fitted_BES_Conservative, predicted_Experiment_Conservative, by="profile")

################# END CHUNK 5 ########################


################ START CHUNK 6 #######################

#Individual Brier Score Brexit
Brexit.Experiment.Clean$BrierScore<- ((Brexit.Experiment.Clean$profile_Leave/100)-Brexit.Experiment.Clean$profile_Leave.Real)^2

BrierScoreTotal<-weighted.mean(Brexit.Experiment.Clean$BrierScore, w=Brexit.Experiment.Clean$respondent_weights, na.rm = T)


#Individual Brier Score Parties
Parties.Experiment.Clean$BrierScore<- (Parties.Experiment.Clean$profile_Conservative.Real-(Parties.Experiment.Clean$profile_Con/100))^2

Parties.Experiment.Clean$BrierScoreBinary <-  ((0.5+0.5*sign((Parties.Experiment.Clean$profile_Con - 50)))-Parties.Experiment.Clean$profile_Conservative.Real)^2

BrierScoreTotalParties<-mean(Parties.Experiment.Clean$BrierScore, w=Parties.Experiment.Clean$respondent_weights, na.rm = T)

################# END CHUNK 6 ##########################



################ START CHUNK 7 #########################
#Average Brier score

#Predicted probabilities for every profile, using BES model
fitted_BESCoef_BrexExp<-predict(BESLeaveLogit, newdata=Brexit.Experiment.Clean,type="response")
fitted_BESCoef_BrexExp<-data.frame(Brexit.Experiment.Clean, fitted_BESCoef_BrexExp)
fitted_BESCoef_PartyExp<-predict(RealConservativeLogit, newdata=Parties.Experiment.Clean,type="response")
fitted_BESCoef_PartyExp<-data.frame(Parties.Experiment.Clean, fitted_BESCoef_PartyExp)

fitted_BESCoef_BrexExp<-fitted_BESCoef_BrexExp %>%
  dplyr::rename(fittedBrexExp = fitted_BESCoef_BrexExp)
fitted_BESCoef_PartyExp<-fitted_BESCoef_PartyExp %>%
  dplyr::rename(fittedPartyExp = fitted_BESCoef_PartyExp)

#Brier score Parties
fitted_BESCoef_PartyExp$BrierScore.fitted<- (fitted_BESCoef_PartyExp$fittedPartyExp-(fitted_BESCoef_PartyExp$profile_Con/100))^2
BrierScoreTotalParty.fitted<-mean(fitted_BESCoef_PartyExp$BrierScore.fitted, w=Parties.Experiment.Clean$respondent_weights, na.rm = T)

#Brier Score Brexit
fitted_BESCoef_BrexExp$BrierScore.fitted<- (fitted_BESCoef_BrexExp$fittedBrexExp-(fitted_BESCoef_BrexExp$profile_Leave/100))^2
BrierScoreTotalBrex.fitted<-mean(fitted_BESCoef_BrexExp$BrierScore.fitted, w=Parties.Experiment.Clean$respondent_weights, na.rm = T)

####### ~ [section removed from Chunk 7 due to separate analysis] ~ ##############

#Preparing data for dichotomous analysis in Parties experiment

fitted_BESCoef_BrexExp<- fitted_BESCoef_BrexExp %>%
  mutate(profile_Leave.Dich = case_when( profile_Leave<50 ~ "Remain",
                                         profile_Leave>50 ~ "Leave",
                                         profile_Leave==50 ~ "50-50"))
fitted_BESCoef_PartyExp<- fitted_BESCoef_PartyExp %>%
  mutate(profile_Con.Dich = case_when( profile_Con<50 ~ "Labour",
                                       profile_Con>50 ~ "Conservative",
                                       profile_Con==50 ~ "50-50"))

fitted_BESCoef_BrexExp <- fitted_BESCoef_BrexExp %>%
  mutate(profile_Leave.RealN = case_when(profile_Leave.Real == 1 ~ "Leave" , 
                                         profile_Leave.Real == 0 ~ "Remain"))

#Creating variable that equals 1 for correct dichotomous guess (and 0.5 for "50-50"). Brexit
fitted_BESCoef_BrexExp <- fitted_BESCoef_BrexExp %>%
  mutate(Correct.Dich.Guess.Brex = case_when(profile_Leave.Dich=="50-50" ~ 0.5,
                                             profile_Leave.Dich==profile_Leave.RealN ~ 1,
                                             profile_Leave.Dich!=profile_Leave.RealN ~ 0))

dclus1<-svydesign(ids=~0, weights=fitted_BESCoef_BrexExp$respondent_weights, data=fitted_BESCoef_BrexExp)
meanleave<-svymean(~Correct.Dich.Guess.Brex, dclus1 ,deff=F, na.rm =T)
CI<-confint(meanleave)
CI.Correct.Brex.Dich<-data.frame("Correct"="Brexit", "fit"=meanleave[1]*100, "lwr"=CI[1,1]*100,"upr"=CI[1,2]*100)

#Creating variable that equals 1 for correct dichotomous guess (and 0.5 for "50-50"). Parties
fitted_BESCoef_PartyExp <- fitted_BESCoef_PartyExp %>%
  mutate(profile_Conservative.RealN = case_when(profile_Conservative.Real == 1 ~ "Conservative" , 
                                                profile_Conservative.Real == 0 ~ "Labour"))
fitted_BESCoef_PartyExp <- fitted_BESCoef_PartyExp %>%
  mutate(Correct.Dich.Guess.Party = case_when(profile_Con.Dich=="50-50" ~ 0.5,
                                              profile_Con.Dich==profile_Conservative.RealN ~ 1,
                                              profile_Con.Dich!=profile_Conservative.RealN ~ 0))

dclus1<-svydesign(ids=~0, weights=fitted_BESCoef_PartyExp$respondent_weights, data=fitted_BESCoef_PartyExp)
meanCon<-svymean(~Correct.Dich.Guess.Party, dclus1 ,deff=F, na.rm =T)
CI<-confint(meanCon)
CI.Correct.Party.Dich<-data.frame("Correct"="Party", "fit"=meanCon[1]*100, "lwr"=CI[1,1]*100,"upr"=CI[1,2]*100)

##### ~ [End of Chunk 7 removed due to separate analysis] ~ ####

# set political attention to factor 


Parties.Experiment.Clean$respondent_PoliticalAttention<-as.numeric(as.character(Parties.Experiment.Clean$respondent_PoliticalAttention))

Brexit.Experiment.Clean$respondent_PoliticalAttention<-as.numeric(as.character(Brexit.Experiment.Clean$respondent_PoliticalAttention))

fitted_BESCoef_BrexExp$respondent_PoliticalAttention<-as.numeric(as.character(fitted_BESCoef_BrexExp$respondent_PoliticalAttention))

fitted_BESCoef_PartyExp$respondent_PoliticalAttention<-as.numeric(as.character(fitted_BESCoef_PartyExp$respondent_PoliticalAttention))

################# END CHUNK 7 ##################################

###################### End: ###############################
########## [Titelman & Lauderdale (2021c) code] ###########
###########################################################

#check levels of variables being used
levels(Brexit.Experiment.Clean$respondent_pastvote_2017)
levels(fitted_BESCoef_BrexExp$respondent_pastvote_2017)


levels(Brexit.Experiment.Clean$respondent_pastvote_EURef)
levels(fitted_BESCoef_BrexExp$respondent_pastvote_EURef)


levels(Brexit.Experiment.Clean$respondent_Gender) 
levels(fitted_BESCoef_BrexExp$respondent_Gender)


levels(Brexit.Experiment.Clean$respondent_Education)
levels(fitted_BESCoef_BrexExp$respondent_Education)


levels(Brexit.Experiment.Clean$respondent_Region)
levels(fitted_BESCoef_BrexExp$respondent_Region)
# run original models for verification
# brier - brexit 
modelBrier_brex <- lm(BrierScore ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                       + respondent_Age + respondent_Education + respondent_Gender + respondent_Region, 
                       weights = Brexit.Experiment.Clean$respondent_weights,
                       data = Brexit.Experiment.Clean)

# brier - party 
modelBrier_party <- lm(BrierScore ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                       + respondent_Age + respondent_Education + respondent_Gender + respondent_Region, 
                       weights = Parties.Experiment.Clean$respondent_weights,
                       data = Parties.Experiment.Clean)



# dichot - brexit 
modelDicho_brex <- lm(Correct.Dich.Guess.Brex ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                      + respondent_Age + respondent_Education + respondent_Gender + respondent_Region,
                      weights = fitted_BESCoef_BrexExp$respondent_weights, data = fitted_BESCoef_BrexExp)

# dichot - Party 
modelDicho_party <- lm(Correct.Dich.Guess.Party ~ respondent_PoliticalAttention + respondent_pastvote_2017 + respondent_pastvote_EURef 
                      + respondent_Age + respondent_Education + respondent_Gender + respondent_Region,
                      weights = fitted_BESCoef_PartyExp$respondent_weights, data = fitted_BESCoef_PartyExp)


sjPlot::tab_model(
  modelBrier_brex, modelBrier_party, modelDicho_brex, modelDicho_party,
  file = "Outputs/DataAppendixOutputs/tab_A1.html",
  digits = 3,
  title = "Table A1. Verification of Original Models Predicting Guess Accuracy Across Brexit and Party Experiments.",
  pred.labels = c(
    "(Intercept)" = "Intercept",
    "respondent_PoliticalAttention" = "Political Attention",
    "respondent_pastvote_2017Labour" = "Party Vote: Labour",
    "respondent_pastvote_2017Liberal Democrat" = "Party Vote: Liberal Democrat",
    "respondent_pastvote_2017Scottish National Party (SNP)" = "Party Vote: SNP",
    "respondent_pastvote_2017Plaid Cymru" = "Party Vote: Plaid Cymru",
    "respondent_pastvote_2017UK Independence Party (UKIP)" = "Party Vote: UKIP",
    "respondent_pastvote_2017Green" = "Party Vote: Green",
    "respondent_pastvote_2017Other" = "Party Vote: Other",
    "respondent_pastvote_2017Don’t know" = "Party Vote: Don't Know",
    "respondent_pastvote_EURefLeave" = "EU Ref. Vote: Leave",
    "respondent_pastvote_EURefDid not vote" = "EU Ref. Vote: Did Not Vote",
    "respondent_Age" = "Age",
    "respondent_EducationLevel 1" = "Education Level: 1",
    "respondent_EducationLevel 2" = "Education Level: 2",
    "respondent_EducationLevel 3" = "Education Level: 3",
    "respondent_EducationLevel 4" = "Education Level: 4",
    "respondent_EducationLevel 5 and above" = "Education Level: 5 and Above",
    "respondent_EducationOther" = "Education Level: Other",
    "respondent_GenderFemale" = "Gender: Female",
    "respondent_RegionNorth West" = "Region: North West",
    "respondent_RegionYorkshire and the Humber" = "Region: Yorkshire and the Humber",
    "respondent_RegionEast Midlands" = "Region: East Midlands",
    "respondent_RegionWest Midlands" = "Region: West Midlands",
    "respondent_RegionEast of England" = "Region: East of England",
    "respondent_RegionLondon" = "Region: London",
    "respondent_RegionSouth East" = "Region: South East",
    "respondent_RegionSouth West" = "Region: South West",
    "respondent_RegionWales" = "Region: Wales",
    "respondent_RegionScotland" = "Region: Scotland"
    ),
  dv.labels = c(
    "<div style='text-align: center;'><b>Brier Score</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Brexit Exp.</div>",
    "<div style='text-align: center;'><b>Brier Score</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Party Exp.</div>", 
    "<div style='text-align: center;'><b>Correct Dichotomised Guess</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Brexit Exp.</div>", 
    "<div style='text-align: center;'><b>Correct Dichotomised Guess</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Party Exp.</div>"
  ),
  string.pred = "Coefficient",
  show.ci = FALSE,
  show.se = TRUE,
  string.se = "Standard Error",
  p.style = "stars",
  CSS = list(
    css.colheaders = "text-align: center; vertical-align: bottom;"
  )
)
# Create new dataframes relevant for replication project 


# 1. BREXIT DATA: New dataframe extracting and renaming only the core analytical variables
brexit_brier <- Brexit.Experiment.Clean %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    brier_score         = BrierScore,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# 2. BREXIT & DICHOTOMOUS DATA: New dataframe for Dicho analysis
brexit_dicho <- fitted_BESCoef_BrexExp %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    dicho_score         = Correct.Dich.Guess.Brex,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# 3. PARTIES EXPERIMENT DATA: New dataframe extracting only the core analytical variables
parties_brier <- Parties.Experiment.Clean %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    brier_score         = BrierScore,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# 4. PARTIES & DICHOTOMOUS: New dataframe for dicho analysis
parties_dicho <- fitted_BESCoef_PartyExp %>%
  select(
    respondent_id       = respondent_ID,
    weights             = respondent_weights,
    dicho_score         = Correct.Dich.Guess.Party,
    pol_attention       = respondent_PoliticalAttention,
    past_vote_2017      = respondent_pastvote_2017,
    eu_ref_vote         = respondent_pastvote_EURef,
    age                 = respondent_Age,
    education           = respondent_Education,
    gender              = respondent_Gender,
    region              = respondent_Region
  )

# class check before modelling 
class(brexit_brier$respondent_id) # make a factor
class(brexit_brier$brier_score)
class(brexit_brier$pol_attention) # make numeric
class(brexit_brier$past_vote_2017)
class(brexit_brier$eu_ref_vote)
class(brexit_brier$age)
class(brexit_brier$education)
class(brexit_brier$gender)
class(brexit_brier$region)

class(brexit_dicho$dicho_score)

# check rows 
nrow(brexit_brier)
nrow(parties_brier)
# recode variables for modelling and interpretation

# ensure pol_attention is a numeric variable in all datasets 
brexit_brier$pol_attention  <- as.numeric(brexit_brier$pol_attention)
brexit_dicho$pol_attention <- as.numeric(brexit_dicho$pol_attention)
parties_brier$pol_attention   <- as.numeric(parties_brier$pol_attention)
parties_dicho$pol_attention <- as.numeric(parties_dicho$pol_attention)


# Ensure respondent_id is set up as a factor variable in all datasets
brexit_brier$respondent_id  <- as.factor(brexit_brier$respondent_id)
brexit_dicho$respondent_id <- as.factor(brexit_dicho$respondent_id)
parties_brier$respondent_id   <- as.factor(parties_brier$respondent_id)
parties_dicho$respondent_id  <- as.factor(parties_dicho$respondent_id)



# check levels of all factor vs (set baseline categories to same as original models for ease of comparison)
levels(brexit_brier$past_vote_2017)
levels(brexit_dicho$past_vote_2017)
levels(parties_brier$past_vote_2017)
levels(parties_dicho$past_vote_2017)

levels(brexit_brier$eu_ref_vote)
levels(brexit_brier$education)
levels(brexit_brier$gender)
levels(brexit_brier$region)


# graphs made for each experiment (brexit/party) then combined using patchwork package


# Brexit Experiment

brex_brier_plot <- ggplot(data = brexit_brier, aes(x = brier_score)) +
  geom_histogram(
    binwidth = 0.05,        
    fill = "#2c3e50",       
    color = "white",        
    alpha = 0.8             
  ) +
  theme_minimal(base_size = 13) + 
  theme(
    plot.title = element_text(face = "bold", hjust= 0.5), 
    panel.grid.minor = element_blank(),
    axis.title.x = element_text(size = 11),
    axis.title.y = element_text(size = 11),
  ) +
  labs(
    title = "Brexit Experiment",
    x = "Brier Score (0 - 1)",
    y = "Count"
  )


# Parties Experiment 

party_brier_plot <- ggplot(data = parties_brier, aes(x = brier_score)) +
  geom_histogram(
    binwidth = 0.05,        
    fill = "#2c3e50",       
    color = "white",        
    alpha = 0.8             
  ) +
  theme_minimal(base_size = 13) + 
  theme(
    plot.title = element_text(face = "bold", hjust = 0.5), 
    panel.grid.minor = element_blank(),
    axis.title.x = element_text(size = 11),
    axis.title.y = element_text(size = 11),
  ) +
  labs(
    title = "Parties Experiment",
    x = "Brier Score (0 - 1)",
    y = "Count"  
  )


# align the y-axis
brex_brier_plot_aligned <- brex_brier_plot + coord_cartesian(ylim = c(0, 600))
party_brier_plot_aligned <- party_brier_plot + coord_cartesian(ylim = c(0, 600))



# Combine both histograms using patchwork package # - dv1 plot
dv1 <- (brex_brier_plot_aligned + party_brier_plot_aligned) +
  plot_annotation(
    title = "Figure 1: Overall Distribution of Brier Scores",
    subtitle = "Comparing prediction accuracy across both experimental groups",
    caption = "Note: A lower Brier Score indicates better prediction accuracy.",
    theme = theme(
      plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
      plot.subtitle = element_text(size = 13, face = "italic", hjust = 0.5),
      plot.caption = element_text(size = 11, face = "italic", hjust = 0.5, margin = margin(t = 15))
    )
  )

ggsave(filename = "Outputs/Results/fig_1.png", dv1)

dv1

# Make Distribution plots for Dicho variables for both brexit and party
# combine plots

# bar chart 
brex_dicho_plot <- ggplot(
  brexit_dicho %>% 
    dplyr::filter(!is.na(dicho_score)),
  aes(x = factor(dicho_score))
) +
  geom_bar(fill = "#4C78A8", aes(y = after_stat(prop), group = 1)) +
  scale_x_discrete(
    labels = c(
      "0" = "Incorrect",
      "0.5" = "50–50 (Unsure)",
      "1" = "Correct"
    )
  ) +
  scale_y_continuous(
    labels = scales::percent,
    limits = c(0, 1),
    breaks = seq(0, 1, 0.25)
  ) +
  labs(
    x = "Directional Accuracy",
    y = "Proportion",
    title = "Brexit Experiment"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust= 0.5),
    axis.title.x = element_text(size = 11),
    axis.title.y = element_text(size = 11),
    legend.position = "none"
  )

# Parties Experiment 

#bar chart
party_dicho_plot <- ggplot(
  parties_dicho %>% 
    dplyr::filter(!is.na(dicho_score)),
  aes(x = factor(dicho_score))
) +
  geom_bar(fill = "#4C78A8", aes(y = after_stat(prop), group = 1)) +
  scale_x_discrete(
    labels = c(
      "0" = "Incorrect",
      "0.5" = "50–50 (Unsure)",
      "1" = "Correct"
    )
  ) +
  scale_y_continuous(
    labels = scales::percent,
    limits = c(0, 1),
    breaks = seq(0, 1, 0.25)
  ) +
  labs(
    x = "Directional Accuracy",
    y = "Proportion",
    title = "Party Experiment"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", hjust= 0.5),
    axis.title.x = element_text(size = 11),
    axis.title.y = element_text(size = 11),
    legend.position = "none"
  )

# patchwork two plots together - dv2 plot 
dv2 <- brex_dicho_plot + party_dicho_plot +
  plot_layout(axes = "collect") +
  plot_annotation(
    title = "Figure 2: Directional Accuracy of Respondent Guesses",
    subtitle = "Distribution of Correct Dichotomised Guess scores across both Experiments",
    theme = theme(
      plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
      plot.subtitle = element_text(size = 13, face = "italic", hjust = 0.5)
    ))

ggsave(filename = "Outputs/Results/fig_2.png", dv2)

dv2

# Generate the cross-tabulations

# clean the NAs
clean_brex_tab_df <- brexit_brier %>%
  mutate(past_vote_2017 = str_replace_all(as.character(past_vote_2017), "Don’t know", "Dont know")) %>%
  filter(!past_vote_2017 %in% c("N/A", "N/A_duplicated_9"))

clean_party_tab_df <- parties_brier %>%
  mutate(past_vote_2017 = str_replace_all(as.character(past_vote_2017), "Don’t know", "Dont know")) %>%
  filter(!past_vote_2017 %in% c("N/A", "N/A_duplicated_9"))


brexit_tab  <- table(clean_brex_tab_df$past_vote_2017, clean_brex_tab_df$eu_ref_vote)
parties_tab <- table(clean_party_tab_df$past_vote_2017, clean_party_tab_df$eu_ref_vote)


# Combine them 
# format = "html" ensures the HTML page aligns them horizontally
tab1 <- knitr::kable(
  list(brexit_tab, parties_tab),
  format = "html",
  caption = "**Table 1: Cross-Tab of Respondent Voter History for Brexit (Left) and Parties (Right) Samples**",
  col.names = colnames(brexit_tab)
)

save_kable(tab1, "Outputs/Results/tab_1.html")

tab1

# Brexit sample distribution
edu_brex <- prop.table(table(brexit_brier$education)) * 100

# Parties sample distribution
edu_parties <- prop.table(table(parties_brier$education)) * 100

edu_brex_str <- paste(names(edu_brex), round(edu_brex, 1), "%", collapse = "; ")
edu_parties_str <- paste(names(edu_parties), round(edu_parties, 1), "%", collapse = "; ")

tibble(
  `Sample` = c("Brexit", "Parties"),
  `Education (%)` = c(edu_brex_str, edu_parties_str)
)



# key background predictors
iv_stats <- tibble(
  `Sample` = c("Brexit", "Parties"),
  `Total (N)` = c(nrow(brexit_brier), nrow(parties_brier)),
  `Unique IDs` = c(
    length(unique(brexit_brier$respondent_id)),
    length(unique(parties_brier$respondent_id))
  ),
  `Mean Political Attention (0-10)` = c(
    mean(brexit_brier$pol_attention, na.rm = TRUE),
    mean(parties_brier$pol_attention, na.rm = TRUE)
  ),
  `Mean Age (Years)` = c(
    mean(brexit_brier$age, na.rm = TRUE),
    mean(parties_brier$age, na.rm = TRUE)
  ),
  `Gender (% Female)` = c(
    mean(brexit_brier$gender == "Female", na.rm = TRUE) * 100,
    mean(parties_brier$gender == "Female", na.rm = TRUE) * 100
  )
)

tab2 <- knitr::kable(iv_stats, digits = 2, 
             caption = "**Table 2: Additional Respondent Demographics Summary Stats**")

save_kable(tab2, "Outputs/Results/tab_2.html")

tab2
# run original models with new subsetted datasets for comparison

########## Brier Score ###########
# brier brexit
original_brier_brex <- lm(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = brexit_brier)

# brier party
original_brier_party <- lm(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = parties_brier)


######## Dichotomised ###########
# dicho brexit
original_dicho_brex <- lm(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = brexit_dicho)

# dicho party
original_dicho_party <- lm(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region, 
                      weights = weights,
                      data = parties_dicho)

# original table results

tab_model(
  original_brier_brex, original_brier_party, original_dicho_brex, original_dicho_party,
  file = "Outputs/DataAppendixOutputs/tab_A2.html",
  digits = 3,
  title = "Table A2. Verification of Original Models with New Dataframes of Respondent Characteristics Predicting Guess Accuracy Across Brexit and Party Experiments.",
  pred.labels = c(
    "(Intercept)" = "Intercept",
    "pol_attention" = "Political Attention",
    "past_vote_2017Labour" = "Party Vote: Labour",
    "past_vote_2017Scottish National Party (SNP)" = "Party Vote: SNP",
    "past_vote_2017Liberal Democrat" = "Party Vote: Liberal Democrat",
    "past_vote_2017Green" = "Party Vote: Green",
    "past_vote_2017Plaid Cymru" = "Party Vote: Plaid Cymru",
    "past_vote_2017UK Independence Party (UKIP)" = "Party Vote: UKIP",
    "past_vote_2017Other" = "Party Vote: Other",
    "past_vote_2017Don’t know" = "Party Vote: Don't Know",
    "eu_ref_voteLeave" = "EU Ref. Vote: Leave",
    "eu_ref_voteDid not vote" = "EU Ref. Vote: Did Not Vote",
    "age" = "Age",
    "educationLevel 1" = "Education Level: 1",
    "educationLevel 2" = "Education Level: 2",
    "educationLevel 3" = "Education Level: 3",
    "educationLevel 4" = "Education Level: 4",
    "educationLevel 5 and above" = "Education Level: 5 and Above",
    "educationOther" = "Education Level: Other",
    "genderFemale" = "Gender: Female",
    "regionNorth West" = "Region: North West",
    "regionYorkshire and the Humber" = "Region: Yorkshire and the Humber",
    "regionEast Midlands" = "Region: East Midlands",
    "regionWest Midlands" = "Region: West Midlands",
    "regionEast of England" = "Region: East of England",
    "regionLondon" = "Region: London",
    "regionSouth East" = "Region: South East",
    "regionSouth West" = "Region: South West",
    "regionWales" = "Region: Wales",
    "regionScotland" = "Region: Scotland"
    ),
  dv.labels = c(
    "<div style='text-align: center;'><b>Brier Score</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Brexit Exp.</div>",
    "<div style='text-align: center;'><b>Brier Score</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Party Exp.</div>", 
    "<div style='text-align: center;'><b>Correct Dichotomised Guess</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Brexit Exp.</div>", 
    "<div style='text-align: center;'><b>Correct Dichotomised Guess</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Party Exp.</div>"
  ),
  string.pred = "Coefficient",
  show.ci = FALSE,
  show.se = TRUE,
  string.se = "Standard Error",
  p.style = "stars",
  CSS = list(
    css.colheaders = "text-align: center; vertical-align: bottom;"
  )
)


# check length of unique respondent_id 
length(unique(brexit_brier$respondent_id))
length(unique(brexit_dicho$respondent_id))

length(unique(parties_dicho$respondent_id))
length(unique(parties_brier$respondent_id))
# Run multilevel model with respondent ID's as nested variable 

###### Brier #######
# mixed brier brexit model
mixed_brier_brex <- lmer(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = brexit_brier)

# mixed brier party model
mixed_brier_party <- lmer(brier_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = parties_brier)

###### dicho ######
# dicho brexit
mixed_dicho_brex <- lmer(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = brexit_dicho)

# dicho party
mixed_dicho_party <- lmer(dicho_score ~ pol_attention + 
                        past_vote_2017 + eu_ref_vote 
                      + age + education + gender + region +
                        (1 | respondent_id), 
                      weights = weights,
                      data = parties_dicho)

# summary coomparison of original and new multilevel specs

# brier brexit
summary(original_brier_brex)
summary(mixed_brier_brex)

# brier party 
summary(original_brier_party)
summary(mixed_brier_party)

# dicho brexit
summary(original_dicho_brex)
summary(mixed_dicho_brex)

# dicho party
summary(original_dicho_party)
summary(mixed_dicho_party)

# comparing OLS model fit against Multilevel model fit
# Brier Score models
fit_test_brexit_brier <- anova(mixed_brier_brex, original_brier_brex)
fit_test_party_brier  <- anova(mixed_brier_party, original_brier_party)

# Dicho models
fit_test_brexit_dicho <- anova(mixed_dicho_brex, original_dicho_brex)
fit_test_party_dicho <- anova(mixed_dicho_party, original_dicho_party)

# Display results
print(fit_test_brexit_brier)
print(fit_test_party_brier)
print(fit_test_brexit_dicho)
print(fit_test_party_dicho)
# summary of model fit tests with plugged in values from print output above
test_summary_table <- tibble(
  `Model Context & Comparison` = c(
    "Brexit Experiment: Brier Score",
    "Parties Experiment: Brier Score",
    "Brexit Experiment: Dichotomised Score",
    "Parties Experiment: Dichotomised Score"
  ),
  `OLS AIC` = c(1646.5, 1480.8, 4802.2, 5049.8),
  `Multilevel AIC` = c(1597.3, 1419.3, 4781.8, 5039.2),
  `Chi-Square` = c(51.159, 63.498, 22.323, 12.638),
  `Significance (p-value)` = c("***", "***", "***", "***")
)

# Print consolidated table
tab3 <- knitr::kable(test_summary_table, digits = 3, 
             caption = "**Table 3: Test Fit Comparisons across Modeling Structures**")

save_kable(tab3, "Outputs/Results/tab_3.html")

tab3 
# summary table of new multi level models

tab_model(
  mixed_brier_brex, mixed_brier_party, mixed_dicho_brex, mixed_dicho_party,
  file = "Outputs/Results/tab_4.html",
  digits = 3,
  title = "Table 4. Multi-level Predictors of Respondent Characteristics Predicting Guess Accuracy (Brier Score and Correct Dichotomised Guess) Across Brexit and Party Experiments.",
  pred.labels = c(
    "(Intercept)" = "Intercept",
    "pol_attention" = "Political Attention",
    "past_vote_2017Labour" = "Party Vote: Labour",
    "past_vote_2017Scottish National Party (SNP)" = "Party Vote: SNP",
    "past_vote_2017Liberal Democrat" = "Party Vote: Liberal Democrat",
    "past_vote_2017Green" = "Party Vote: Green",
    "past_vote_2017Plaid Cymru" = "Party Vote: Plaid Cymru",
    "past_vote_2017UK Independence Party (UKIP)" = "Party Vote: UKIP",
    "past_vote_2017Other" = "Party Vote: Other",
    "past_vote_2017Don’t know" = "Party Vote: Don't Know",
    "eu_ref_voteLeave" = "EU Ref. Vote: Leave",
    "eu_ref_voteDid not vote" = "EU Ref. Vote: Did Not Vote",
    "age" = "Age",
    "educationLevel 1" = "Education Level: 1",
    "educationLevel 2" = "Education Level: 2",
    "educationLevel 3" = "Education Level: 3",
    "educationLevel 4" = "Education Level: 4",
    "educationLevel 5 and above" = "Education Level: 5 and Above",
    "educationOther" = "Education Level: Other",
    "genderFemale" = "Gender: Female",
    "regionNorth West" = "Region: North West",
    "regionYorkshire and the Humber" = "Region: Yorkshire and the Humber",
    "regionEast Midlands" = "Region: East Midlands",
    "regionWest Midlands" = "Region: West Midlands",
    "regionEast of England" = "Region: East of England",
    "regionLondon" = "Region: London",
    "regionSouth East" = "Region: South East",
    "regionSouth West" = "Region: South West",
    "regionWales" = "Region: Wales",
    "regionScotland" = "Region: Scotland"
    ),
  dv.labels = c(
    "<div style='text-align: center;'><b>Brier Score</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Brexit Exp.</div>",
    "<div style='text-align: center;'><b>Brier Score</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Party Exp.</div>", 
    "<div style='text-align: center;'><b>Correct Dichotomised Guess</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Brexit Exp.</div>", 
    "<div style='text-align: center;'><b>Correct Dichotomised Guess</b><hr style='border: none; border-top: 1px solid black; margin: 4px 0;'>Party Exp.</div>"
  ),
  string.pred = "Coefficient",
  show.ci = FALSE,
  show.se = TRUE,
  string.se = "Standard Error",
  p.style = "stars",
  show.icc = TRUE,
  CSS = list(
    css.colheaders = "text-align: center; vertical-align: bottom;",
    css.randomparts = "font-style: normal; font-weight: bold; color: #2c3e50;"
  )
)

# Detailed information of environment
sessionInfo()
LS0tDQp0aXRsZTogIkEgTXVsdGlsZXZlbCBBbmFseXNpcyBvZiBUaXRlbG1hbiAmIExhdWRlcmRhbGUncyAoMjAyMSkgVm90aW5nIERlbW9ncmFwaGljIEZvcmVjYXN0aW5nIg0KYXV0aG9yOiAiMjAwMzgxOTg1Ig0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdG9jOiB0cnVlDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZQ0KICAgICAgDQotLS0NCg0KYGBge3Igc3RhcnQsIGluY2x1ZGU9RkFMU0V9DQojIEtleSBMaWJyYXJpZXMgZm9yIHJlcGxpY2F0aW9uDQoNCiMgcm1hcmtkb3duDQpsaWJyYXJ5KHJtYXJrZG93bikNCmxpYnJhcnkoa25pdHIpDQoNCmBgYA0KIA0KIyMjIyBSZXBsaWNhdGlvbiBQcm9qZWN0DQoNCioqUnB1YnMgbGluazoqKiBodHRwczovL3JwdWJzLmNvbS9zMjAwMzgxOTg1LzE0NTEyMzggDQoNCioqR2l0SHViIFJlcG9zaXRvcnkgbGluazoqKiBodHRwczovL2dpdGh1Yi5jb20vc2Vmb3JiZXMxL1JlcGxpY2F0aW9uX01hdGVyaWFsc18yMDAzODE5ODUuZ2l0IA0KDQojIyAqKlJlcGxpY2F0aW9uIFByb2plY3QgSW5mb3JtYXRpb24qKiB7LX0NCg0KKlJlcGxpY2F0aW9uIHByb2plY3QgYmFzZWQgb246KiAgDQpUaXRlbG1hbiwgTi4gYW5kIExhdWRlcmRhbGUsIEIuRS4gKDIwMjFhKSBDYW4gY2l0aXplbnMgZ3Vlc3MgaG93IG90aGVyIGNpdGl6ZW5zIHZvdGUgYmFzZWQgb24gZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzPyAqUG9saXRpY2FsIFNjaWVuY2UgUmVzZWFyY2ggYW5kIE1ldGhvZHMqLCAxMSgyKSwgMjU0LTI3NC4gQXZhaWxhYmxlIGF0OiBodHRwczovL2RvaS5vcmcvMTAuMTAxNy9wc3JtLjIwMjEuNTMgDQoNCipSZXBsaWNhdGlvbiBtZXRob2QgdXNpbmcgcmVwbGljYXRpb24gbWF0ZXJpYWxzIG1hZGUgYXZhaWxhYmxlIGJ5IGF1dGhvcnM6KiAgDQpUaXRlbG1hbiwgTi4gYW5kIExhdWRlcmRhbGUsIEIuRS4gKDIwMjFiKSAqUmVwbGljYXRpb24gZGF0YSBmb3I6IENhbiBjaXRpemVucyBndWVzcyBob3cgb3RoZXIgY2l0aXplbnMgdm90ZSBiYXNlZCBvbiBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3M/KiBbZGF0YXNldF0uIEhhcnZhcmQgRGF0YXZlcnNlLiBBdmFpbGFibGUgYXQ6IGh0dHBzOi8vZG9pLm9yZy8xMC43OTEwL0RWTi8zT1dGNkMgDQoNCg0KIyMgKipXb3JrcGxhY2Ugc2V0dXAqKiB7LnRhYnNldCAudGFic2V0LXBpbGxzfQ0KDQojIyMgWUFNTCBzZXR0aW5ncw0KYGBgeWFtbA0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZGVwdGg6IDINCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQogICAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQpgYGANCg0KIyMjIEdsb2JhbCBjaHVuayBzZXR0aW5ncw0KYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBUUlVFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KA0KICBlY2hvID0gVFJVRSwNCiAgY2FjaGUgPSBUUlVFLA0KICBjb21tZW50ID0gTkEsDQogIHdhcm5pbmcgPSBGQUxTRSwNCiAgbWVzc2FnZSA9IEZBTFNFLA0KICBmaWcuYWxpZ24gPSAiY2VudGVyIg0KKQ0KYGBgDQoNCiMjIyBWZXJzaW9ucyBvZiB1c2VkIHBhY2thZ2VzIA0KVGhpcyBwcm9qZWN0IHVzZXMgdGhlIHJlbnYgcGFja2FnZSBmb3IgcmVwcm9kdWNpYmlsaXR5LiBSdW4gYHJlbnY6OnJlc3RvcmUoKWAuICAgDQpUaGlzIHdpbGwgaW5zdGFsbCBhbGwgcGFja2FnZXMgd2l0aCB0aGUgY29ycmVjdCB2ZXJzaW9ucyBhcyBzcGVjaWZpZWQgaW4gdGhlIGByZW52LmxvY2tgIGZpbGUuDQoNCmBgYHtyIHZlcnNpb25zLCByZXN1bHRzPSdoaWRlJ30NCnJlbnY6OnJlc3RvcmUoKQ0KYGBgDQoNCiMjIyBMaWJyYXJpZXMgDQpgYGB7ciBsaWJyYXJpZXMsIGluY2x1ZGU9VFJVRX0NCiMgbGlicmFyaWVzIHN1cHBsaWVkIGJ5IGF1dGhvcnMgcmVwbGljYXRpb24gbWF0ZXJpYWxzDQpsaWJyYXJ5KG1hdGxpYikNCmxpYnJhcnkoRGVzY1Rvb2xzKQ0KbGlicmFyeShzdXJ2ZXkpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ3RoZW1lcykNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShwbG90cml4KQ0KbGlicmFyeShwbHlyKQ0KbGlicmFyeShjb2VmcGxvdCkNCmxpYnJhcnkocXVlc3Rpb25yKQ0KbGlicmFyeShwc3ljaCkNCmxpYnJhcnkoc25ha2VjYXNlKSANCmxpYnJhcnkoc3RhcmdhemVyKQ0KbGlicmFyeSh0ZXhyZWcpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShmb3JjYXRzKQ0KbGlicmFyeShzdHJpbmdyKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShuYW5pYXIpDQpsaWJyYXJ5KHBhcGFqYSkNCg0KIyBsaWJyYXJpZXMgZm9yIHJlcGxpY2F0aW9uIGV4dGVuc2lvbg0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHNqUGxvdCkNCmxpYnJhcnkobG1lNCkNCmxpYnJhcnkobG1lclRlc3QpDQpsaWJyYXJ5KHBhdGNod29yaykNCg0KYGBgDQoNCg0KIyMjIEVudmlyb25tZW50IA0KYGBge3IgbXlSLCBlY2hvPUZBTFNFfQ0KdmVyc2lvbltbJ3ZlcnNpb24uc3RyaW5nJ11dDQpgYGANCkZvciBtb3JlIGRldGFpbHMgb24gcHJvamVjdCBlbnZpcm9ubWVudCBwbGVhc2Ugc2VlIEFwcGVuZGl4IDEuDQoNCg0KIyMgKipJbnRyb2R1Y3Rpb24qKiB7LX0NCg0KVGhlIGFiaWxpdHkgb2YgY2l0aXplbnMgdG8gaW5mZXIgcG9saXRpY2FsIHByZWZlcmVuY2VzIGFuZCBiZWhhdmlvdXJzIGZyb20gc29jaWFsIGluZm9ybWF0aW9uIGlzIGtleSB0byB1bmRlcnN0YW5kaW5nIGhvdyBpbmRpdmlkdWFscyBuYXZpZ2F0ZSBzb2NpYWwgYW5kIHBvbGl0aWNhbCBlbnZpcm9ubWVudHMuIFJlc2VhcmNoIHN1Z2dlc3RzIHRoYXQgY2l0aXplbnMgd2l0aCBncmVhdGVyIHBvbGl0aWNhbCBrbm93bGVkZ2UsIGVuZ2FnZW1lbnQsIGFuZCBjb2duaXRpdmUgcmVzb3VyY2VzIGFyZSBiZXR0ZXIgYWJsZSB0byBwcm9jZXNzIHBvbGl0aWNhbCBpbmZvcm1hdGlvbiBhbmQgbWFrZSBhY2N1cmF0ZSBqdWRnZW1lbnRzIGFib3V0IG90aGVycycgcHJlZmVyZW5jZXMgKExld2lzLUJlY2sgYW5kIFN0ZWdtYWllciwgMjAxMSkuIEhvd2V2ZXIsIHdoZXRoZXIgdGhlc2UgcmVzb3VyY2VzIHRyYW5zbGF0ZSBpbnRvIGFjY3VyYXRlIHByZWRpY3Rpb24gb2Ygb3RoZXJz4oCZIGJlaGF2aW91ciByZW1haW5zIGFuIG9wZW4gZW1waXJpY2FsIHF1ZXN0aW9uLiANCg0KVGl0ZWxtYW4gYW5kIExhdWRlcmRhbGUgKDIwMjFhKSBkZW1vbnN0cmF0ZSB0aGF0IGNpdGl6ZW5zIGNhbiwgdG8gYW4gZXh0ZW50LCBwcmVkaWN0IG90aGVycycgdm90aW5nIGJlaGF2aW91ciB1c2luZyBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiwgd2hpbGUgZnVydGhlciBsaW5raW5nIGluZGl2aWR1YWwgZGVtb2dyYXBoaWNzIGxpa2UgcG9saXRpY2FsIGtub3dsZWRnZSBhbmQgYXR0ZW50aW9uIHRvIGZhY2lsaXRhdGUgdGhpcyBmb3JlY2FzdGluZyBhYmlsaXR5LCB3aXRoIHBvbGl0aWNhbGx5IGVuZ2FnZWQgcmVzcG9uZGVudHMgZm91bmQgdG8gYWNoaWV2ZSBncmVhdGVyIGFjY3VyYWN5IGluIHRoZWlyIHN0dWR5LiBUaGV5IHVzZSBleHBlcmltZW50YWwgcHJvZmlsZXMgd2hlcmUgcmVzcG9uZGVudHMgcHJlZGljdCB0aGVpciB2b3RpbmcgYmVoYXZpb3VyIGJhc2VkIG9uIGdpdmVuIHByb2ZpbGUgZGVtb2dyYXBoaWNzLg0KDQpUaGlzIHJlcGxpY2F0aW9uIHByb2plY3Qgd2lsbCBhc3Nlc3MgcGFydCBvZiB0aGlzIHBhcGVyIHJlbGF0aW5nIHRvIFRhYmxlIDEgb2YgVGl0ZWxtYW4gYW5kIExhdWRlcmRhbGXigJlzIGFuYWx5c2lzICgyMDIxYSwgcC4gMjcwKSwgd2hlcmUgdGhleSBjbGFpbWVkIHRvIGhhdmUgZm91bmQgdGhhdCBhY2N1cmFjeSBvZiB2b3RpbmcgYmVoYXZpb3VyIGluY3JlYXNlZCB3aGVuIGNlcnRhaW4gcmVzcG9uZGVudC1sZXZlbCBkZW1vZ3JhcGhpYyBjaGFyYWN0ZXJpc3RpY3Mgd2VyZSBwcmVzZW50LCBzcGVjaWZpY2FsbHkgcG9saXRpY2FsIGF0dGVudGlvbi4gVGhvdWdoIHRoZXkgZG8gbm90IG92ZXJzdGF0ZSBhbnkgY2F1c2FsIGNsYWltLCB0aGVpciBjaG9pY2Ugb2YgcmVncmVzc2lvbiBhbmFseXNpcyBkb2VzIG5vdCBhY2NvdW50IGZvciBwb3NzaWJsZSB1bm9ic2VydmVkIHJlc3BvbmRlbnQtbGV2ZWwgaGV0ZXJvZ2VuZWl0eS4NCg0KRm9sbG93aW5nIHRoZSBkaW1lbnNpb25zIG9mIHJlcGxpY2F0aW9uIG91dGxpbmVkIGJ5IEZyZWVzZSBhbmQgUGV0ZXJzb24gKDIwMTcpLCB0aGlzIHN0dWR5IHByaW1hcmlseSBmb2N1c2VzIG9uIHJvYnVzdG5lc3MsIGFsb25nc2lkZSBhbiBhc3Nlc3NtZW50IG9mIHZlcmlmaWFiaWxpdHkuIFRoaXMgcHJvamVjdCBiZWdpbnMgYnkgYXNzZXNzaW5nIHdoZXRoZXIgdGhlIGF2YWlsYWJsZSByZXBsaWNhdGlvbiBwYWNrYWdlIGNhbiByZXByb2R1Y2UgdGhlIHBhcGVycyBvcmlnaW5hbCByZXN1bHRzLCBmaW5kaW5nIHBhcnRpYWwgdmVyaWZpYWJpbGl0eSBkdWUgdG8gaW5jb25zaXN0ZW5jaWVzIGluIHRoZSBkYXRhLWNsZWFuaW5nIGFuZCBhbmFseXNpcyBwaXBlbGluZS4gQWx0aG91Z2ggdGhlIGF1dGhvcnMgcHJvdmlkZSByZXBsaWNhdGlvbiBtYXRlcmlhbHMsIGtleSBzdGFnZXMgb2YgZGF0YSBwcm9jZXNzaW5nIGFuZCB0cmFuc2Zvcm1hdGlvbiBhcmUgbm90IGZ1bGx5IGRvY3VtZW50ZWQsIGxpbWl0aW5nIGNvbXBsZXRlIHJlcHJvZHVjdGlvbiBmcm9tIHJhdyBkYXRhLiBXaGlsZSB0aGlzIHN0dWR5IGV2YWx1YXRlcyB2ZXJpZmlhYmlsaXR5IHVzaW5nIHRoZSBwcm92aWRlZCByZXBsaWNhdGlvbiBtYXRlcmlhbHMsIGl0IGRvZXMgbm90IGNvbnN0aXR1dGUgYSBmdWxsIGVuZC10by1lbmQgcmVwcm9kdWN0aW9uIGZyb20gcmF3IGRhdGEgc291cmNlcywgbGltaXRpbmcgdGhlIGV4dGVudCB0byB3aGljaCB0aGUgY29tcGxldGUgZGF0YSBnZW5lcmF0aW9uIHByb2Nlc3MgY2FuIGJlIGluZGVwZW5kZW50bHkgdmVyaWZpZWQuIA0KDQpSZXBlYXRhYmlsaXR5IGFuZCBnZW5lcmFsaXNhYmlsaXR5IGFyZSBub3QgdGVzdGVkIHdpdGhpbiB0aGlzIHByb2plY3QsIHJlbHlpbmcgZXhjbHVzaXZlbHkgb24gdGhlIG9yaWdpbmFsIGNsZWFuZWQgZGF0YXNldHMgcHJvdmlkZWQgcmF0aGVyIHRoYW4gY29sbGVjdGluZyBuZXcgZGF0YSBvciByZS1ydW5uaW5nIHRoZWlyIGV4cGVyaW1lbnRhbCBkZXNpZ24sIGFuZCByZW1haW5zIGNvbmZpbmVkIHRvIHRoZSBvcmlnaW5hbCBleHBlcmltZW50YWwgY29udGV4dCBvZiB0aGUgMjAxNyBVSyBwb2xpdGljYWwgY2xpbWF0ZS4gVGhlIGV4dGVudCB0byB3aGljaCB0aGVzZSBmaW5kaW5ncyBhcHBseSB0byBvdGhlciBwb2xpdGljYWwgY29udGV4dHMsIHRpbWUgcGVyaW9kcyBvciBwb3B1bGF0aW9ucyByZW1haW4gb3BlbiB0byBxdWVzdGlvbiwgYW5kIHRoaXMgcHJvamVjdCBkb2VzIG5vdCBldmFsdWF0ZSB3aGV0aGVyIHRoZXNlIGZpbmRpbmdzIHdvdWxkIHJlcHJvZHVjZSB1bmRlciByZXBlYXRlZCBkYXRhIGNvbGxlY3Rpb24uIA0KDQpUaGUgcHJpbWFyeSBhaW0gb2YgdGhpcyBwcm9qZWN0IGlzIHRvIGFzc2VzcyByb2J1c3RuZXNzIGJ5IGFuYWx5c2luZyB0aGUgc2FtZSBkYXRhIHVuZGVyIGFsdGVybmF0aXZlIG1vZGVsbGluZyBhc3N1bXB0aW9ucy4gQnkgY2hhbmdpbmcgdGhlIG1vZGVsbGluZyBzdHJhdGVneSwgdGhpcyByZXBsaWNhdGlvbiB0ZXN0cyB3aGV0aGVyIHRoZSBhdXRob3JzJyBmaW5kaW5ncyBhcmUgc2Vuc2l0aXZlIHRvIGRpZmZlcmVudCBtb2RlbGxpbmcgYXNzdW1wdGlvbnMuIFRoZXJlIGlzIGFuIGFyZ3VtZW50IGZvciB0aGVpciBjaG9zZW4gbW9kZWxsaW5nIHN0cmF0ZWd5IGlnbm9yaW5nIHJlcGVhdGVkIG9ic2VydmF0aW9ucyBwZXIgcmVzcG9uZGVudC4gV2hpbGUgVGl0ZWxtYW4gYW5kIExhdWRlcmRhbGUgKDIwMjFhLCBwLiAyNTkpIGNvbnNpZGVyIHRoZSBpbXBsaWNhdGlvbnMgb2YgdGhlaXIgZXhwZXJpbWVudGFsIGRlc2lnbiBmb3IgY2F1c2FsIGludGVycHJldGF0aW9uLCB0aGV5IGRvIG5vdCBhZGRyZXNzIGEgc2VwYXJhdGUgYnV0IGltcG9ydGFudCBpc3N1ZSBhcmlzaW5nIGZyb20gdGhlIHN0cnVjdHVyZSBvZiB0aGVpciBkYXRhOiBlYWNoIHJlc3BvbmRlbnQgZXZhbHVhdGVzIG11bHRpcGxlIHByb2ZpbGVzLiBUaGlzIGdlbmVyYXRlcyByZXBlYXRlZCBvYnNlcnZhdGlvbnMgbmVzdGVkIHdpdGhpbiBpbmRpdmlkdWFsIHJlc3BvbmRlbnRzLCB2aW9sYXRpbmcgdGhlIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbnQgb2JzZXJ2YXRpb25zIHVuZGVybHlpbmcgdGhlaXIgY2hvc2VuIE9MUyBtb2RlbHMgYW5kIHBvdGVudGlhbGx5IGxlYWRpbmcgdG8gdW5kZXJlc3RpbWF0ZWQgc3RhbmRhcmQgZXJyb3JzIChGaW5jaCwgMjAyNjsgTWFhcyAmIEhveCwgMjAwNSkuIElmIHRoZSBvcmlnaW5hbCByZXN1bHRzIGFyZSBwYXJ0bHkgZHJpdmVuIGJ5IGNsdXN0ZXJlZCBvYnNlcnZhdGlvbnMsIGl0IGlzIGV4cGVjdGVkIHRoYXQgbXVsdGlsZXZlbCBtb2RlbHMgaW5jcmVhc2Ugc3RhbmRhcmQgZXJyb3JzIGFuZCByZWR1Y2UgdGhlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBvZiBzb21lIGNvZWZmaWNpZW50cyAoQmlja2VsLCAyMDA3OyBSYXVkZW5idXNoICYgQnJ5aywgMjAwMjsgU25pamRlcnMgJiBCb3NrZXIsIDIwMTIpLiBCYXNlZCBvbiBwcmlvciByZXNlYXJjaCBvbiBjbHVzdGVyZWQgZGF0YSBzdHJ1Y3R1cmVzIGFuZCBtdWx0aWxldmVsIG1vZGVsbGluZyAoR2VsbWFuICYgSGlsbCwgMjAwNzsgTWFhcyAmIEhveCwgMjAwNSksIGl0IGlzIGV4cGVjdGVkIHRoYXQgYWNjb3VudGluZyBmb3IgcmVzcG9uZGVudC1sZXZlbCBjbHVzdGVyaW5nIHdpbGwgaW5jcmVhc2Ugc3RhbmRhcmQgZXJyb3JzIGFuZCByZWR1Y2UgdGhlIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBvZiBzb21lIHByZWRpY3RvcnMgb2JzZXJ2ZWQgaW4gdGhlIG9yaWdpbmFsIE9MUyBtb2RlbHMuIFN1YnN0YW50aXZlbHksIHdoaWxlIHBvbGl0aWNhbCBhdHRlbnRpb24gYW5kIGVkdWNhdGlvbiBhcmUgZXhwZWN0ZWQgdG8gcmVtYWluIHBvc2l0aXZlbHkgYXNzb2NpYXRlZCB3aXRoIGZvcmVjYXN0aW5nIGFjY3VyYWN5LCB0aGVpciBlc3RpbWF0ZWQgZWZmZWN0cyBhcmUgbGlrZWx5IHRvIGJlIHNtYWxsZXIgYW5kIGxlc3MgcHJlY2lzZSBvbmNlIHVub2JzZXJ2ZWQgaW5kaXZpZHVhbC1sZXZlbCBoZXRlcm9nZW5laXR5IGlzIGFjY291bnRlZCBmb3IuIFRoZXJlZm9yZSwgdGhpcyBzdHVkeSBleHRlbmRzIHRoZSBvcmlnaW5hbCBhbmFseXNpcyBieSBleGFtaW5pbmcgd2hldGhlciBpdHMgY29uY2x1c2lvbnMgcmVtYWluIHJvYnVzdCB3aGVuIGFjY291bnRpbmcgZm9yIHRoZSBjbHVzdGVyZWQgc3RydWN0dXJlIG9mIHRoZSBleHBlcmltZW50YWwgZGVzaWduIHRocm91Z2ggYSBtdWx0aWxldmVsIG1vZGVsbGluZyBhcHByb2FjaC4NCiANCg0KDQojIyAqKkRhdGEgYW5kIE1ldGhvZHMqKiB7LX0NCg0KIyMjIERhdGENCg0KVGhpcyBhbmFseXNpcyB1c2VzIGV4cGVyaW1lbnRhbCByZXBsaWNhdGlvbiBkYXRhc2V0cyBwcm92aWRlZCBieSBUaXRlbG1hbiBhbmQgTGF1ZGVyZGFsZSAoMjAyMWIpLCBjb21iaW5pbmcgZGF0YSBmcm9tIHRoZSAyMDE3IEJyaXRpc2ggRWxlY3Rpb24gU3R1ZHkgKEJFUykgZmFjZS10by1mYWNlIHN1cnZleSAoRmllbGRob3VzZSBldCBhbC4sIDIwMTkpLCBhbmQgYW4gb25saW5lIFlvdUdvdiBleHBlcmltZW50LiBUaGUgcmVwbGljYXRpb24gcGFja2FnZSBjb250YWlucyBsYXllcmVkIGRhdGFmaWxlcyBgQ2xlYW5CcmV4aXQuUkRhdGFgIGFuZCBgQ2xlYW5QYXJ0aWVzLlJEYXRhYC4gDQoNClJldHJvc3BlY3RpdmVseSB3aXRoaW4gdGhlc2UsIGNvbnRhaW4gaW1wdXRlZCBgQnJleGl0LkJFUy5DbGVhbmAgYW5kIGBQYXJ0aWVzLkJFUy5DbGVhbmAgZGF0YSBvZiBCRVMtc291cmNlZCBkZW1vZ3JhcGhpYyBwcm9maWxlcywgYW5kIHRoZSBmaW5hbCBhbmFseXNpcyBkYXRhc2V0cyBgQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW5gIGFuZCBgUGFydGllcy5FeHBlcmltZW50LkNsZWFuYCBjb250YWluaW5nIHJlc3BvbmRlbnRz4oCZIGRlbW9ncmFwaGljIGNoYXJhY3RlcmlzdGljcyBhbmQgdGhlaXIgdm90aW5nIHByZWRpY3Rpb25zIGNvbWJpbmVkIHdpdGggdGhlIGltcHV0ZWQgYEJFUy5DbGVhbmAgZGF0YS4gVGhlc2UgYC5FeHBlcmltZW50LkNsZWFuYCBkYXRhc2V0cyBjb250YWluIHJlcGVhdGVkIG9ic2VydmF0aW9ucyBhcyByZXNwb25kZW50cyBldmFsdWF0ZWQgbXVsdGlwbGUgZGVtb2dyYXBoaWMgcHJvZmlsZXMuDQoNClRoZSBzdXBwbGllZCBgLlJEYXRhYCBmaWxlcyB3ZXJlIGxvYWRlZCBhbmQgY2h1bmtzIG9mIHRoZSBhdXRob3Jz4oCZIG9yaWdpbmFsIGFuYWx5c2lzIHNjcmlwdCBgYXBwZW5kaXgucm1kYCB3ZXJlIGV4ZWN1dGVkIHRvIHJlY3JlYXRlIHRoZSByZXF1aXJlZCBvdXRjb21lIHZhcmlhYmxlcyAoc2VlIEFwcGVuZGl4IDIpLCB3aGljaCB3ZXJlIG5vdCBpbmNsdWRlZCBpbiB0aGUgYXV0aG9ycyBvcmlnaW5hbCBjbGVhbmVkIGRhdGFzZXRzLiBDb2Rpbmcgd2FzIGNodW5rZWQgYW5kIHJlZmVyZW5jZWQgdG8gc2lnbmFsIGF1dGhvcnNoaXAgb2YgYGFwcGVuZGl4LnJtZGAgY29kZSAoVGl0ZWxtYW4gJiBMYXVkZXJkYWxlLCAyMDIxYykuIA0KDQpgYGB7ciBsb2FkX2RhdGEsIHJlc3VsdHM9J2hpZGUnfQ0KIyBsb2FkIEJyZXhpdCBEYXRhZmlsZQ0KbG9hZCgiRGF0YS9JbnB1dERhdGEvQ2xlYW5CcmV4aXQuUkRhdGEiKQ0KDQojIGxvYWQgUGFydHkgRGF0YWZpbGUNCmxvYWQoIkRhdGEvSW5wdXREYXRhL0NsZWFuUGFydGllcy5SRGF0YSIpDQpgYGANCg0KYGBge3IgZGF0YV9jbGVhbmluZywgaW5jbHVkZT1GQUxTRX0NCiMgUnVuIFRpdGVsbWFuICYgTGF1ZGVyZGFsZSdzIGBhcHBlbmRpeC5ybWRgIGNvZGUgdG8gY3JlYXRlIG91dGNvbWUgdmFyaWFibGVzIG5lY2Nlc2FyeSBmb3IgcnVubmluZyBtb2RlbHMNCg0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyBbVGl0ZWxtYW4gJiBMYXVkZXJkYWxlICgyMDIxYykgY29kZV0gIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgU3RhcnQ6ICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIA0KDQojIyMjIyMjIyMjIyMjIyMjIyMgU1RBUlQgQ0hVTksgMyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIFRoZSBmb2xsb3dpbmcgY2h1bmsgYWxsb3dzIGZvciBjb21wYXJpc29ucyBvZiBtZWFuIGd1ZXNzIGJ5IHByb2ZpbGUgYXR0cmlidXRlIHdpdGggdGhlIHNhbWUgbWVhbiBpbiB0aGUgQkVTDQoNCiMgUHJlZGljdGluZyB0aGUgKHdlaWdodGVkKSBtZWFuIGd1ZXNzIGJ5IGxldmVsIG9mIGVhY2ggYXR0cmlidXRlDQoNCk1lYW5zQWdlPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfQWdlR3JvdXAsIGRhdGE9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4sICB3PUJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cykNCmxldmVscyhCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRwcm9maWxlX0FnZUdyb3VwKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfQWdlR3JvdXA9YygiMTgtMjkiLCAiMzAtNDQiLCAiNDUtNTkiLCAiNjAtNzQiLCAiNzUgKyIpKQ0KTWVhbnNBZ2UxPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNBZ2UsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc0FnZUJFUzwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX0FnZUdyb3VwLCBkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sICB3PUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNBZ2VCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNBZ2VCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zQWdlQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNBZ2UxJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zQWdlLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0FnZTEsIE1lYW5zQWdlQkVTMikNCg0KTWVhbnNSZWxpZ2lvbjwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX1JlbGlnaW9uLCBkYXRhPUJyZXhpdC5FeHBlcmltZW50LkNsZWFuLCAgdz1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9SZWxpZ2lvbj1jKCJSb21hbiBDYXRob2xpYyIsIkNodXJjaCBvZiBFbmdsYW5kL0FuZ2xpY2FuL0VwaXNjb3BhbCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJIaW5kdSIsIklzbGFtIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1ldGhvZGlzdCIsIlByZXNieXRlcmlhbi9DaHVyY2ggb2YgU2NvdGxhbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm8gcmVsaWdpb24iLCJVbmtub3duIiApKQ0KTWVhbnNSZWxpZ2lvbjE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc1JlbGlnaW9uLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KTWVhbnNSZWxpZ2lvbjEkcHJvZmlsZV9SZWxpZ2lvbjwtZmFjdG9yKE1lYW5zUmVsaWdpb24xJHByb2ZpbGVfUmVsaWdpb24sIGxldmVscz1NZWFuc1JlbGlnaW9uMSRwcm9maWxlX1JlbGlnaW9uW29yZGVyKE1lYW5zUmVsaWdpb24xJGZpdCldKQ0KTWVhbnNSZWxpZ2lvbkJFUzwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX1JlbGlnaW9uLCBkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sICB3PUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNSZWxpZ2lvbkJFUzI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc1JlbGlnaW9uQkVTLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KDQpNZWFuc1JlbGlnaW9uQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNSZWxpZ2lvbjEkUHJvYmFiaWxpdGllczwtIkd1ZXNzIg0KTWVhbnNSZWxpZ2lvbi5CaW5kPC1iaW5kX3Jvd3MoTWVhbnNSZWxpZ2lvbjEsIE1lYW5zUmVsaWdpb25CRVMyKQ0KDQoNCk1lYW5zRXRobmljaXR5PC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfRXRobmljaXR5LCBkYXRhPUJyZXhpdC5FeHBlcmltZW50LkNsZWFuLCAgdz1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9FdGhuaWNpdHk9YygiQXNpYW4iLCJCbGFjayIsICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1peGVkIiwiV2hpdGUiLCAgICAgICAgICAgICAgICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJVbmtub3duIikpDQpNZWFuc0V0aG5pY2l0eTE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0V0aG5pY2l0eSwgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zRXRobmljaXR5MSRwcm9maWxlX0V0aG5pY2l0eTwtZmFjdG9yKE1lYW5zRXRobmljaXR5MSRwcm9maWxlX0V0aG5pY2l0eSwgbGV2ZWxzPU1lYW5zRXRobmljaXR5MSRwcm9maWxlX0V0aG5pY2l0eVtvcmRlcihNZWFuc0V0aG5pY2l0eTEkZml0KV0pDQpNZWFuc0V0aG5pY2l0eUJFUzwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX0V0aG5pY2l0eSwgZGF0YT1CcmV4aXQuQkVTLkNsZWFuLCAgdz1CcmV4aXQuQkVTLkNsZWFuJHByb2ZpbGVfd2VpZ2h0cykNCk1lYW5zRXRobmljaXR5QkVTMjwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zRXRobmljaXR5QkVTLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KDQoNCk1lYW5zRXRobmljaXR5QkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNFdGhuaWNpdHkxJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zRXRobmljaXR5LkJpbmQ8LWJpbmRfcm93cyhNZWFuc0V0aG5pY2l0eTEsIE1lYW5zRXRobmljaXR5QkVTMikNCg0KDQpNZWFuc0dlbmRlcjwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX0dlbmRlciwgZGF0YT1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiwgIHc9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfR2VuZGVyPWMoIk1hbGUiLCAiRmVtYWxlIikpDQpNZWFuc0dlbmRlcjE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0dlbmRlciwgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zR2VuZGVyQkVTPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfR2VuZGVyLCBkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sICB3PUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNHZW5kZXJCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNHZW5kZXJCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zR2VuZGVyQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNHZW5kZXIxJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zR2VuZGVyLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0dlbmRlcjEsIE1lYW5zR2VuZGVyQkVTMikNCg0KTWVhbnNFZHVjYXRpb24yPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfRWR1Y2F0aW9uLCBkYXRhPUJyZXhpdC5FeHBlcmltZW50LkNsZWFuLCAgdz1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9FZHVjYXRpb249YygnSG9sZHMgYSB1bml2ZXJzaXR5IGRlZ3JlZScsICdEb2VzIG5vdCBob2xkIGEgdW5pdmVyc2l0eSBkZWdyZWUnKSkNCk1lYW5zRWR1Y2F0aW9uMzwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zRWR1Y2F0aW9uMiwgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zRWR1Y2F0aW9uQkVTPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfRWR1Y2F0aW9uLCBkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sICB3PUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNFZHVjYXRpb25CRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNFZHVjYXRpb25CRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zRWR1Y2F0aW9uQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNFZHVjYXRpb24zJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zRWR1Y2F0aW9uLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0VkdWNhdGlvbjMsIE1lYW5zRWR1Y2F0aW9uQkVTMikNCg0KTWVhbnNSZWdpb248LWxtKHByb2ZpbGVfTGVhdmV+cHJvZmlsZV9SZWdpb24sIGRhdGE9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4sICB3PUJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cykNCm5ldy5kYXQgPC0gZGF0YS5mcmFtZShwcm9maWxlX1JlZ2lvbj1mYWN0b3IoYygiTG9uZG9uIiwgIk5vcnRoIEVhc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOb3J0aCBXZXN0IiwiU2NvdGxhbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTb3V0aCBFYXN0IiwiU291dGggV2VzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRoZSBFYXN0IE1pZGxhbmRzIiwidGhlIEVhc3Qgb2YgRW5nbGFuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldhbGVzIiwiV2VzdCBNaWRsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIllvcmtzaGlyZSAmIEh1bWJlciIpKSkNCk1lYW5zUmVnaW9uMTwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zUmVnaW9uLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KTWVhbnNSZWdpb24xJHByb2ZpbGVfUmVnaW9uPC1mYWN0b3IoTWVhbnNSZWdpb24xJHByb2ZpbGVfUmVnaW9uLCBsZXZlbHM9TWVhbnNSZWdpb24xJHByb2ZpbGVfUmVnaW9uW29yZGVyKE1lYW5zUmVnaW9uMSRmaXQpXSkNCk1lYW5zUmVnaW9uQkVTPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfUmVnaW9uLCBkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sICB3PUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNSZWdpb25CRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNSZWdpb25CRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc1JlZ2lvbkJFUzIkUHJvYmFiaWxpdGllczwtIkJFUyINCk1lYW5zUmVnaW9uMSRQcm9iYWJpbGl0aWVzPC0iR3Vlc3MiDQpNZWFuc1JlZ2lvbi5CaW5kPC1iaW5kX3Jvd3MoTWVhbnNSZWdpb24xLCBNZWFuc1JlZ2lvbkJFUzIpDQoNCg0KTWVhbnNSZWdpb24uQmluZDwtYmluZF9yb3dzKE1lYW5zUmVnaW9uMSwgTWVhbnNSZWdpb25CRVMyKQ0KUmVhbHZvdGU8LUJyZXhpdC5FeHBlcmltZW50LkNsZWFuICU+JQ0KICBkcGx5cjo6ICBncm91cF9ieShwcm9maWxlX1JlZ2lvbikgJT4lDQogIGRwbHlyOjogICBzdW1tYXJpc2UoZml0ID0gbWVhbihwcm9maWxlX3JlZ2lvbmFsX3JlYWxfTGVhdmUsIG5hLnJtPVQpKQ0KUmVhbHZvdGUkUHJvYmFiaWxpdGllczwtIlJlYWwiDQoNCk1lYW5zUmVnaW9uLkJpbmQyPC1iaW5kX3Jvd3MoTWVhbnNSZWdpb24uQmluZCwgUmVhbHZvdGUpDQpNZWFuc1JlZ2lvbi5CaW5kMiRQcm9iYWJpbGl0aWVzPC1mYWN0b3IoTWVhbnNSZWdpb24uQmluZDIkUHJvYmFiaWxpdGllcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiQkVTIltvcmRlcigxKV0sIlJlYWwiW29yZGVyKDIpXSwiR3Vlc3MiW29yZGVyKDMpXSkpDQoNCg0KTWVhbnNJbmNvbWU8LWxtKHByb2ZpbGVfTGVhdmV+cHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWUsIGRhdGE9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4sIHc9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lPWZhY3RvcihjKCJMZXNzIHRoYW4gNSwxOTkiW29yZGVyKDEpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA1LDIwMCBhbmQgMTUsNTk5IltvcmRlcigyKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldHdlZW4gMTUsNjAwIGFuZCAyNSw5OTkiW29yZGVyKDMpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiAyNiwwMDAgYW5kIDM2LDM5OSJbb3JkZXIoNCldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDM2LDQwMCBhbmQgNDQsOTk5IltvcmRlcig1KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldHdlZW4gNDUsMDAwIGFuZCA1OSw5OTkiW29yZGVyKDYpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA2MCwwMDAgYW5kIDk5LDk5OSJbb3JkZXIoNyldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmVhdGVyIHRoYW4gMTAwLDAwMCJbb3JkZXIoOCldICkpKQ0KDQpNZWFuc0luY29tZTI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0luY29tZSwgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zSW5jb21lQkVTPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lLCBkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sICB3PUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNJbmNvbWVCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNJbmNvbWVCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zSW5jb21lQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNJbmNvbWUyJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zSW5jb21lLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0luY29tZTIsIE1lYW5zSW5jb21lQkVTMikNCg0KTWVhbnNJbmNvbWUuQmluZCRwcm9maWxlX0FubnVhbEhvdXNlaG9sZEluY29tZTwtZmFjdG9yKE1lYW5zSW5jb21lLkJpbmQkcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiTGVzcyB0aGFuIDUsMTk5IltvcmRlcigxKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA1LDIwMCBhbmQgMTUsNTk5IltvcmRlcigyKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiAxNSw2MDAgYW5kIDI1LDk5OSJbb3JkZXIoMyldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldHdlZW4gMjYsMDAwIGFuZCAzNiwzOTkiW29yZGVyKDQpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDM2LDQwMCBhbmQgNDQsOTk5IltvcmRlcig1KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA0NSwwMDAgYW5kIDU5LDk5OSJbb3JkZXIoNildLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldHdlZW4gNjAsMDAwIGFuZCA5OSw5OTkiW29yZGVyKDcpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmVhdGVyIHRoYW4gMTAwLDAwMCJbb3JkZXIoOCldICkpDQoNCk1lYW5zQ2xhc3M8LWxtKHByb2ZpbGVfTGVhdmV+cHJvZmlsZV9TdWJDbGFzcywgZGF0YT1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiwgIHc9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfU3ViQ2xhc3M9YygiV29ya2luZyBjbGFzcyIsICJNaWRkbGUgY2xhc3MiKSkNCk1lYW5zQ2xhc3MxPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNDbGFzcywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zQ2xhc3NCRVM8LWxtKHByb2ZpbGVfTGVhdmV+cHJvZmlsZV9TdWJDbGFzcywgZGF0YT1CcmV4aXQuQkVTLkNsZWFuLCAgdz1CcmV4aXQuQkVTLkNsZWFuJHByb2ZpbGVfd2VpZ2h0cykNCk1lYW5zQ2xhc3NCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNDbGFzc0JFUywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCg0KTWVhbnNDbGFzc0JFUzIkUHJvYmFiaWxpdGllczwtIkJFUyINCk1lYW5zQ2xhc3MxJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zQ2xhc3MuQmluZDwtYmluZF9yb3dzKE1lYW5zQ2xhc3MxLCBNZWFuc0NsYXNzQkVTMikNCg0KTWVhbnNGYW1DbGFzczwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX1N1YkZhbUNsYXNzLCBkYXRhPUJyZXhpdC5FeHBlcmltZW50LkNsZWFuLCAgdz1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9TdWJGYW1DbGFzcz1jKCJGYW1pbHkgd29ya2luZyBjbGFzcyIsICJGYW1pbHkgbWlkZGxlIGNsYXNzIikpDQpNZWFuc0ZhbUNsYXNzMTwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zRmFtQ2xhc3MsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc0ZhbUNsYXNzQkVTPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfU3ViRmFtQ2xhc3MsIGRhdGE9QnJleGl0LkJFUy5DbGVhbiwgIHc9QnJleGl0LkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc0ZhbUNsYXNzQkVTMjwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zRmFtQ2xhc3NCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zRmFtQ2xhc3NCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0ZhbUNsYXNzMSRQcm9iYWJpbGl0aWVzPC0iR3Vlc3MiDQpNZWFuc0ZhbUNsYXNzLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0ZhbUNsYXNzMSwgTWVhbnNGYW1DbGFzc0JFUzIpDQoNCg0KTWVhbnNIb21lU3RhdHVzPC1sbShwcm9maWxlX0xlYXZlfnByb2ZpbGVfSG9tZVN0YXR1cywgZGF0YT1CcmV4aXQuRXhwZXJpbWVudC5DbGVhbiwgIHc9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfSG9tZVN0YXR1cz1jKCJSZW50cyBob21lIiwgIk93bnMgaG9tZSIpKQ0KTWVhbnNIb21lU3RhdHVzMTwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zSG9tZVN0YXR1cywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zSG9tZVN0YXR1c0JFUzwtbG0ocHJvZmlsZV9MZWF2ZX5wcm9maWxlX0hvbWVTdGF0dXMsIGRhdGE9QnJleGl0LkJFUy5DbGVhbiwgIHc9QnJleGl0LkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc0hvbWVTdGF0dXNCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNIb21lU3RhdHVzQkVTLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KDQpNZWFuc0hvbWVTdGF0dXNCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0hvbWVTdGF0dXMxJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zSG9tZVN0YXR1cy5CaW5kPC1iaW5kX3Jvd3MoTWVhbnNIb21lU3RhdHVzMSwgTWVhbnNIb21lU3RhdHVzQkVTMikNCg0KDQojUGFydGllcw0KDQpNZWFuc0FnZTwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9BZ2VHcm91cCwgZGF0YT1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4sICB3PVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpsZXZlbHMoUGFydGllcy5FeHBlcmltZW50LkNsZWFuJHByb2ZpbGVfQWdlR3JvdXApDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9BZ2VHcm91cD1jKCIxOC0yOSIsICIzMC00NCIsICI0NS01OSIsICI2MC03NCIsICI3NSArIikpDQpNZWFuc0FnZTE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0FnZSwgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zQWdlQkVTPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX0FnZUdyb3VwLCBkYXRhPVBhcnRpZXMuQkVTLkNsZWFuLCAgdz1QYXJ0aWVzLkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc0FnZUJFUzI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0FnZUJFUywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCg0KTWVhbnNBZ2VCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0FnZTEkUHJvYmFiaWxpdGllczwtIkd1ZXNzIg0KTWVhbnNBZ2VQLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0FnZTEsIE1lYW5zQWdlQkVTMikNCg0KTWVhbnNFZHVjYXRpb25QPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX0VkdWNhdGlvbiwgZGF0YT1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4sICB3PVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9FZHVjYXRpb249YygnSG9sZHMgYSB1bml2ZXJzaXR5IGRlZ3JlZScsICdEb2VzIG5vdCBob2xkIGEgdW5pdmVyc2l0eSBkZWdyZWUnKSkNCk1lYW5zRWR1Y2F0aW9uUDI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0VkdWNhdGlvblAsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc0VkdWNhdGlvblBCRVM8LWxtKHByb2ZpbGVfQ29ufnByb2ZpbGVfRWR1Y2F0aW9uLCBkYXRhPVBhcnRpZXMuQkVTLkNsZWFuLCAgdz1QYXJ0aWVzLkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc0VkdWNhdGlvblBCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNFZHVjYXRpb25QQkVTLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KDQpNZWFuc0VkdWNhdGlvblBCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0VkdWNhdGlvblAyJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zRWR1Y2F0aW9uUC5CaW5kPC1iaW5kX3Jvd3MoTWVhbnNFZHVjYXRpb25QMiwgTWVhbnNFZHVjYXRpb25QQkVTMikNCg0KDQpNZWFuc0luY29tZVA8LWxtKHByb2ZpbGVfQ29ufnByb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lLCBkYXRhPVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiwgIHc9UGFydGllcy5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cykNCm5ldy5kYXQgPC0gZGF0YS5mcmFtZSgocHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWU9ZmFjdG9yKGMoIkxlc3MgdGhhbiA1LDE5OSJbb3JkZXIoMSldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA1LDIwMCBhbmQgMTUsNTk5IltvcmRlcigyKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDE1LDYwMCBhbmQgMjUsOTk5IltvcmRlcigzKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDI2LDAwMCBhbmQgMzYsMzk5IltvcmRlcig0KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDM2LDQwMCBhbmQgNDQsOTk5IltvcmRlcig1KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDQ1LDAwMCBhbmQgNTksOTk5IltvcmRlcig2KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDYwLDAwMCBhbmQgOTksOTk5IltvcmRlcig3KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmVhdGVyIHRoYW4gMTAwLDAwMCJbb3JkZXIoOCldKSkpKQ0KDQpNZWFuc0luY29tZVAyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNJbmNvbWVQLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KTWVhbnNJbmNvbWVQQkVTPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX0FubnVhbEhvdXNlaG9sZEluY29tZSwgZGF0YT1QYXJ0aWVzLkJFUy5DbGVhbiwgIHc9UGFydGllcy5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNJbmNvbWVQQkVTMjwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zSW5jb21lUEJFUywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCg0KDQpNZWFuc0luY29tZVBCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0luY29tZVAyJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zSW5jb21lUC5CaW5kPC1iaW5kX3Jvd3MoTWVhbnNJbmNvbWVQMiwgTWVhbnNJbmNvbWVQQkVTMikNCg0KTWVhbnNJbmNvbWVQLkJpbmQkcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWU8LWZhY3RvcihNZWFuc0luY29tZS5CaW5kJHByb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJMZXNzIHRoYW4gNSwxOTkiW29yZGVyKDEpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA1LDIwMCBhbmQgMTUsNTk5IltvcmRlcigyKV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldHdlZW4gMTUsNjAwIGFuZCAyNSw5OTkiW29yZGVyKDMpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiAyNiwwMDAgYW5kIDM2LDM5OSJbb3JkZXIoNCldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZXR3ZWVuIDM2LDQwMCBhbmQgNDQsOTk5IltvcmRlcig1KV0sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJldHdlZW4gNDUsMDAwIGFuZCA1OSw5OTkiW29yZGVyKDYpXSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmV0d2VlbiA2MCwwMDAgYW5kIDk5LDk5OSJbb3JkZXIoNyldLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcmVhdGVyIHRoYW4gMTAwLDAwMCJbb3JkZXIoOCldICkpDQoNCg0KDQpNZWFuc1JlZ2lvblA8LWxtKHByb2ZpbGVfQ29ufnByb2ZpbGVfUmVnaW9uLCBkYXRhPVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiwgIHc9UGFydGllcy5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cykNCm5ldy5kYXQgPC0gZGF0YS5mcmFtZShwcm9maWxlX1JlZ2lvbj1mYWN0b3IoYygiTG9uZG9uIiwgIk5vcnRoIEVhc3QiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOb3J0aCBXZXN0IiwiU2NvdGxhbmQiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTb3V0aCBFYXN0IiwiU291dGggV2VzdCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRoZSBFYXN0IE1pZGxhbmRzIiwidGhlIEVhc3Qgb2YgRW5nbGFuZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIldhbGVzIiwiV2VzdCBNaWRsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIllvcmtzaGlyZSAmIEh1bWJlciIpKSkNCk1lYW5zUmVnaW9uUDE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc1JlZ2lvblAsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc1JlZ2lvblAxJHByb2ZpbGVfUmVnaW9uPC1mYWN0b3IoTWVhbnNSZWdpb25QMSRwcm9maWxlX1JlZ2lvbiwgbGV2ZWxzPU1lYW5zUmVnaW9uUDEkcHJvZmlsZV9SZWdpb25bb3JkZXIoTWVhbnNSZWdpb25QMSRmaXQpXSkNCk1lYW5zUmVnaW9uUEJFUzwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9SZWdpb24sIGRhdGE9UGFydGllcy5CRVMuQ2xlYW4sICB3PVBhcnRpZXMuQkVTLkNsZWFuJHByb2ZpbGVfd2VpZ2h0cykNCk1lYW5zUmVnaW9uUEJFUzI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc1JlZ2lvblBCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCg0KTWVhbnNSZWdpb25QQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNSZWdpb25QMSRQcm9iYWJpbGl0aWVzPC0iR3Vlc3MiDQpNZWFuc1JlZ2lvblAuQmluZDwtYmluZF9yb3dzKE1lYW5zUmVnaW9uUDEsIE1lYW5zUmVnaW9uUEJFUzIpDQpSZWFsdm90ZTwtUGFydGllcy5FeHBlcmltZW50LkNsZWFuICU+JQ0KICBkcGx5cjo6ICBncm91cF9ieShwcm9maWxlX1JlZ2lvbikgJT4lDQogIGRwbHlyOjogICBzdW1tYXJpc2UoZml0ID0gbWVhbihwcm9maWxlX3JlZ2lvbmFsX3JlYWxfQ29uLCBuYS5ybT1UKSkNClJlYWx2b3RlJFByb2JhYmlsaXRpZXM8LSJSZWFsIg0KDQpNZWFuc1JlZ2lvblAyLkJpbmQ8LWJpbmRfcm93cyhNZWFuc1JlZ2lvblAuQmluZCwgUmVhbHZvdGUpDQpNZWFuc1JlZ2lvblAyLkJpbmQkUHJvYmFiaWxpdGllczwtZmFjdG9yKE1lYW5zUmVnaW9uLkJpbmQyJFByb2JhYmlsaXRpZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCJCRVMiW29yZGVyKDEpXSwiUmVhbCJbb3JkZXIoMildLCJHdWVzcyJbb3JkZXIoMyldKSkNCg0KDQpNZWFuc0NsYXNzUDwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9TdWJDbGFzcywgZGF0YT1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4sICB3PVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9TdWJDbGFzcz1jKCJXb3JraW5nIGNsYXNzIiwgIk1pZGRsZSBjbGFzcyIpKQ0KTWVhbnNDbGFzc1AxPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNDbGFzc1AsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc0NsYXNzUEJFUzwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9TdWJDbGFzcywgZGF0YT1QYXJ0aWVzLkJFUy5DbGVhbiwgIHc9UGFydGllcy5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNDbGFzc1BCRVMyPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNDbGFzc1BCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zQ2xhc3NQQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNDbGFzc1AxJFByb2JhYmlsaXRpZXM8LSJHdWVzcyINCk1lYW5zQ2xhc3NQLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0NsYXNzUDEsIE1lYW5zQ2xhc3NQQkVTMikNCg0KDQpNZWFuc0ZhbUNsYXNzUDwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9TdWJGYW1DbGFzcywgZGF0YT1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4sICB3PVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMpDQpuZXcuZGF0IDwtIGRhdGEuZnJhbWUocHJvZmlsZV9TdWJGYW1DbGFzcz1jKCJGYW1pbHkgd29ya2luZyBjbGFzcyIsICJGYW1pbHkgbWlkZGxlIGNsYXNzIikpDQpNZWFuc0ZhbUNsYXNzUDE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0ZhbUNsYXNzUCwgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCk1lYW5zRmFtQ2xhc3NQQkVTPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX1N1YkZhbUNsYXNzLCBkYXRhPVBhcnRpZXMuQkVTLkNsZWFuLCAgdz1QYXJ0aWVzLkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc0ZhbUNsYXNzUEJFUzI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0ZhbUNsYXNzUEJFUywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCg0KTWVhbnNGYW1DbGFzc1BCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0ZhbUNsYXNzUDEkUHJvYmFiaWxpdGllczwtIkd1ZXNzIg0KTWVhbnNGYW1DbGFzc1AuQmluZDwtYmluZF9yb3dzKE1lYW5zRmFtQ2xhc3NQMSwgTWVhbnNGYW1DbGFzc1BCRVMyKQ0KDQpNZWFuc0hvbWVTdGF0dXNQPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX0hvbWVTdGF0dXMsIGRhdGE9UGFydGllcy5FeHBlcmltZW50LkNsZWFuLCAgdz1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfSG9tZVN0YXR1cz1jKCJSZW50cyBob21lIiwgIk93bnMgaG9tZSIpKQ0KTWVhbnNIb21lU3RhdHVzUDE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0hvbWVTdGF0dXNQLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KTWVhbnNIb21lU3RhdHVzUEJFUzwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9Ib21lU3RhdHVzLCBkYXRhPVBhcnRpZXMuQkVTLkNsZWFuLCAgdz1QYXJ0aWVzLkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc0hvbWVTdGF0dXNQQkVTMjwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zSG9tZVN0YXR1c1BCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zSG9tZVN0YXR1c1BCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc0hvbWVTdGF0dXNQMSRQcm9iYWJpbGl0aWVzPC0iR3Vlc3MiDQpNZWFuc0hvbWVTdGF0dXNQLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0hvbWVTdGF0dXNQMSwgTWVhbnNIb21lU3RhdHVzUEJFUzIpDQoNCg0KTWVhbnNSZWxpZ2lvblA8LWxtKHByb2ZpbGVfQ29ufnByb2ZpbGVfUmVsaWdpb24sIGRhdGE9UGFydGllcy5FeHBlcmltZW50LkNsZWFuLCAgdz1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfUmVsaWdpb249YygiUm9tYW4gQ2F0aG9saWMiLCJDaHVyY2ggb2YgRW5nbGFuZC9BbmdsaWNhbi9FcGlzY29wYWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSGluZHUiLCJJc2xhbSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNZXRob2Rpc3QiLCJQcmVzYnl0ZXJpYW4vQ2h1cmNoIG9mIFNjb3RsYW5kIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5vIHJlbGlnaW9uIiwiVW5rbm93biIgKSkNCk1lYW5zUmVsaWdpb25QMTwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zUmVsaWdpb25QLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KTWVhbnNSZWxpZ2lvblAxJHByb2ZpbGVfUmVsaWdpb248LWZhY3RvcihNZWFuc1JlbGlnaW9uUDEkcHJvZmlsZV9SZWxpZ2lvbiwgbGV2ZWxzPU1lYW5zUmVsaWdpb25QMSRwcm9maWxlX1JlbGlnaW9uW29yZGVyKE1lYW5zUmVsaWdpb25QMSRmaXQpXSkNCk1lYW5zUmVsaWdpb25QQkVTPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX1JlbGlnaW9uLCBkYXRhPVBhcnRpZXMuQkVTLkNsZWFuLCAgdz1QYXJ0aWVzLkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMpDQpNZWFuc1JlbGlnaW9uUEJFUzI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc1JlbGlnaW9uUEJFUywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCg0KTWVhbnNSZWxpZ2lvblBCRVMyJFByb2JhYmlsaXRpZXM8LSJCRVMiDQpNZWFuc1JlbGlnaW9uUDEkUHJvYmFiaWxpdGllczwtIkd1ZXNzIg0KTWVhbnNSZWxpZ2lvblAuQmluZDwtYmluZF9yb3dzKE1lYW5zUmVsaWdpb25QMSwgTWVhbnNSZWxpZ2lvblBCRVMyKQ0KDQoNCk1lYW5zRXRobmljaXR5UDwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9FdGhuaWNpdHksIGRhdGE9UGFydGllcy5FeHBlcmltZW50LkNsZWFuLCAgdz1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdDwtZGF0YS5mcmFtZShwcm9maWxlX0V0aG5pY2l0eT1mYWN0b3IoYygiQXNpYW4iLCJCbGFjayIsICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTWl4ZWQiLCJXaGl0ZSIsICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVW5rbm93biIpKSkNCk1lYW5zRXRobmljaXR5UDE8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0V0aG5pY2l0eVAsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQpNZWFuc0V0aG5pY2l0eVAxJHByb2ZpbGVfRXRobmljaXR5PC1mYWN0b3IoTWVhbnNFdGhuaWNpdHlQMSRwcm9maWxlX0V0aG5pY2l0eSwgbGV2ZWxzPU1lYW5zRXRobmljaXR5UDEkcHJvZmlsZV9FdGhuaWNpdHlbb3JkZXIoTWVhbnNFdGhuaWNpdHlQMSRmaXQpXSkNCk1lYW5zRXRobmljaXR5UEJFUzwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9FdGhuaWNpdHksIGRhdGE9UGFydGllcy5CRVMuQ2xlYW4sICB3PVBhcnRpZXMuQkVTLkNsZWFuJHByb2ZpbGVfd2VpZ2h0cykNCk1lYW5zRXRobmljaXR5UEJFUzI8LWRhdGEuZnJhbWUobmV3LmRhdCwgcHJlZGljdChNZWFuc0V0aG5pY2l0eVBCRVMsIG5ld2RhdGEgPSBuZXcuZGF0LCBpbnRlcnZhbCA9ICdjb25maWRlbmNlJykpDQoNCk1lYW5zRXRobmljaXR5UEJFUzIkUHJvYmFiaWxpdGllczwtIkJFUyINCk1lYW5zRXRobmljaXR5UDEkUHJvYmFiaWxpdGllczwtIkd1ZXNzIg0KTWVhbnNFdGhuaWNpdHlQLkJpbmQ8LWJpbmRfcm93cyhNZWFuc0V0aG5pY2l0eVAxLCBNZWFuc0V0aG5pY2l0eVBCRVMyKQ0KDQoNCk1lYW5zR2VuZGVyUDwtbG0ocHJvZmlsZV9Db25+cHJvZmlsZV9HZW5kZXIsIGRhdGE9UGFydGllcy5FeHBlcmltZW50LkNsZWFuLCAgdz1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzKQ0KbmV3LmRhdCA8LSBkYXRhLmZyYW1lKHByb2ZpbGVfR2VuZGVyPWMoIk1hbGUiLCAiRmVtYWxlIikpDQpNZWFuc0dlbmRlclAxPC1kYXRhLmZyYW1lKG5ldy5kYXQsIHByZWRpY3QoTWVhbnNHZW5kZXJQLCBuZXdkYXRhID0gbmV3LmRhdCwgaW50ZXJ2YWwgPSAnY29uZmlkZW5jZScpKQ0KTWVhbnNHZW5kZXJQQkVTPC1sbShwcm9maWxlX0Nvbn5wcm9maWxlX0dlbmRlciwgZGF0YT1QYXJ0aWVzLkJFUy5DbGVhbiwgIHc9UGFydGllcy5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzKQ0KTWVhbnNHZW5kZXJQQkVTMjwtZGF0YS5mcmFtZShuZXcuZGF0LCBwcmVkaWN0KE1lYW5zR2VuZGVyUEJFUywgbmV3ZGF0YSA9IG5ldy5kYXQsIGludGVydmFsID0gJ2NvbmZpZGVuY2UnKSkNCg0KTWVhbnNHZW5kZXJQQkVTMiRQcm9iYWJpbGl0aWVzPC0iQkVTIg0KTWVhbnNHZW5kZXJQMSRQcm9iYWJpbGl0aWVzPC0iR3Vlc3MiDQpNZWFuc0dlbmRlclAuQmluZDwtYmluZF9yb3dzKE1lYW5zR2VuZGVyUDEsIE1lYW5zR2VuZGVyUEJFUzIpDQoNCiMjIyMjIyMjIyMjIyMjIyMgRU5EIENIVU5LIDMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyMjIyMjIyMjIyMjIyMgU1RBUlQgQ0hVTksgNCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KI1RoZSBmb2xsb3dpbmcgY2h1bmsgYWxsb3dzIGZvciBhIGNvbXBhcmlzb24gYmV0d2VlbiBCRVMgYW5kIGd1ZXNzZXMsIG1vZGVsbGluZyBhbGwgcHJvZmlsZSBhdHRyaWJ1dGVzIHRvZ2V0aGVyLiBNb2RlbHMgYXJlIGVzdGFpbXRlZCBpbiBsb2dpdCBhbmQgbGluZWFyIGZvcm0uIExpbmVhciBtb2RlbCBsZWFkIHRvIGludmFsaWQgcHJlZGljdGlvbnMgZm9yIHRoZSBiaW5hcnkgdm90ZSBjaG9pY2UgZm9yIHNvbWUgcHJvZmlsZXMNCg0KDQojIEJyZXhpdCBMaW5lYXIgbW9kZWwsIHByZWRpY3RpbmcgZ3Vlc3NlZCBwcm9iYWJpbGl0eSBvZiBsZWF2ZSB3aXRoIHBvcmZpbGVzIGF0dHJpYnV0ZXMgYXMgcHJlZGljdG9ycw0KR3Vlc3NlZExlYXZlPC0gbG0ocHJvZmlsZV9MZWF2ZSB+IHByb2ZpbGVfR2VuZGVyK3Byb2ZpbGVfQWdlR3JvdXArcHJvZmlsZV9FZHVjYXRpb24rcHJvZmlsZV9SZWdpb24rcHJvZmlsZV9TdWJDbGFzcytwcm9maWxlX1N1YkZhbUNsYXNzK3Byb2ZpbGVfSG9tZVN0YXR1cytwcm9maWxlX1JlbGlnaW9uK3Byb2ZpbGVfRXRobmljaXR5K3Byb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lLCB3ZWlnaHRzPUJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cywgZGF0YSA9IEJyZXhpdC5FeHBlcmltZW50LkNsZWFuKQ0KQkVTTGVhdmU8LSBsbShwcm9maWxlX0xlYXZlIH4gcHJvZmlsZV9HZW5kZXIrcHJvZmlsZV9BZ2VHcm91cCtwcm9maWxlX0VkdWNhdGlvbitwcm9maWxlX1JlZ2lvbitwcm9maWxlX1N1YkNsYXNzK3Byb2ZpbGVfU3ViRmFtQ2xhc3MrcHJvZmlsZV9Ib21lU3RhdHVzK3Byb2ZpbGVfUmVsaWdpb24rcHJvZmlsZV9FdGhuaWNpdHkrcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWUsIHdlaWdodHM9QnJleGl0LkJFUy5DbGVhbiRwcm9maWxlX3dlaWdodHMsIGRhdGEgPSBCcmV4aXQuQkVTLkNsZWFuKQ0KDQojIEJyZXhpdCBMb2dpdCBtb2RlbA0KQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kTGVhdmVQcm9wIDwtIEJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHByb2ZpbGVfTGVhdmUvMTAwDQoNCiNCcmV4aXQuU2VwIGlzIGEgZGF0YXNldCBjcmVhdGVkIHRvIGVhc2UgbGF0ZXIgY29tYmluYXRpb24NCkJyZXhpdC5TZXA8LSBCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiAlPiUNCiAgc2VsZWN0KExlYXZlUHJvcCwgc3RhcnRzX3dpdGgoInByb2ZpbGUiKSwgcmVzcG9uZGVudF93ZWlnaHRzKQ0KY29sbmFtZXMoQnJleGl0LlNlcCk8LXBhc3RlMChjb2xuYW1lcyhCcmV4aXQuU2VwKSwgIl8iKQ0KDQpHdWVzc2VkTGVhdmVMb2dpdDAgPC0gZ2xtKCJMZWF2ZVByb3BfIH4gcHJvZmlsZV9HZW5kZXJfICtwcm9maWxlX0FnZUdyb3VwXyArcHJvZmlsZV9FZHVjYXRpb25fICtwcm9maWxlX1JlZ2lvbl8gK3Byb2ZpbGVfU3ViQ2xhc3NfICtwcm9maWxlX1N1YkZhbUNsYXNzXyArcHJvZmlsZV9Ib21lU3RhdHVzXyArcHJvZmlsZV9SZWxpZ2lvbl8gK3Byb2ZpbGVfRXRobmljaXR5XyArcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWVfIiwgd2VpZ2h0cz1CcmV4aXQuU2VwJHJlc3BvbmRlbnRfd2VpZ2h0c18sIGRhdGEgPSBCcmV4aXQuU2VwLCBmYW1pbHk9cXVhc2liaW5vbWlhbChsaW5rPSJsb2dpdCIpKQ0KDQojIFNhbWUgcmVncmVzc2lvbiB3aXRoIEJyZXhpdC5FeHBlcmltZW50LkNsZWFuIGRhdGFzZXQgKGd1ZXNzZXMgbW9kZWwpDQpHdWVzc2VkTGVhdmVMb2dpdDwtZ2xtKExlYXZlUHJvcCB+IHByb2ZpbGVfR2VuZGVyICtwcm9maWxlX0FnZUdyb3VwICtwcm9maWxlX0VkdWNhdGlvbiArcHJvZmlsZV9SZWdpb24gK3Byb2ZpbGVfU3ViQ2xhc3MgK3Byb2ZpbGVfU3ViRmFtQ2xhc3MgK3Byb2ZpbGVfSG9tZVN0YXR1cyArcHJvZmlsZV9SZWxpZ2lvbiArcHJvZmlsZV9FdGhuaWNpdHkgK3Byb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lLCB3ZWlnaHRzPUJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cywgZGF0YSA9IEJyZXhpdC5FeHBlcmltZW50LkNsZWFuLCBmYW1pbHk9cXVhc2liaW5vbWlhbChsaW5rPSJsb2dpdCIpKQ0KDQoNCiMgU2FtZSByZWdyZXNzaW9uIHdpdGggQnJleGl0LkJFUy5DbGVhbiBkYXRhc2V0IChCRVMgbW9kZWwpDQpCcmV4aXQuQkVTLkNsZWFuJExlYXZlLkJFUy5Qcm9wIDwtIEJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV9MZWF2ZS8xMDANCg0KI0JyZXhpdC5TZXAuQkVTIGlzIGEgZGF0YXNldCBjcmVhdGVkIHRvIGVhc2UgbGF0ZXIgY29tYmluYXRpb24NCkJyZXhpdC5TZXAuQkVTPC0gQnJleGl0LkJFUy5DbGVhbiAlPiUNCiAgc2VsZWN0KExlYXZlLkJFUy5Qcm9wLCBzdGFydHNfd2l0aCgicHJvZmlsZSIpKQ0KY29sbmFtZXMoQnJleGl0LlNlcC5CRVMpPC1wYXN0ZTAoY29sbmFtZXMoQnJleGl0LlNlcC5CRVMpLCAiXyIpDQoNCkJFU0xlYXZlTG9naXQwIDwtIGdsbSgiTGVhdmUuQkVTLlByb3BfIH4gcHJvZmlsZV9HZW5kZXJfICtwcm9maWxlX0FnZUdyb3VwXyArcHJvZmlsZV9FZHVjYXRpb25fICtwcm9maWxlX1JlZ2lvbl8gK3Byb2ZpbGVfU3ViQ2xhc3NfICtwcm9maWxlX1N1YkZhbUNsYXNzXyArcHJvZmlsZV9Ib21lU3RhdHVzXyArcHJvZmlsZV9SZWxpZ2lvbl8gK3Byb2ZpbGVfRXRobmljaXR5XyArcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWVfIiwgd2VpZ2h0cz1CcmV4aXQuU2VwLkJFUyRwcm9maWxlX3dlaWdodHNfLCBkYXRhID0gQnJleGl0LlNlcC5CRVMsIGZhbWlseT1xdWFzaWJpbm9taWFsKGxpbms9ImxvZ2l0IikpDQoNCiMgU2FtZSByZWdyZXNzaW9uIHdpdGggQnJleGl0LkJFUy5DbGVhbiBkYXRhc2V0IChCRVMgbW9kZWwpDQpCRVNMZWF2ZUxvZ2l0IDwtIGdsbShMZWF2ZS5CRVMuUHJvcCB+IHByb2ZpbGVfR2VuZGVyICtwcm9maWxlX0FnZUdyb3VwICtwcm9maWxlX0VkdWNhdGlvbiArcHJvZmlsZV9SZWdpb24gK3Byb2ZpbGVfU3ViQ2xhc3MgK3Byb2ZpbGVfU3ViRmFtQ2xhc3MgK3Byb2ZpbGVfSG9tZVN0YXR1cyArcHJvZmlsZV9SZWxpZ2lvbiArcHJvZmlsZV9FdGhuaWNpdHkgK3Byb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lLCB3ZWlnaHRzPUJyZXhpdC5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzLCBkYXRhID0gQnJleGl0LkJFUy5DbGVhbiwgZmFtaWx5PXF1YXNpYmlub21pYWwobGluaz0ibG9naXQiKSkNCg0KDQojIFBhcnRpZXMgTGluZWFyIG1vZGVscw0KR3Vlc3NlZENvbnNlcnZhdGl2ZSA8LSBsbShwcm9maWxlX0NvbiB+IHByb2ZpbGVfR2VuZGVyK3Byb2ZpbGVfQWdlR3JvdXArcHJvZmlsZV9FZHVjYXRpb24rcHJvZmlsZV9SZWdpb24rcHJvZmlsZV9TdWJDbGFzcytwcm9maWxlX1N1YkZhbUNsYXNzK3Byb2ZpbGVfSG9tZVN0YXR1cytwcm9maWxlX1JlbGlnaW9uK3Byb2ZpbGVfRXRobmljaXR5K3Byb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lICx3ZWlnaHRzID0gUGFydGllcy5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cyxkYXRhID0gUGFydGllcy5FeHBlcmltZW50LkNsZWFuKQ0KUmVhbENvbnNlcnZhdGl2ZTwtIGxtKHByb2ZpbGVfQ29uIH4gcHJvZmlsZV9HZW5kZXIrcHJvZmlsZV9BZ2VHcm91cCtwcm9maWxlX0VkdWNhdGlvbitwcm9maWxlX1JlZ2lvbitwcm9maWxlX1N1YkNsYXNzK3Byb2ZpbGVfU3ViRmFtQ2xhc3MrcHJvZmlsZV9Ib21lU3RhdHVzK3Byb2ZpbGVfUmVsaWdpb24rcHJvZmlsZV9FdGhuaWNpdHkrcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWUsIHdlaWdodHM9UGFydGllcy5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzLCBkYXRhID0gUGFydGllcy5CRVMuQ2xlYW4pDQoNCiMgUGFydGllcyBMb2dzdGljcyBtb2RlbHMNClBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRDb25Qcm9wIDwtIFBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRwcm9maWxlX0Nvbi8xMDANCg0KIyBQYXJ0eS5TZXAgZGF0YXNldCBjcmVhdGVkIHRvIHRvIGVhc2UgY29tYmluYXRpb24gbGF0ZXINClBhcnR5LlNlcDwtIFBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiAlPiUNCiAgc2VsZWN0KENvblByb3AsIHN0YXJ0c193aXRoKCJwcm9maWxlIiksIHJlc3BvbmRlbnRfd2VpZ2h0cykNCmNvbG5hbWVzKFBhcnR5LlNlcCk8LXBhc3RlMChjb2xuYW1lcyhQYXJ0eS5TZXApLCAiXyIpDQoNCkd1ZXNzZWRDb25zZXJ2YXRpdmVMb2dpdDAgPC0gZ2xtKCJDb25Qcm9wXyB+IHByb2ZpbGVfR2VuZGVyXyArcHJvZmlsZV9BZ2VHcm91cF8gK3Byb2ZpbGVfRWR1Y2F0aW9uXyArcHJvZmlsZV9SZWdpb25fICtwcm9maWxlX1N1YkNsYXNzXyArcHJvZmlsZV9TdWJGYW1DbGFzc18gK3Byb2ZpbGVfSG9tZVN0YXR1c18gK3Byb2ZpbGVfUmVsaWdpb25fICtwcm9maWxlX0V0aG5pY2l0eV8gK3Byb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lXyIsIHdlaWdodHM9UGFydHkuU2VwJHJlc3BvbmRlbnRfd2VpZ2h0c18sIGRhdGEgPSBQYXJ0eS5TZXAsIGZhbWlseT1xdWFzaWJpbm9taWFsKGxpbms9ImxvZ2l0IikpDQoNCiMgU2FtZSByZWdyZXNzaW9uIHdpdGggUGFydGllcy5FeHBlcmltZW50LkNsZWFuIChndWVzc2VzKQ0KR3Vlc3NlZENvbnNlcnZhdGl2ZUxvZ2l0IDwtIGdsbShDb25Qcm9wIH4gcHJvZmlsZV9HZW5kZXIgK3Byb2ZpbGVfQWdlR3JvdXAgICtwcm9maWxlX0VkdWNhdGlvbiAgK3Byb2ZpbGVfUmVnaW9uICArcHJvZmlsZV9TdWJDbGFzcyAgK3Byb2ZpbGVfU3ViRmFtQ2xhc3MgICtwcm9maWxlX0hvbWVTdGF0dXMgICtwcm9maWxlX1JlbGlnaW9uICArcHJvZmlsZV9FdGhuaWNpdHkgICtwcm9maWxlX0FubnVhbEhvdXNlaG9sZEluY29tZSAsIHdlaWdodHM9UGFydGllcy5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cywgZGF0YSA9IFBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiwgZmFtaWx5PXF1YXNpYmlub21pYWwobGluaz0ibG9naXQiKSkNCg0KI1NhbWUgcmVncmVzc2lvbiB3aXRoIEJFUyBtb2RlbA0KUGFydGllcy5CRVMuQ2xlYW4kcHJvZmlsZV9Db24uUHJvcCA8LSBQYXJ0aWVzLkJFUy5DbGVhbiRwcm9maWxlX0Nvbi8xMDANClBhcnR5LlNlcC5CRVM8LSBQYXJ0aWVzLkJFUy5DbGVhbiAlPiUNCiAgc2VsZWN0KHN0YXJ0c193aXRoKCJwcm9maWxlIikpDQpjb2xuYW1lcyhQYXJ0eS5TZXAuQkVTKTwtcGFzdGUwKGNvbG5hbWVzKFBhcnR5LlNlcC5CRVMpLCAiXyIpDQoNClJlYWxDb25zZXJ2YXRpdmVMb2dpdDAgPC0gZ2xtKCJwcm9maWxlX0Nvbi5Qcm9wXyB+IHByb2ZpbGVfR2VuZGVyXyArcHJvZmlsZV9BZ2VHcm91cF8gK3Byb2ZpbGVfRWR1Y2F0aW9uXyArcHJvZmlsZV9SZWdpb25fICtwcm9maWxlX1N1YkNsYXNzXyArcHJvZmlsZV9TdWJGYW1DbGFzc18gK3Byb2ZpbGVfSG9tZVN0YXR1c18gK3Byb2ZpbGVfUmVsaWdpb25fICtwcm9maWxlX0V0aG5pY2l0eV8gK3Byb2ZpbGVfQW5udWFsSG91c2Vob2xkSW5jb21lXyIsIHdlaWdodHM9UGFydHkuU2VwLkJFUyRwcm9maWxlX3dlaWdodHNfLCBkYXRhID0gUGFydHkuU2VwLkJFUywgZmFtaWx5PXF1YXNpYmlub21pYWwobGluaz0ibG9naXQiKSkNCg0KUmVhbENvbnNlcnZhdGl2ZUxvZ2l0IDwtIGdsbShwcm9maWxlX0Nvbi5Qcm9wIH4gcHJvZmlsZV9HZW5kZXIgK3Byb2ZpbGVfQWdlR3JvdXAgK3Byb2ZpbGVfRWR1Y2F0aW9uICtwcm9maWxlX1JlZ2lvbiArcHJvZmlsZV9TdWJDbGFzcyArcHJvZmlsZV9TdWJGYW1DbGFzcyArcHJvZmlsZV9Ib21lU3RhdHVzICtwcm9maWxlX1JlbGlnaW9uICtwcm9maWxlX0V0aG5pY2l0eSArcHJvZmlsZV9Bbm51YWxIb3VzZWhvbGRJbmNvbWUsIHdlaWdodHM9UGFydGllcy5CRVMuQ2xlYW4kcHJvZmlsZV93ZWlnaHRzXywgZGF0YSA9IFBhcnRpZXMuQkVTLkNsZWFuLCBmYW1pbHk9cXVhc2liaW5vbWlhbChsaW5rPSJsb2dpdCIpKQ0KDQojIyMjIyMjIyMjIyMjIyBFTkQgQ0hVTksgNCAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyMjIyMjIyMjIyMjIyBTVEFSVCBDSFVOSyA1ICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojVGhpcyBjaHVuayBhbGxvd3MgZm9yIGNvbXBhcmlzb25zIGJldHdlZW4gdGhlIHByZWRpY3RlZCBwcm9iYWJpbGl0aWVzIG9mIHByb2ZpbGVzIHZvdGluZyBpbiBhIGNlcnRhaW4gd2F5LCBpbXBsaWVkIGJ5IHRoZSBCRVMgbW9kZWwgYW5kIHRoZSBHdWVzc2VzIG1vZGVsLg0KDQojQnJleGl0IGV4cGVyaW1lbnQNCmZpdHRlZF9CRVNfQnJleDwtcHJlZGljdChCRVNMZWF2ZUxvZ2l0LCBuZXdkYXRhPUJyZXhpdC5CRVMuQ2xlYW4sIHR5cGU9InJlc3BvbnNlIikNCnByZWRpY3RlZF9FeHBlcmltZW50X0JyZXggPC0gcHJlZGljdChHdWVzc2VkTGVhdmVMb2dpdCAsIG5ld2RhdGE9QnJleGl0LkJFUy5DbGVhbix0eXBlPSJyZXNwb25zZSIpDQpmaXR0ZWRfQkVTX0JyZXhpdDwtZGF0YS5mcmFtZSgicHJvZmlsZSI9IGMoMToxNjk1KSwgZml0dGVkX0JFU19CcmV4KQ0KcHJlZGljdGVkX0V4cGVyaW1lbnRfQnJleGl0PC1kYXRhLmZyYW1lKCJwcm9maWxlIj0gYygxOjE2OTUpLCBwcmVkaWN0ZWRfRXhwZXJpbWVudF9CcmV4KQ0KUmVsYXRpb24uTW9kZWxzLkJyZXhpdDwtbWVyZ2UoZml0dGVkX0JFU19CcmV4aXQsIHByZWRpY3RlZF9FeHBlcmltZW50X0JyZXhpdCwgYnk9InByb2ZpbGUiKQ0KDQoNCiNQYXJ0aWVzIGV4cGVyaW1lbnQNCmZpdHRlZF9CRVNfQ29uPC0gcHJlZGljdChSZWFsQ29uc2VydmF0aXZlTG9naXQsIG5ld2RhdGE9UGFydGllcy5CRVMuQ2xlYW4sdHlwZT0icmVzcG9uc2UiKQ0KcHJlZGljdGVkX0V4cGVyaW1lbnRfQ29uIDwtIHByZWRpY3QoR3Vlc3NlZENvbnNlcnZhdGl2ZUxvZ2l0LCBuZXdkYXRhPVBhcnRpZXMuQkVTLkNsZWFuLHR5cGU9InJlc3BvbnNlIikNCg0KZml0dGVkX0JFU19Db25zZXJ2YXRpdmU8LWRhdGEuZnJhbWUoInByb2ZpbGUiPSBjKDE6MTM2MiksIGZpdHRlZF9CRVNfQ29uKQ0KcHJlZGljdGVkX0V4cGVyaW1lbnRfQ29uc2VydmF0aXZlPC1kYXRhLmZyYW1lKCJwcm9maWxlIj0gYygxOjEzNjIpLCBwcmVkaWN0ZWRfRXhwZXJpbWVudF9Db24pDQoNCg0KUmVsYXRpb24uTW9kZWxzLkNvbjwtbWVyZ2UoZml0dGVkX0JFU19Db25zZXJ2YXRpdmUsIHByZWRpY3RlZF9FeHBlcmltZW50X0NvbnNlcnZhdGl2ZSwgYnk9InByb2ZpbGUiKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyBFTkQgQ0hVTksgNSAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KDQojIyMjIyMjIyMjIyMjIyMjIFNUQVJUIENIVU5LIDYgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KI0luZGl2aWR1YWwgQnJpZXIgU2NvcmUgQnJleGl0DQpCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRCcmllclNjb3JlPC0gKChCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRwcm9maWxlX0xlYXZlLzEwMCktQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcHJvZmlsZV9MZWF2ZS5SZWFsKV4yDQoNCkJyaWVyU2NvcmVUb3RhbDwtd2VpZ2h0ZWQubWVhbihCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRCcmllclNjb3JlLCB3PUJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cywgbmEucm0gPSBUKQ0KDQoNCiNJbmRpdmlkdWFsIEJyaWVyIFNjb3JlIFBhcnRpZXMNClBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRCcmllclNjb3JlPC0gKFBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRwcm9maWxlX0NvbnNlcnZhdGl2ZS5SZWFsLShQYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcHJvZmlsZV9Db24vMTAwKSleMg0KDQpQYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kQnJpZXJTY29yZUJpbmFyeSA8LSAgKCgwLjUrMC41KnNpZ24oKFBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRwcm9maWxlX0NvbiAtIDUwKSkpLVBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRwcm9maWxlX0NvbnNlcnZhdGl2ZS5SZWFsKV4yDQoNCkJyaWVyU2NvcmVUb3RhbFBhcnRpZXM8LW1lYW4oUGFydGllcy5FeHBlcmltZW50LkNsZWFuJEJyaWVyU2NvcmUsIHc9UGFydGllcy5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfd2VpZ2h0cywgbmEucm0gPSBUKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyBFTkQgQ0hVTksgNiAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQoNCg0KIyMjIyMjIyMjIyMjIyMjIyBTVEFSVCBDSFVOSyA3ICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiNBdmVyYWdlIEJyaWVyIHNjb3JlDQoNCiNQcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmb3IgZXZlcnkgcHJvZmlsZSwgdXNpbmcgQkVTIG1vZGVsDQpmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwPC1wcmVkaWN0KEJFU0xlYXZlTG9naXQsIG5ld2RhdGE9QnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4sdHlwZT0icmVzcG9uc2UiKQ0KZml0dGVkX0JFU0NvZWZfQnJleEV4cDwtZGF0YS5mcmFtZShCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiwgZml0dGVkX0JFU0NvZWZfQnJleEV4cCkNCmZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwPC1wcmVkaWN0KFJlYWxDb25zZXJ2YXRpdmVMb2dpdCwgbmV3ZGF0YT1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4sdHlwZT0icmVzcG9uc2UiKQ0KZml0dGVkX0JFU0NvZWZfUGFydHlFeHA8LWRhdGEuZnJhbWUoUGFydGllcy5FeHBlcmltZW50LkNsZWFuLCBmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCkNCg0KZml0dGVkX0JFU0NvZWZfQnJleEV4cDwtZml0dGVkX0JFU0NvZWZfQnJleEV4cCAlPiUNCiAgZHBseXI6OnJlbmFtZShmaXR0ZWRCcmV4RXhwID0gZml0dGVkX0JFU0NvZWZfQnJleEV4cCkNCmZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwPC1maXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCAlPiUNCiAgZHBseXI6OnJlbmFtZShmaXR0ZWRQYXJ0eUV4cCA9IGZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwKQ0KDQojQnJpZXIgc2NvcmUgUGFydGllcw0KZml0dGVkX0JFU0NvZWZfUGFydHlFeHAkQnJpZXJTY29yZS5maXR0ZWQ8LSAoZml0dGVkX0JFU0NvZWZfUGFydHlFeHAkZml0dGVkUGFydHlFeHAtKGZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwJHByb2ZpbGVfQ29uLzEwMCkpXjINCkJyaWVyU2NvcmVUb3RhbFBhcnR5LmZpdHRlZDwtbWVhbihmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCRCcmllclNjb3JlLmZpdHRlZCwgdz1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzLCBuYS5ybSA9IFQpDQoNCiNCcmllciBTY29yZSBCcmV4aXQNCmZpdHRlZF9CRVNDb2VmX0JyZXhFeHAkQnJpZXJTY29yZS5maXR0ZWQ8LSAoZml0dGVkX0JFU0NvZWZfQnJleEV4cCRmaXR0ZWRCcmV4RXhwLShmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwJHByb2ZpbGVfTGVhdmUvMTAwKSleMg0KQnJpZXJTY29yZVRvdGFsQnJleC5maXR0ZWQ8LW1lYW4oZml0dGVkX0JFU0NvZWZfQnJleEV4cCRCcmllclNjb3JlLmZpdHRlZCwgdz1QYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF93ZWlnaHRzLCBuYS5ybSA9IFQpDQoNCiMjIyMjIyMgfiBbc2VjdGlvbiByZW1vdmVkIGZyb20gQ2h1bmsgNyBkdWUgdG8gc2VwYXJhdGUgYW5hbHlzaXNdIH4gIyMjIyMjIyMjIyMjIyMNCg0KI1ByZXBhcmluZyBkYXRhIGZvciBkaWNob3RvbW91cyBhbmFseXNpcyBpbiBQYXJ0aWVzIGV4cGVyaW1lbnQNCg0KZml0dGVkX0JFU0NvZWZfQnJleEV4cDwtIGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAgJT4lDQogIG11dGF0ZShwcm9maWxlX0xlYXZlLkRpY2ggPSBjYXNlX3doZW4oIHByb2ZpbGVfTGVhdmU8NTAgfiAiUmVtYWluIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZmlsZV9MZWF2ZT41MCB+ICJMZWF2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2ZpbGVfTGVhdmU9PTUwIH4gIjUwLTUwIikpDQpmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cDwtIGZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwICU+JQ0KICBtdXRhdGUocHJvZmlsZV9Db24uRGljaCA9IGNhc2Vfd2hlbiggcHJvZmlsZV9Db248NTAgfiAiTGFib3VyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2ZpbGVfQ29uPjUwIH4gIkNvbnNlcnZhdGl2ZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9maWxlX0Nvbj09NTAgfiAiNTAtNTAiKSkNCg0KZml0dGVkX0JFU0NvZWZfQnJleEV4cCA8LSBmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwICU+JQ0KICBtdXRhdGUocHJvZmlsZV9MZWF2ZS5SZWFsTiA9IGNhc2Vfd2hlbihwcm9maWxlX0xlYXZlLlJlYWwgPT0gMSB+ICJMZWF2ZSIgLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZmlsZV9MZWF2ZS5SZWFsID09IDAgfiAiUmVtYWluIikpDQoNCiNDcmVhdGluZyB2YXJpYWJsZSB0aGF0IGVxdWFscyAxIGZvciBjb3JyZWN0IGRpY2hvdG9tb3VzIGd1ZXNzIChhbmQgMC41IGZvciAiNTAtNTAiKS4gQnJleGl0DQpmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwIDwtIGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAgJT4lDQogIG11dGF0ZShDb3JyZWN0LkRpY2guR3Vlc3MuQnJleCA9IGNhc2Vfd2hlbihwcm9maWxlX0xlYXZlLkRpY2g9PSI1MC01MCIgfiAwLjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9maWxlX0xlYXZlLkRpY2g9PXByb2ZpbGVfTGVhdmUuUmVhbE4gfiAxLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvZmlsZV9MZWF2ZS5EaWNoIT1wcm9maWxlX0xlYXZlLlJlYWxOIH4gMCkpDQoNCmRjbHVzMTwtc3Z5ZGVzaWduKGlkcz1+MCwgd2VpZ2h0cz1maXR0ZWRfQkVTQ29lZl9CcmV4RXhwJHJlc3BvbmRlbnRfd2VpZ2h0cywgZGF0YT1maXR0ZWRfQkVTQ29lZl9CcmV4RXhwKQ0KbWVhbmxlYXZlPC1zdnltZWFuKH5Db3JyZWN0LkRpY2guR3Vlc3MuQnJleCwgZGNsdXMxICxkZWZmPUYsIG5hLnJtID1UKQ0KQ0k8LWNvbmZpbnQobWVhbmxlYXZlKQ0KQ0kuQ29ycmVjdC5CcmV4LkRpY2g8LWRhdGEuZnJhbWUoIkNvcnJlY3QiPSJCcmV4aXQiLCAiZml0Ij1tZWFubGVhdmVbMV0qMTAwLCAibHdyIj1DSVsxLDFdKjEwMCwidXByIj1DSVsxLDJdKjEwMCkNCg0KI0NyZWF0aW5nIHZhcmlhYmxlIHRoYXQgZXF1YWxzIDEgZm9yIGNvcnJlY3QgZGljaG90b21vdXMgZ3Vlc3MgKGFuZCAwLjUgZm9yICI1MC01MCIpLiBQYXJ0aWVzDQpmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCA8LSBmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCAlPiUNCiAgbXV0YXRlKHByb2ZpbGVfQ29uc2VydmF0aXZlLlJlYWxOID0gY2FzZV93aGVuKHByb2ZpbGVfQ29uc2VydmF0aXZlLlJlYWwgPT0gMSB+ICJDb25zZXJ2YXRpdmUiICwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9maWxlX0NvbnNlcnZhdGl2ZS5SZWFsID09IDAgfiAiTGFib3VyIikpDQpmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCA8LSBmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCAlPiUNCiAgbXV0YXRlKENvcnJlY3QuRGljaC5HdWVzcy5QYXJ0eSA9IGNhc2Vfd2hlbihwcm9maWxlX0Nvbi5EaWNoPT0iNTAtNTAiIH4gMC41LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2ZpbGVfQ29uLkRpY2g9PXByb2ZpbGVfQ29uc2VydmF0aXZlLlJlYWxOIH4gMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9maWxlX0Nvbi5EaWNoIT1wcm9maWxlX0NvbnNlcnZhdGl2ZS5SZWFsTiB+IDApKQ0KDQpkY2x1czE8LXN2eWRlc2lnbihpZHM9fjAsIHdlaWdodHM9Zml0dGVkX0JFU0NvZWZfUGFydHlFeHAkcmVzcG9uZGVudF93ZWlnaHRzLCBkYXRhPWZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwKQ0KbWVhbkNvbjwtc3Z5bWVhbih+Q29ycmVjdC5EaWNoLkd1ZXNzLlBhcnR5LCBkY2x1czEgLGRlZmY9RiwgbmEucm0gPVQpDQpDSTwtY29uZmludChtZWFuQ29uKQ0KQ0kuQ29ycmVjdC5QYXJ0eS5EaWNoPC1kYXRhLmZyYW1lKCJDb3JyZWN0Ij0iUGFydHkiLCAiZml0Ij1tZWFuQ29uWzFdKjEwMCwgImx3ciI9Q0lbMSwxXSoxMDAsInVwciI9Q0lbMSwyXSoxMDApDQoNCiMjIyMjIH4gW0VuZCBvZiBDaHVuayA3IHJlbW92ZWQgZHVlIHRvIHNlcGFyYXRlIGFuYWx5c2lzXSB+ICMjIyMNCg0KIyBzZXQgcG9saXRpY2FsIGF0dGVudGlvbiB0byBmYWN0b3IgDQoNCg0KUGFydGllcy5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uPC1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihQYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF9Qb2xpdGljYWxBdHRlbnRpb24pKQ0KDQpCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X1BvbGl0aWNhbEF0dGVudGlvbjwtYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF9Qb2xpdGljYWxBdHRlbnRpb24pKQ0KDQpmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwJHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uPC1hcy5udW1lcmljKGFzLmNoYXJhY3RlcihmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwJHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uKSkNCg0KZml0dGVkX0JFU0NvZWZfUGFydHlFeHAkcmVzcG9uZGVudF9Qb2xpdGljYWxBdHRlbnRpb248LWFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKGZpdHRlZF9CRVNDb2VmX1BhcnR5RXhwJHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uKSkNCg0KIyMjIyMjIyMjIyMjIyMjIyMgRU5EIENIVU5LIDcgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIEVuZDogIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyBbVGl0ZWxtYW4gJiBMYXVkZXJkYWxlICgyMDIxYykgY29kZV0gIyMjIyMjIyMjIyMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCmBgYA0KDQpgYGB7ciBjaGVja19sZXZlbHMsIGluY2x1ZGU9RkFMU0V9DQojY2hlY2sgbGV2ZWxzIG9mIHZhcmlhYmxlcyBiZWluZyB1c2VkDQpsZXZlbHMoQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3KQ0KbGV2ZWxzKGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAkcmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3KQ0KDQoNCmxldmVscyhCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3Bhc3R2b3RlX0VVUmVmKQ0KbGV2ZWxzKGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAkcmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZikNCg0KDQpsZXZlbHMoQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4kcmVzcG9uZGVudF9HZW5kZXIpIA0KbGV2ZWxzKGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAkcmVzcG9uZGVudF9HZW5kZXIpDQoNCg0KbGV2ZWxzKEJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfRWR1Y2F0aW9uKQ0KbGV2ZWxzKGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAkcmVzcG9uZGVudF9FZHVjYXRpb24pDQoNCg0KbGV2ZWxzKEJyZXhpdC5FeHBlcmltZW50LkNsZWFuJHJlc3BvbmRlbnRfUmVnaW9uKQ0KbGV2ZWxzKGZpdHRlZF9CRVNDb2VmX0JyZXhFeHAkcmVzcG9uZGVudF9SZWdpb24pDQpgYGANCg0KDQpWZXJpZmljYXRpb24gY2hlY2tzIGNvbmZpcm1lZCBjb25zaXN0ZW5jeSB3aXRoIHRoZSBvcmlnaW5hbCBkYXRhIGFuZCBtb2RlbCBzcGVjaWZpY2F0aW9uLCBpbmNsdWRpbmcgdHJlYXRtZW50IG9mIHBvbGl0aWNhbCBhdHRlbnRpb24gYXMgbnVtZXJpYyBhbmQgZGVtb2dyYXBoaWMgdmFyaWFibGVzIGFzIGNhdGVnb3JpY2FsIGZhY3RvcnMuIEV4YWN0IHJlcHJvZHVjdGlvbiBvZiByZXBvcnRlZCBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UgbGV2ZWxzIHdhcyBub3QgcG9zc2libGUgZHVlIHRvIGRpc2NyZXBhbmNpZXMgYmV0d2VlbiB0aGUgc3VwcGxpZWQgcmVwbGljYXRpb24gbWF0ZXJpYWxzIGFuZCBwdWJsaXNoZWQgb3V0cHV0czsgaG93ZXZlciwgY29lZmZpY2llbnQgZGlyZWN0aW9uLCBtb2RlbCBzcGVjaWZpY2F0aW9uLCBhbmQgdmFyaWFibGUgY29uc3RydWN0aW9uIHdlcmUgc3VjY2Vzc2Z1bGx5IHJlcHJvZHVjZWQgc3VmZmljaWVudGx5IHRvIGNvbmR1Y3QgdGhlIHJvYnVzdG5lc3MgZXh0ZW5zaW9uIChzZWUgQXBwZW5kaXggMiwgIFRhYmxlIEExKS4NCg0KYGBge3Igc3BlY19jaGVja3MsIHJlc3VsdHM9J2hpZGUnfQ0KIyBydW4gb3JpZ2luYWwgbW9kZWxzIGZvciB2ZXJpZmljYXRpb24NCiMgYnJpZXIgLSBicmV4aXQgDQptb2RlbEJyaWVyX2JyZXggPC0gbG0oQnJpZXJTY29yZSB+IHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uICsgcmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3ICsgcmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZiANCiAgICAgICAgICAgICAgICAgICAgICAgKyByZXNwb25kZW50X0FnZSArIHJlc3BvbmRlbnRfRWR1Y2F0aW9uICsgcmVzcG9uZGVudF9HZW5kZXIgKyByZXNwb25kZW50X1JlZ2lvbiwgDQogICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBCcmV4aXQuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMsDQogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBCcmV4aXQuRXhwZXJpbWVudC5DbGVhbikNCg0KIyBicmllciAtIHBhcnR5IA0KbW9kZWxCcmllcl9wYXJ0eSA8LSBsbShCcmllclNjb3JlIH4gcmVzcG9uZGVudF9Qb2xpdGljYWxBdHRlbnRpb24gKyByZXNwb25kZW50X3Bhc3R2b3RlXzIwMTcgKyByZXNwb25kZW50X3Bhc3R2b3RlX0VVUmVmIA0KICAgICAgICAgICAgICAgICAgICAgICArIHJlc3BvbmRlbnRfQWdlICsgcmVzcG9uZGVudF9FZHVjYXRpb24gKyByZXNwb25kZW50X0dlbmRlciArIHJlc3BvbmRlbnRfUmVnaW9uLCANCiAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cyA9IFBhcnRpZXMuRXhwZXJpbWVudC5DbGVhbiRyZXNwb25kZW50X3dlaWdodHMsDQogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBQYXJ0aWVzLkV4cGVyaW1lbnQuQ2xlYW4pDQoNCg0KDQojIGRpY2hvdCAtIGJyZXhpdCANCm1vZGVsRGljaG9fYnJleCA8LSBsbShDb3JyZWN0LkRpY2guR3Vlc3MuQnJleCB+IHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uICsgcmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3ICsgcmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZiANCiAgICAgICAgICAgICAgICAgICAgICArIHJlc3BvbmRlbnRfQWdlICsgcmVzcG9uZGVudF9FZHVjYXRpb24gKyByZXNwb25kZW50X0dlbmRlciArIHJlc3BvbmRlbnRfUmVnaW9uLA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwJHJlc3BvbmRlbnRfd2VpZ2h0cywgZGF0YSA9IGZpdHRlZF9CRVNDb2VmX0JyZXhFeHApDQoNCiMgZGljaG90IC0gUGFydHkgDQptb2RlbERpY2hvX3BhcnR5IDwtIGxtKENvcnJlY3QuRGljaC5HdWVzcy5QYXJ0eSB+IHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uICsgcmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3ICsgcmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZiANCiAgICAgICAgICAgICAgICAgICAgICArIHJlc3BvbmRlbnRfQWdlICsgcmVzcG9uZGVudF9FZHVjYXRpb24gKyByZXNwb25kZW50X0dlbmRlciArIHJlc3BvbmRlbnRfUmVnaW9uLA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCRyZXNwb25kZW50X3dlaWdodHMsIGRhdGEgPSBmaXR0ZWRfQkVTQ29lZl9QYXJ0eUV4cCkNCg0KDQpgYGANCg0KYGBge3IgdGFiZWxfY2hlY2tzLCBpbmNsdWRlPUZBTFNFfQ0Kc2pQbG90Ojp0YWJfbW9kZWwoDQogIG1vZGVsQnJpZXJfYnJleCwgbW9kZWxCcmllcl9wYXJ0eSwgbW9kZWxEaWNob19icmV4LCBtb2RlbERpY2hvX3BhcnR5LA0KICBmaWxlID0gIk91dHB1dHMvRGF0YUFwcGVuZGl4T3V0cHV0cy90YWJfQTEuaHRtbCIsDQogIGRpZ2l0cyA9IDMsDQogIHRpdGxlID0gIlRhYmxlIEExLiBWZXJpZmljYXRpb24gb2YgT3JpZ2luYWwgTW9kZWxzIFByZWRpY3RpbmcgR3Vlc3MgQWNjdXJhY3kgQWNyb3NzIEJyZXhpdCBhbmQgUGFydHkgRXhwZXJpbWVudHMuIiwNCiAgcHJlZC5sYWJlbHMgPSBjKA0KICAgICIoSW50ZXJjZXB0KSIgPSAiSW50ZXJjZXB0IiwNCiAgICAicmVzcG9uZGVudF9Qb2xpdGljYWxBdHRlbnRpb24iID0gIlBvbGl0aWNhbCBBdHRlbnRpb24iLA0KICAgICJyZXNwb25kZW50X3Bhc3R2b3RlXzIwMTdMYWJvdXIiID0gIlBhcnR5IFZvdGU6IExhYm91ciIsDQogICAgInJlc3BvbmRlbnRfcGFzdHZvdGVfMjAxN0xpYmVyYWwgRGVtb2NyYXQiID0gIlBhcnR5IFZvdGU6IExpYmVyYWwgRGVtb2NyYXQiLA0KICAgICJyZXNwb25kZW50X3Bhc3R2b3RlXzIwMTdTY290dGlzaCBOYXRpb25hbCBQYXJ0eSAoU05QKSIgPSAiUGFydHkgVm90ZTogU05QIiwNCiAgICAicmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3UGxhaWQgQ3ltcnUiID0gIlBhcnR5IFZvdGU6IFBsYWlkIEN5bXJ1IiwNCiAgICAicmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3VUsgSW5kZXBlbmRlbmNlIFBhcnR5IChVS0lQKSIgPSAiUGFydHkgVm90ZTogVUtJUCIsDQogICAgInJlc3BvbmRlbnRfcGFzdHZvdGVfMjAxN0dyZWVuIiA9ICJQYXJ0eSBWb3RlOiBHcmVlbiIsDQogICAgInJlc3BvbmRlbnRfcGFzdHZvdGVfMjAxN090aGVyIiA9ICJQYXJ0eSBWb3RlOiBPdGhlciIsDQogICAgInJlc3BvbmRlbnRfcGFzdHZvdGVfMjAxN0RvbsOi4oKs4oSidCBrbm93IiA9ICJQYXJ0eSBWb3RlOiBEb24ndCBLbm93IiwNCiAgICAicmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZkxlYXZlIiA9ICJFVSBSZWYuIFZvdGU6IExlYXZlIiwNCiAgICAicmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZkRpZCBub3Qgdm90ZSIgPSAiRVUgUmVmLiBWb3RlOiBEaWQgTm90IFZvdGUiLA0KICAgICJyZXNwb25kZW50X0FnZSIgPSAiQWdlIiwNCiAgICAicmVzcG9uZGVudF9FZHVjYXRpb25MZXZlbCAxIiA9ICJFZHVjYXRpb24gTGV2ZWw6IDEiLA0KICAgICJyZXNwb25kZW50X0VkdWNhdGlvbkxldmVsIDIiID0gIkVkdWNhdGlvbiBMZXZlbDogMiIsDQogICAgInJlc3BvbmRlbnRfRWR1Y2F0aW9uTGV2ZWwgMyIgPSAiRWR1Y2F0aW9uIExldmVsOiAzIiwNCiAgICAicmVzcG9uZGVudF9FZHVjYXRpb25MZXZlbCA0IiA9ICJFZHVjYXRpb24gTGV2ZWw6IDQiLA0KICAgICJyZXNwb25kZW50X0VkdWNhdGlvbkxldmVsIDUgYW5kIGFib3ZlIiA9ICJFZHVjYXRpb24gTGV2ZWw6IDUgYW5kIEFib3ZlIiwNCiAgICAicmVzcG9uZGVudF9FZHVjYXRpb25PdGhlciIgPSAiRWR1Y2F0aW9uIExldmVsOiBPdGhlciIsDQogICAgInJlc3BvbmRlbnRfR2VuZGVyRmVtYWxlIiA9ICJHZW5kZXI6IEZlbWFsZSIsDQogICAgInJlc3BvbmRlbnRfUmVnaW9uTm9ydGggV2VzdCIgPSAiUmVnaW9uOiBOb3J0aCBXZXN0IiwNCiAgICAicmVzcG9uZGVudF9SZWdpb25Zb3Jrc2hpcmUgYW5kIHRoZSBIdW1iZXIiID0gIlJlZ2lvbjogWW9ya3NoaXJlIGFuZCB0aGUgSHVtYmVyIiwNCiAgICAicmVzcG9uZGVudF9SZWdpb25FYXN0IE1pZGxhbmRzIiA9ICJSZWdpb246IEVhc3QgTWlkbGFuZHMiLA0KICAgICJyZXNwb25kZW50X1JlZ2lvbldlc3QgTWlkbGFuZHMiID0gIlJlZ2lvbjogV2VzdCBNaWRsYW5kcyIsDQogICAgInJlc3BvbmRlbnRfUmVnaW9uRWFzdCBvZiBFbmdsYW5kIiA9ICJSZWdpb246IEVhc3Qgb2YgRW5nbGFuZCIsDQogICAgInJlc3BvbmRlbnRfUmVnaW9uTG9uZG9uIiA9ICJSZWdpb246IExvbmRvbiIsDQogICAgInJlc3BvbmRlbnRfUmVnaW9uU291dGggRWFzdCIgPSAiUmVnaW9uOiBTb3V0aCBFYXN0IiwNCiAgICAicmVzcG9uZGVudF9SZWdpb25Tb3V0aCBXZXN0IiA9ICJSZWdpb246IFNvdXRoIFdlc3QiLA0KICAgICJyZXNwb25kZW50X1JlZ2lvbldhbGVzIiA9ICJSZWdpb246IFdhbGVzIiwNCiAgICAicmVzcG9uZGVudF9SZWdpb25TY290bGFuZCIgPSAiUmVnaW9uOiBTY290bGFuZCINCiAgICApLA0KICBkdi5sYWJlbHMgPSBjKA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5CcmllciBTY29yZTwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+QnJleGl0IEV4cC48L2Rpdj4iLA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5CcmllciBTY29yZTwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+UGFydHkgRXhwLjwvZGl2PiIsIA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5Db3JyZWN0IERpY2hvdG9taXNlZCBHdWVzczwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+QnJleGl0IEV4cC48L2Rpdj4iLCANCiAgICAiPGRpdiBzdHlsZT0ndGV4dC1hbGlnbjogY2VudGVyOyc+PGI+Q29ycmVjdCBEaWNob3RvbWlzZWQgR3Vlc3M8L2I+PGhyIHN0eWxlPSdib3JkZXI6IG5vbmU7IGJvcmRlci10b3A6IDFweCBzb2xpZCBibGFjazsgbWFyZ2luOiA0cHggMDsnPlBhcnR5IEV4cC48L2Rpdj4iDQogICksDQogIHN0cmluZy5wcmVkID0gIkNvZWZmaWNpZW50IiwNCiAgc2hvdy5jaSA9IEZBTFNFLA0KICBzaG93LnNlID0gVFJVRSwNCiAgc3RyaW5nLnNlID0gIlN0YW5kYXJkIEVycm9yIiwNCiAgcC5zdHlsZSA9ICJzdGFycyIsDQogIENTUyA9IGxpc3QoDQogICAgY3NzLmNvbGhlYWRlcnMgPSAidGV4dC1hbGlnbjogY2VudGVyOyB2ZXJ0aWNhbC1hbGlnbjogYm90dG9tOyINCiAgKQ0KKQ0KYGBgDQoNCg0KRm91ciBhbmFseXRpY2FsIGRhdGFzZXRzIHdlcmUgc3Vic2VxdWVudGx5IGNvbnN0cnVjdGVkIChgYnJleGl0X2JyaWVyYCwgYGJyZXhpdF9kaWNob2AsIGBwYXJ0aWVzX2JyaWVyYCwgYW5kIGBwYXJ0aWVzX2RpY2hvYCkgY29udGFpbmluZyB0aGUgdmFyaWFibGVzIHJlcXVpcmVkIGZvciB0aGUgcmVwbGljYXRlZCBhbmQgZXh0ZW5kZWQgbW9kZWxzLg0KDQpgYGB7ciBhbmFseXNpc19kZiwgZWNobz1UUlVFfQ0KIyBDcmVhdGUgbmV3IGRhdGFmcmFtZXMgcmVsZXZhbnQgZm9yIHJlcGxpY2F0aW9uIHByb2plY3QgDQoNCg0KIyAxLiBCUkVYSVQgREFUQTogTmV3IGRhdGFmcmFtZSBleHRyYWN0aW5nIGFuZCByZW5hbWluZyBvbmx5IHRoZSBjb3JlIGFuYWx5dGljYWwgdmFyaWFibGVzDQpicmV4aXRfYnJpZXIgPC0gQnJleGl0LkV4cGVyaW1lbnQuQ2xlYW4gJT4lDQogIHNlbGVjdCgNCiAgICByZXNwb25kZW50X2lkICAgICAgID0gcmVzcG9uZGVudF9JRCwNCiAgICB3ZWlnaHRzICAgICAgICAgICAgID0gcmVzcG9uZGVudF93ZWlnaHRzLA0KICAgIGJyaWVyX3Njb3JlICAgICAgICAgPSBCcmllclNjb3JlLA0KICAgIHBvbF9hdHRlbnRpb24gICAgICAgPSByZXNwb25kZW50X1BvbGl0aWNhbEF0dGVudGlvbiwNCiAgICBwYXN0X3ZvdGVfMjAxNyAgICAgID0gcmVzcG9uZGVudF9wYXN0dm90ZV8yMDE3LA0KICAgIGV1X3JlZl92b3RlICAgICAgICAgPSByZXNwb25kZW50X3Bhc3R2b3RlX0VVUmVmLA0KICAgIGFnZSAgICAgICAgICAgICAgICAgPSByZXNwb25kZW50X0FnZSwNCiAgICBlZHVjYXRpb24gICAgICAgICAgID0gcmVzcG9uZGVudF9FZHVjYXRpb24sDQogICAgZ2VuZGVyICAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfR2VuZGVyLA0KICAgIHJlZ2lvbiAgICAgICAgICAgICAgPSByZXNwb25kZW50X1JlZ2lvbg0KICApDQoNCiMgMi4gQlJFWElUICYgRElDSE9UT01PVVMgREFUQTogTmV3IGRhdGFmcmFtZSBmb3IgRGljaG8gYW5hbHlzaXMNCmJyZXhpdF9kaWNobyA8LSBmaXR0ZWRfQkVTQ29lZl9CcmV4RXhwICU+JQ0KICBzZWxlY3QoDQogICAgcmVzcG9uZGVudF9pZCAgICAgICA9IHJlc3BvbmRlbnRfSUQsDQogICAgd2VpZ2h0cyAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfd2VpZ2h0cywNCiAgICBkaWNob19zY29yZSAgICAgICAgID0gQ29ycmVjdC5EaWNoLkd1ZXNzLkJyZXgsDQogICAgcG9sX2F0dGVudGlvbiAgICAgICA9IHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uLA0KICAgIHBhc3Rfdm90ZV8yMDE3ICAgICAgPSByZXNwb25kZW50X3Bhc3R2b3RlXzIwMTcsDQogICAgZXVfcmVmX3ZvdGUgICAgICAgICA9IHJlc3BvbmRlbnRfcGFzdHZvdGVfRVVSZWYsDQogICAgYWdlICAgICAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfQWdlLA0KICAgIGVkdWNhdGlvbiAgICAgICAgICAgPSByZXNwb25kZW50X0VkdWNhdGlvbiwNCiAgICBnZW5kZXIgICAgICAgICAgICAgID0gcmVzcG9uZGVudF9HZW5kZXIsDQogICAgcmVnaW9uICAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfUmVnaW9uDQogICkNCg0KIyAzLiBQQVJUSUVTIEVYUEVSSU1FTlQgREFUQTogTmV3IGRhdGFmcmFtZSBleHRyYWN0aW5nIG9ubHkgdGhlIGNvcmUgYW5hbHl0aWNhbCB2YXJpYWJsZXMNCnBhcnRpZXNfYnJpZXIgPC0gUGFydGllcy5FeHBlcmltZW50LkNsZWFuICU+JQ0KICBzZWxlY3QoDQogICAgcmVzcG9uZGVudF9pZCAgICAgICA9IHJlc3BvbmRlbnRfSUQsDQogICAgd2VpZ2h0cyAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfd2VpZ2h0cywNCiAgICBicmllcl9zY29yZSAgICAgICAgID0gQnJpZXJTY29yZSwNCiAgICBwb2xfYXR0ZW50aW9uICAgICAgID0gcmVzcG9uZGVudF9Qb2xpdGljYWxBdHRlbnRpb24sDQogICAgcGFzdF92b3RlXzIwMTcgICAgICA9IHJlc3BvbmRlbnRfcGFzdHZvdGVfMjAxNywNCiAgICBldV9yZWZfdm90ZSAgICAgICAgID0gcmVzcG9uZGVudF9wYXN0dm90ZV9FVVJlZiwNCiAgICBhZ2UgICAgICAgICAgICAgICAgID0gcmVzcG9uZGVudF9BZ2UsDQogICAgZWR1Y2F0aW9uICAgICAgICAgICA9IHJlc3BvbmRlbnRfRWR1Y2F0aW9uLA0KICAgIGdlbmRlciAgICAgICAgICAgICAgPSByZXNwb25kZW50X0dlbmRlciwNCiAgICByZWdpb24gICAgICAgICAgICAgID0gcmVzcG9uZGVudF9SZWdpb24NCiAgKQ0KDQojIDQuIFBBUlRJRVMgJiBESUNIT1RPTU9VUzogTmV3IGRhdGFmcmFtZSBmb3IgZGljaG8gYW5hbHlzaXMNCnBhcnRpZXNfZGljaG8gPC0gZml0dGVkX0JFU0NvZWZfUGFydHlFeHAgJT4lDQogIHNlbGVjdCgNCiAgICByZXNwb25kZW50X2lkICAgICAgID0gcmVzcG9uZGVudF9JRCwNCiAgICB3ZWlnaHRzICAgICAgICAgICAgID0gcmVzcG9uZGVudF93ZWlnaHRzLA0KICAgIGRpY2hvX3Njb3JlICAgICAgICAgPSBDb3JyZWN0LkRpY2guR3Vlc3MuUGFydHksDQogICAgcG9sX2F0dGVudGlvbiAgICAgICA9IHJlc3BvbmRlbnRfUG9saXRpY2FsQXR0ZW50aW9uLA0KICAgIHBhc3Rfdm90ZV8yMDE3ICAgICAgPSByZXNwb25kZW50X3Bhc3R2b3RlXzIwMTcsDQogICAgZXVfcmVmX3ZvdGUgICAgICAgICA9IHJlc3BvbmRlbnRfcGFzdHZvdGVfRVVSZWYsDQogICAgYWdlICAgICAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfQWdlLA0KICAgIGVkdWNhdGlvbiAgICAgICAgICAgPSByZXNwb25kZW50X0VkdWNhdGlvbiwNCiAgICBnZW5kZXIgICAgICAgICAgICAgID0gcmVzcG9uZGVudF9HZW5kZXIsDQogICAgcmVnaW9uICAgICAgICAgICAgICA9IHJlc3BvbmRlbnRfUmVnaW9uDQogICkNCg0KYGBgDQoNCmBgYHtyIGRhdGFfY2hlY2tzLCBpbmNsdWRlPUZBTFNFfQ0KIyBjbGFzcyBjaGVjayBiZWZvcmUgbW9kZWxsaW5nIA0KY2xhc3MoYnJleGl0X2JyaWVyJHJlc3BvbmRlbnRfaWQpICMgbWFrZSBhIGZhY3Rvcg0KY2xhc3MoYnJleGl0X2JyaWVyJGJyaWVyX3Njb3JlKQ0KY2xhc3MoYnJleGl0X2JyaWVyJHBvbF9hdHRlbnRpb24pICMgbWFrZSBudW1lcmljDQpjbGFzcyhicmV4aXRfYnJpZXIkcGFzdF92b3RlXzIwMTcpDQpjbGFzcyhicmV4aXRfYnJpZXIkZXVfcmVmX3ZvdGUpDQpjbGFzcyhicmV4aXRfYnJpZXIkYWdlKQ0KY2xhc3MoYnJleGl0X2JyaWVyJGVkdWNhdGlvbikNCmNsYXNzKGJyZXhpdF9icmllciRnZW5kZXIpDQpjbGFzcyhicmV4aXRfYnJpZXIkcmVnaW9uKQ0KDQpjbGFzcyhicmV4aXRfZGljaG8kZGljaG9fc2NvcmUpDQoNCiMgY2hlY2sgcm93cyANCm5yb3coYnJleGl0X2JyaWVyKQ0KbnJvdyhwYXJ0aWVzX2JyaWVyKQ0KYGBgDQoNClZhcmlhYmxlcyB3ZXJlIHJlbmFtZWQgYW5kIHN1YnNldHRlZCB3aGlsZSBwcmVzZXJ2aW5nIHRoZSBvcmlnaW5hbCBjb2Rpbmcgc3RydWN0dXJlLCBmYWN0b3IgbGV2ZWxzLCBhbmQgcmVmZXJlbmNlIGNhdGVnb3JpZXMuIEJvdGggbmV3IGBicmV4aXRfYCBhbmQgYHBhcnRpZXNfYCBkYXRhc2V0cyBjb250YWluIGlkZW50aWNhbCBwcmVkaWN0b3IgdmFyaWFibGUgaW5mb3JtYXRpb24uIERhdGFzZXRzIGFyZSBkaXN0aW5ndWlzaGVkIGJ5IGNvbnRhaW5pbmcgZWl0aGVyIGBicmllcmAgb3IgYGRpY2hvYCBkZXBlbmRlbnQgdmFyaWFibGVzLg0KDQpgYGB7ciByZWNvZGVfbmV3LCByZXN1bHRzPSdoaWRlJ30NCiMgcmVjb2RlIHZhcmlhYmxlcyBmb3IgbW9kZWxsaW5nIGFuZCBpbnRlcnByZXRhdGlvbg0KDQojIGVuc3VyZSBwb2xfYXR0ZW50aW9uIGlzIGEgbnVtZXJpYyB2YXJpYWJsZSBpbiBhbGwgZGF0YXNldHMgDQpicmV4aXRfYnJpZXIkcG9sX2F0dGVudGlvbiAgPC0gYXMubnVtZXJpYyhicmV4aXRfYnJpZXIkcG9sX2F0dGVudGlvbikNCmJyZXhpdF9kaWNobyRwb2xfYXR0ZW50aW9uIDwtIGFzLm51bWVyaWMoYnJleGl0X2RpY2hvJHBvbF9hdHRlbnRpb24pDQpwYXJ0aWVzX2JyaWVyJHBvbF9hdHRlbnRpb24gICA8LSBhcy5udW1lcmljKHBhcnRpZXNfYnJpZXIkcG9sX2F0dGVudGlvbikNCnBhcnRpZXNfZGljaG8kcG9sX2F0dGVudGlvbiA8LSBhcy5udW1lcmljKHBhcnRpZXNfZGljaG8kcG9sX2F0dGVudGlvbikNCg0KDQojIEVuc3VyZSByZXNwb25kZW50X2lkIGlzIHNldCB1cCBhcyBhIGZhY3RvciB2YXJpYWJsZSBpbiBhbGwgZGF0YXNldHMNCmJyZXhpdF9icmllciRyZXNwb25kZW50X2lkICA8LSBhcy5mYWN0b3IoYnJleGl0X2JyaWVyJHJlc3BvbmRlbnRfaWQpDQpicmV4aXRfZGljaG8kcmVzcG9uZGVudF9pZCA8LSBhcy5mYWN0b3IoYnJleGl0X2RpY2hvJHJlc3BvbmRlbnRfaWQpDQpwYXJ0aWVzX2JyaWVyJHJlc3BvbmRlbnRfaWQgICA8LSBhcy5mYWN0b3IocGFydGllc19icmllciRyZXNwb25kZW50X2lkKQ0KcGFydGllc19kaWNobyRyZXNwb25kZW50X2lkICA8LSBhcy5mYWN0b3IocGFydGllc19kaWNobyRyZXNwb25kZW50X2lkKQ0KDQoNCmBgYA0KDQoNCmBgYHtyIGRhdGFfbGV2ZWxzLCBpbmNsdWRlPUZBTFNFfQ0KDQojIGNoZWNrIGxldmVscyBvZiBhbGwgZmFjdG9yIHZzIChzZXQgYmFzZWxpbmUgY2F0ZWdvcmllcyB0byBzYW1lIGFzIG9yaWdpbmFsIG1vZGVscyBmb3IgZWFzZSBvZiBjb21wYXJpc29uKQ0KbGV2ZWxzKGJyZXhpdF9icmllciRwYXN0X3ZvdGVfMjAxNykNCmxldmVscyhicmV4aXRfZGljaG8kcGFzdF92b3RlXzIwMTcpDQpsZXZlbHMocGFydGllc19icmllciRwYXN0X3ZvdGVfMjAxNykNCmxldmVscyhwYXJ0aWVzX2RpY2hvJHBhc3Rfdm90ZV8yMDE3KQ0KDQpsZXZlbHMoYnJleGl0X2JyaWVyJGV1X3JlZl92b3RlKQ0KbGV2ZWxzKGJyZXhpdF9icmllciRlZHVjYXRpb24pDQpsZXZlbHMoYnJleGl0X2JyaWVyJGdlbmRlcikNCmxldmVscyhicmV4aXRfYnJpZXIkcmVnaW9uKQ0KDQpgYGANCg0KDQpUaGUgcmVwbGljYXRlZCBtb2RlbCBleGFtaW5lcyBwcmVkaWN0b3JzIG9mIHJlc3BvbmRlbnQgZm9yZWNhc3RpbmcgYWNjdXJhY3kgdXNpbmcgdHdvIGRlcGVuZGVudCB2YXJpYWJsZXM6IHRoZSBjb250aW51b3VzIEJyaWVyIFNjb3JlLCB3aGVyZSBsb3dlciB2YWx1ZXMgaW5kaWNhdGUgZ3JlYXRlciBwcmVkaWN0aW9uIGFjY3VyYWN5LCBhbmQgdGhlIENvcnJlY3QgRGljaG90b21pc2VkIEd1ZXNzIG1lYXN1cmUsIHdoaWNoIGNhcHR1cmVzIGRpcmVjdGlvbmFsIGFjY3VyYWN5IG9uIGEgMOKAkzAuNeKAkzEgc2NhbGUuIA0KDQpgYGB7ciBkdl9icmllcl9wbG90LCBlY2hvPUZBTFNFfQ0KDQojIGdyYXBocyBtYWRlIGZvciBlYWNoIGV4cGVyaW1lbnQgKGJyZXhpdC9wYXJ0eSkgdGhlbiBjb21iaW5lZCB1c2luZyBwYXRjaHdvcmsgcGFja2FnZQ0KDQoNCiMgQnJleGl0IEV4cGVyaW1lbnQNCg0KYnJleF9icmllcl9wbG90IDwtIGdncGxvdChkYXRhID0gYnJleGl0X2JyaWVyLCBhZXMoeCA9IGJyaWVyX3Njb3JlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgNCiAgICBiaW53aWR0aCA9IDAuMDUsICAgICAgICANCiAgICBmaWxsID0gIiMyYzNlNTAiLCAgICAgICANCiAgICBjb2xvciA9ICJ3aGl0ZSIsICAgICAgICANCiAgICBhbHBoYSA9IDAuOCAgICAgICAgICAgICANCiAgKSArDQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTMpICsgDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgaGp1c3Q9IDAuNSksIA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSksDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkJyZXhpdCBFeHBlcmltZW50IiwNCiAgICB4ID0gIkJyaWVyIFNjb3JlICgwIC0gMSkiLA0KICAgIHkgPSAiQ291bnQiDQogICkNCg0KDQojIFBhcnRpZXMgRXhwZXJpbWVudCANCg0KcGFydHlfYnJpZXJfcGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IHBhcnRpZXNfYnJpZXIsIGFlcyh4ID0gYnJpZXJfc2NvcmUpKSArDQogIGdlb21faGlzdG9ncmFtKA0KICAgIGJpbndpZHRoID0gMC4wNSwgICAgICAgIA0KICAgIGZpbGwgPSAiIzJjM2U1MCIsICAgICAgIA0KICAgIGNvbG9yID0gIndoaXRlIiwgICAgICAgIA0KICAgIGFscGhhID0gMC44ICAgICAgICAgICAgIA0KICApICsNCiAgdGhlbWVfbWluaW1hbChiYXNlX3NpemUgPSAxMykgKyANCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksIA0KICAgIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSksDQogICkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlBhcnRpZXMgRXhwZXJpbWVudCIsDQogICAgeCA9ICJCcmllciBTY29yZSAoMCAtIDEpIiwNCiAgICB5ID0gIkNvdW50IiAgDQogICkNCg0KDQojIGFsaWduIHRoZSB5LWF4aXMNCmJyZXhfYnJpZXJfcGxvdF9hbGlnbmVkIDwtIGJyZXhfYnJpZXJfcGxvdCArIGNvb3JkX2NhcnRlc2lhbih5bGltID0gYygwLCA2MDApKQ0KcGFydHlfYnJpZXJfcGxvdF9hbGlnbmVkIDwtIHBhcnR5X2JyaWVyX3Bsb3QgKyBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMCwgNjAwKSkNCg0KDQoNCiMgQ29tYmluZSBib3RoIGhpc3RvZ3JhbXMgdXNpbmcgcGF0Y2h3b3JrIHBhY2thZ2UgIyAtIGR2MSBwbG90DQpkdjEgPC0gKGJyZXhfYnJpZXJfcGxvdF9hbGlnbmVkICsgcGFydHlfYnJpZXJfcGxvdF9hbGlnbmVkKSArDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJGaWd1cmUgMTogT3ZlcmFsbCBEaXN0cmlidXRpb24gb2YgQnJpZXIgU2NvcmVzIiwNCiAgICBzdWJ0aXRsZSA9ICJDb21wYXJpbmcgcHJlZGljdGlvbiBhY2N1cmFjeSBhY3Jvc3MgYm90aCBleHBlcmltZW50YWwgZ3JvdXBzIiwNCiAgICBjYXB0aW9uID0gIk5vdGU6IEEgbG93ZXIgQnJpZXIgU2NvcmUgaW5kaWNhdGVzIGJldHRlciBwcmVkaWN0aW9uIGFjY3VyYWN5LiIsDQogICAgdGhlbWUgPSB0aGVtZSgNCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksDQogICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywgZmFjZSA9ICJpdGFsaWMiLCBoanVzdCA9IDAuNSksDQogICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gIml0YWxpYyIsIGhqdXN0ID0gMC41LCBtYXJnaW4gPSBtYXJnaW4odCA9IDE1KSkNCiAgICApDQogICkNCg0KZ2dzYXZlKGZpbGVuYW1lID0gIk91dHB1dHMvUmVzdWx0cy9maWdfMS5wbmciLCBkdjEpDQoNCmR2MQ0KDQpgYGANCg0KRmlndXJlIDEgc2hvd3MgdGhhdCBCcmllciBzY29yZXMgYXJlIGRpc3RyaWJ1dGVkIHNpbWlsYXJseSBhY3Jvc3MgYm90aCBleHBlcmltZW50YWwgZ3JvdXBzLCB3aXRoIG1vc3QgcmVzcG9uc2VzIGNvbmNlbnRyYXRlZCB0b3dhcmQgbG93ZXIgdmFsdWVzLCBpbmRpY2F0aW5nIGJldHRlciBhY2N1cmFjeSBhbW9uZyByZXNwb25kZW50cy4gSG93ZXZlciwgdGhlIHNwaWtlIHByZXNlbnQgaW4gYm90aCBncm91cHMgc3VnZ2VzdCB0aGF0IHJlc3BvbmRlbnRzIG1heSBzeXN0ZW1hdGljYWxseSBvdmVyZXN0aW1hdGUgdGhlIGFjY3VyYWN5IG9mIHRoZWlyIGd1ZXNzZXMuIA0KDQoNCmBgYHtyIGR2X2RpY2hvX3Bsb3QsIGVjaG89RkFMU0V9DQojIE1ha2UgRGlzdHJpYnV0aW9uIHBsb3RzIGZvciBEaWNobyB2YXJpYWJsZXMgZm9yIGJvdGggYnJleGl0IGFuZCBwYXJ0eQ0KIyBjb21iaW5lIHBsb3RzDQoNCiMgYmFyIGNoYXJ0IA0KYnJleF9kaWNob19wbG90IDwtIGdncGxvdCgNCiAgYnJleGl0X2RpY2hvICU+JSANCiAgICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShkaWNob19zY29yZSkpLA0KICBhZXMoeCA9IGZhY3RvcihkaWNob19zY29yZSkpDQopICsNCiAgZ2VvbV9iYXIoZmlsbCA9ICIjNEM3OEE4IiwgYWVzKHkgPSBhZnRlcl9zdGF0KHByb3ApLCBncm91cCA9IDEpKSArDQogIHNjYWxlX3hfZGlzY3JldGUoDQogICAgbGFiZWxzID0gYygNCiAgICAgICIwIiA9ICJJbmNvcnJlY3QiLA0KICAgICAgIjAuNSIgPSAiNTDigJM1MCAoVW5zdXJlKSIsDQogICAgICAiMSIgPSAiQ29ycmVjdCINCiAgICApDQogICkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoDQogICAgbGFiZWxzID0gc2NhbGVzOjpwZXJjZW50LA0KICAgIGxpbWl0cyA9IGMoMCwgMSksDQogICAgYnJlYWtzID0gc2VxKDAsIDEsIDAuMjUpDQogICkgKw0KICBsYWJzKA0KICAgIHggPSAiRGlyZWN0aW9uYWwgQWNjdXJhY3kiLA0KICAgIHkgPSAiUHJvcG9ydGlvbiIsDQogICAgdGl0bGUgPSAiQnJleGl0IEV4cGVyaW1lbnQiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0PSAwLjUpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApDQoNCiMgUGFydGllcyBFeHBlcmltZW50IA0KDQojYmFyIGNoYXJ0DQpwYXJ0eV9kaWNob19wbG90IDwtIGdncGxvdCgNCiAgcGFydGllc19kaWNobyAlPiUgDQogICAgZHBseXI6OmZpbHRlcighaXMubmEoZGljaG9fc2NvcmUpKSwNCiAgYWVzKHggPSBmYWN0b3IoZGljaG9fc2NvcmUpKQ0KKSArDQogIGdlb21fYmFyKGZpbGwgPSAiIzRDNzhBOCIsIGFlcyh5ID0gYWZ0ZXJfc3RhdChwcm9wKSwgZ3JvdXAgPSAxKSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKA0KICAgIGxhYmVscyA9IGMoDQogICAgICAiMCIgPSAiSW5jb3JyZWN0IiwNCiAgICAgICIwLjUiID0gIjUw4oCTNTAgKFVuc3VyZSkiLA0KICAgICAgIjEiID0gIkNvcnJlY3QiDQogICAgKQ0KICApICsNCiAgc2NhbGVfeV9jb250aW51b3VzKA0KICAgIGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCwNCiAgICBsaW1pdHMgPSBjKDAsIDEpLA0KICAgIGJyZWFrcyA9IHNlcSgwLCAxLCAwLjI1KQ0KICApICsNCiAgbGFicygNCiAgICB4ID0gIkRpcmVjdGlvbmFsIEFjY3VyYWN5IiwNCiAgICB5ID0gIlByb3BvcnRpb24iLA0KICAgIHRpdGxlID0gIlBhcnR5IEV4cGVyaW1lbnQiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIsIGhqdXN0PSAwLjUpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEpLA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIg0KICApDQoNCiMgcGF0Y2h3b3JrIHR3byBwbG90cyB0b2dldGhlciAtIGR2MiBwbG90IA0KZHYyIDwtIGJyZXhfZGljaG9fcGxvdCArIHBhcnR5X2RpY2hvX3Bsb3QgKw0KICBwbG90X2xheW91dChheGVzID0gImNvbGxlY3QiKSArDQogIHBsb3RfYW5ub3RhdGlvbigNCiAgICB0aXRsZSA9ICJGaWd1cmUgMjogRGlyZWN0aW9uYWwgQWNjdXJhY3kgb2YgUmVzcG9uZGVudCBHdWVzc2VzIiwNCiAgICBzdWJ0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgQ29ycmVjdCBEaWNob3RvbWlzZWQgR3Vlc3Mgc2NvcmVzIGFjcm9zcyBib3RoIEV4cGVyaW1lbnRzIiwNCiAgICB0aGVtZSA9IHRoZW1lKA0KICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMC41KSwNCiAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBmYWNlID0gIml0YWxpYyIsIGhqdXN0ID0gMC41KQ0KICAgICkpDQoNCmdnc2F2ZShmaWxlbmFtZSA9ICJPdXRwdXRzL1Jlc3VsdHMvZmlnXzIucG5nIiwgZHYyKQ0KDQpkdjINCg0KYGBgDQpGaWd1cmUgMiBkZW1vbnN0cmF0ZXMgdGhhdCBjb3JyZWN0IHByZWRpY3Rpb25zIHdlcmUgdGhlIG1vc3QgY29tbW9uIG91dGNvbWUgaW4gYm90aCBleHBlcmltZW50cywgYWx0aG91Z2ggYSBzdWJzdGFudGlhbCBwcm9wb3J0aW9uIG9mIGd1ZXNzZXMgd2VyZSBpbmNvcnJlY3QuDQoNCg0KSW5kZXBlbmRlbnQgdmFyaWFibGVzIGluY2x1ZGUgcG9saXRpY2FsIGF0dGVudGlvbiwgMjAxNyBnZW5lcmFsIGVsZWN0aW9uIHZvdGUsIEVVIHJlZmVyZW5kdW0gdm90ZSwgYWdlLCBlZHVjYXRpb24sIGdlbmRlciwgYW5kIHJlZ2lvbjsgdGhlaXIgc3VtbWFyeSBzdGF0aXN0aWNzIGFyZSBkaXNwbGF5ZWQgYmVsb3cgaW4gVGFibGUgMSBhbmQgVGFibGUgMi4gIA0KDQpgYGB7ciBpdl90YWJsZXMsIGVjaG89RkFMU0V9DQojIEdlbmVyYXRlIHRoZSBjcm9zcy10YWJ1bGF0aW9ucw0KDQojIGNsZWFuIHRoZSBOQXMNCmNsZWFuX2JyZXhfdGFiX2RmIDwtIGJyZXhpdF9icmllciAlPiUNCiAgbXV0YXRlKHBhc3Rfdm90ZV8yMDE3ID0gc3RyX3JlcGxhY2VfYWxsKGFzLmNoYXJhY3RlcihwYXN0X3ZvdGVfMjAxNyksICJEb27DouKCrOKEonQga25vdyIsICJEb250IGtub3ciKSkgJT4lDQogIGZpbHRlcighcGFzdF92b3RlXzIwMTcgJWluJSBjKCJOL0EiLCAiTi9BX2R1cGxpY2F0ZWRfOSIpKQ0KDQpjbGVhbl9wYXJ0eV90YWJfZGYgPC0gcGFydGllc19icmllciAlPiUNCiAgbXV0YXRlKHBhc3Rfdm90ZV8yMDE3ID0gc3RyX3JlcGxhY2VfYWxsKGFzLmNoYXJhY3RlcihwYXN0X3ZvdGVfMjAxNyksICJEb27DouKCrOKEonQga25vdyIsICJEb250IGtub3ciKSkgJT4lDQogIGZpbHRlcighcGFzdF92b3RlXzIwMTcgJWluJSBjKCJOL0EiLCAiTi9BX2R1cGxpY2F0ZWRfOSIpKQ0KDQoNCmJyZXhpdF90YWIgIDwtIHRhYmxlKGNsZWFuX2JyZXhfdGFiX2RmJHBhc3Rfdm90ZV8yMDE3LCBjbGVhbl9icmV4X3RhYl9kZiRldV9yZWZfdm90ZSkNCnBhcnRpZXNfdGFiIDwtIHRhYmxlKGNsZWFuX3BhcnR5X3RhYl9kZiRwYXN0X3ZvdGVfMjAxNywgY2xlYW5fcGFydHlfdGFiX2RmJGV1X3JlZl92b3RlKQ0KDQoNCiMgQ29tYmluZSB0aGVtIA0KIyBmb3JtYXQgPSAiaHRtbCIgZW5zdXJlcyB0aGUgSFRNTCBwYWdlIGFsaWducyB0aGVtIGhvcml6b250YWxseQ0KdGFiMSA8LSBrbml0cjo6a2FibGUoDQogIGxpc3QoYnJleGl0X3RhYiwgcGFydGllc190YWIpLA0KICBmb3JtYXQgPSAiaHRtbCIsDQogIGNhcHRpb24gPSAiKipUYWJsZSAxOiBDcm9zcy1UYWIgb2YgUmVzcG9uZGVudCBWb3RlciBIaXN0b3J5IGZvciBCcmV4aXQgKExlZnQpIGFuZCBQYXJ0aWVzIChSaWdodCkgU2FtcGxlcyoqIiwNCiAgY29sLm5hbWVzID0gY29sbmFtZXMoYnJleGl0X3RhYikNCikNCg0Kc2F2ZV9rYWJsZSh0YWIxLCAiT3V0cHV0cy9SZXN1bHRzL3RhYl8xLmh0bWwiKQ0KDQp0YWIxDQoNCmBgYCANCiAgDQpUYWJsZSAxIGRlbW9uc3RyYXRlcyB2YXJpYXRpb24gaW4gcmVzcG9uZGVudHMnIHByZXZpb3VzIHZvdGluZyBiZWhhdmlvdXIgYW5kIHJlZmVyZW5kdW0gY2hvaWNlcywgc2hvd2luZyB0aGUgY29ubmVjdGlvbiBiZXR3ZWVuIHBvbGl0aWNhbCB2b3RpbmcgcGF0dGVybnMgYWNyb3NzIHRoZSBzYW1wbGUuDQoNCmBgYHtyIGVkdWNhdGlvbl9zdW1tYXJ5LCBlY2hvX0ZBTFNFfQ0KIyBCcmV4aXQgc2FtcGxlIGRpc3RyaWJ1dGlvbg0KZWR1X2JyZXggPC0gcHJvcC50YWJsZSh0YWJsZShicmV4aXRfYnJpZXIkZWR1Y2F0aW9uKSkgKiAxMDANCg0KIyBQYXJ0aWVzIHNhbXBsZSBkaXN0cmlidXRpb24NCmVkdV9wYXJ0aWVzIDwtIHByb3AudGFibGUodGFibGUocGFydGllc19icmllciRlZHVjYXRpb24pKSAqIDEwMA0KDQplZHVfYnJleF9zdHIgPC0gcGFzdGUobmFtZXMoZWR1X2JyZXgpLCByb3VuZChlZHVfYnJleCwgMSksICIlIiwgY29sbGFwc2UgPSAiOyAiKQ0KZWR1X3BhcnRpZXNfc3RyIDwtIHBhc3RlKG5hbWVzKGVkdV9wYXJ0aWVzKSwgcm91bmQoZWR1X3BhcnRpZXMsIDEpLCAiJSIsIGNvbGxhcHNlID0gIjsgIikNCg0KdGliYmxlKA0KICBgU2FtcGxlYCA9IGMoIkJyZXhpdCIsICJQYXJ0aWVzIiksDQogIGBFZHVjYXRpb24gKCUpYCA9IGMoZWR1X2JyZXhfc3RyLCBlZHVfcGFydGllc19zdHIpDQopDQoNCg0KYGBgDQoNCg0KYGBge3IgaXZfc3VtbWFyeV90YWJsZSwgZWNobz1GQUxTRX0NCg0KIyBrZXkgYmFja2dyb3VuZCBwcmVkaWN0b3JzDQppdl9zdGF0cyA8LSB0aWJibGUoDQogIGBTYW1wbGVgID0gYygiQnJleGl0IiwgIlBhcnRpZXMiKSwNCiAgYFRvdGFsIChOKWAgPSBjKG5yb3coYnJleGl0X2JyaWVyKSwgbnJvdyhwYXJ0aWVzX2JyaWVyKSksDQogIGBVbmlxdWUgSURzYCA9IGMoDQogICAgbGVuZ3RoKHVuaXF1ZShicmV4aXRfYnJpZXIkcmVzcG9uZGVudF9pZCkpLA0KICAgIGxlbmd0aCh1bmlxdWUocGFydGllc19icmllciRyZXNwb25kZW50X2lkKSkNCiAgKSwNCiAgYE1lYW4gUG9saXRpY2FsIEF0dGVudGlvbiAoMC0xMClgID0gYygNCiAgICBtZWFuKGJyZXhpdF9icmllciRwb2xfYXR0ZW50aW9uLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW4ocGFydGllc19icmllciRwb2xfYXR0ZW50aW9uLCBuYS5ybSA9IFRSVUUpDQogICksDQogIGBNZWFuIEFnZSAoWWVhcnMpYCA9IGMoDQogICAgbWVhbihicmV4aXRfYnJpZXIkYWdlLCBuYS5ybSA9IFRSVUUpLA0KICAgIG1lYW4ocGFydGllc19icmllciRhZ2UsIG5hLnJtID0gVFJVRSkNCiAgKSwNCiAgYEdlbmRlciAoJSBGZW1hbGUpYCA9IGMoDQogICAgbWVhbihicmV4aXRfYnJpZXIkZ2VuZGVyID09ICJGZW1hbGUiLCBuYS5ybSA9IFRSVUUpICogMTAwLA0KICAgIG1lYW4ocGFydGllc19icmllciRnZW5kZXIgPT0gIkZlbWFsZSIsIG5hLnJtID0gVFJVRSkgKiAxMDANCiAgKQ0KKQ0KDQp0YWIyIDwtIGtuaXRyOjprYWJsZShpdl9zdGF0cywgZGlnaXRzID0gMiwgDQogICAgICAgICAgICAgY2FwdGlvbiA9ICIqKlRhYmxlIDI6IEFkZGl0aW9uYWwgUmVzcG9uZGVudCBEZW1vZ3JhcGhpY3MgU3VtbWFyeSBTdGF0cyoqIikNCg0Kc2F2ZV9rYWJsZSh0YWIyLCAiT3V0cHV0cy9SZXN1bHRzL3RhYl8yLmh0bWwiKQ0KDQp0YWIyDQpgYGANCiAgDQpUYWJsZSAyIHN1bW1hcmllcyB0aGUgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzIHVzZWQgYXMgcHJlZGljdG9ycyBpbiB0aGUgbW9kZWxzLg0KDQoNCg0KIyMjIE1ldGhvZHMNCg0KVGl0ZWxtYW4gYW5kIExhdWRlcmRhbGUgKDIwMjFhKSBlbXBsb3kgT3JkaW5hcnkgTGVhc3QgU3F1YXJlcyAoT0xTKSBsaW5lYXIgcmVncmVzc2lvbnMgdG8gZXN0aW1hdGUgd2hldGhlciByZXNwb25kZW50IGF0dHJpYnV0ZXMgcHJlZGljdCBmb3JlY2FzdGluZyBhY2N1cmFjeS4gV2hpbGUgdGhpcyBzdHJhdGVneSBhc3N1bWVzIGluZGVwZW5kZW50IG9ic2VydmF0aW9ucywgYmVjYXVzZSBlYWNoIHJlc3BvbmRlbnQgZXZhbHVhdGVkIG11bHRpcGxlIGRlbW9ncmFwaGljIHByb2ZpbGVzLCBvYnNlcnZhdGlvbnMgbWF5IGJlIG5lc3RlZCB3aXRoaW4gaW5kaXZpZHVhbHMuIFRyZWF0aW5nIHRoZXNlIGV2YWx1YXRpb25zIGFzIGNvbXBsZXRlbHkgaW5kZXBlbmRlbnQgcm93cyB2aW9sYXRlcyB0aGlzIGFzc3VtcHRpb24gYnkgZmFpbGluZyB0byBhY2NvdW50IGZvciByZXNwb25kZW50LWxldmVsIGNoYXJhY3RlcmlzdGljcyB0aGF0IG1heSBpbmZsdWVuY2UgZ3Vlc3NpbmcgYWNjdXJhY3kgYWNyb3NzIGV4cGVyaW1lbnRzLiBJZ25vcmluZyBjbHVzdGVyZWQgZGF0YSBzdHJ1Y3R1cmVzIGNhbiBsZWFkIHRvIGJpYXNlZCBzdGFuZGFyZCBlcnJvcnMgYW5kIG92ZXJjb25maWRlbnQgaW5mZXJlbmNlIChSYXVkZW5idXNoICYgQnJ5aywgMjAwMjsgU25pamRlcnMgJiBCb3NrZXIsIDIwMTIpLiBNdWx0aWxldmVsIG1vZGVscyBhcmUgd2lkZWx5IHJlY29tbWVuZGVkIGluIHN1Y2ggc2V0dGluZ3MgYmVjYXVzZSB0aGV5IGV4cGxpY2l0bHkgbW9kZWwgd2l0aGluLSBhbmQgYmV0d2Vlbi11bml0IHZhcmlhdGlvbiwgcHJvZHVjaW5nIG1vcmUgcmVsaWFibGUgZXN0aW1hdGVzIG9mIHVuY2VydGFpbnR5IChHZWxtYW4gJiBIaWxsLCAyMDA3KS4NCg0KVG8gYWRkcmVzcyB0aGlzIGxpbWl0YXRpb24sIHRoaXMgcmVwbGljYXRpb24gZXh0ZW5kcyB0aGUgb3JpZ2luYWwgYW5hbHlzaXMgdXNpbmcgTGluZWFyIE1peGVkLUVmZmVjdHMgTW9kZWxzLiANCg0KQmVmb3JlIGVzdGltYXRpbmcgdGhlIG11bHRpbGV2ZWwgbW9kZWxzLCB0aGUgcmVwbGljYXRlZCBPTFMgc3BlY2lmaWNhdGlvbnMgd2VyZSByZS1lc3RpbWF0ZWQgdXNpbmcgdGhlIHJlY29uc3RydWN0ZWQgZGF0YXNldHMgZm9yIHZlcmlmaWFiaWxpdHkgd2l0aCB0aGUgb3JpZ2luYWwgYW5hbHl0aWNhbCBmcmFtZXdvcmsgKEZyZWVzZSAmIFBldGVyc29uLCAyMDA3KSAoc2VlIEFwcGVuZGl4IDIsIFRhYmxlIEEyKS4gQWZ0ZXIgbGlzdHdpc2UgZGVsZXRpb24gYW5kIGFwcGxpY2F0aW9uIG9mIHN1cHBsaWVkIHJlc3BvbmRlbnQgd2VpZ2h0cywgb3JpZ2luYWwgYW5hbHlzaXMgb2JzZXJ2YXRpb25zIHJldGFpbiAzLDMwOCBmb3IgdGhlIEJyZXhpdCBleHBlcmltZW50LCBhbmQgMywzOTQgZm9yIHRoZSBQYXJ0eSBleHBlcmltZW50Lg0KDQoNCmBgYHtyIG9yaWdpbmFsX21vZGVsX3NwZWNzLCByZXN1bHRzPSdoaWRlJ30NCiMgcnVuIG9yaWdpbmFsIG1vZGVscyB3aXRoIG5ldyBzdWJzZXR0ZWQgZGF0YXNldHMgZm9yIGNvbXBhcmlzb24NCg0KIyMjIyMjIyMjIyBCcmllciBTY29yZSAjIyMjIyMjIyMjIw0KIyBicmllciBicmV4aXQNCm9yaWdpbmFsX2JyaWVyX2JyZXggPC0gbG0oYnJpZXJfc2NvcmUgfiBwb2xfYXR0ZW50aW9uICsgDQogICAgICAgICAgICAgICAgICAgICAgICBwYXN0X3ZvdGVfMjAxNyArIGV1X3JlZl92b3RlIA0KICAgICAgICAgICAgICAgICAgICAgICsgYWdlICsgZWR1Y2F0aW9uICsgZ2VuZGVyICsgcmVnaW9uLCANCiAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzID0gd2VpZ2h0cywNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gYnJleGl0X2JyaWVyKQ0KDQojIGJyaWVyIHBhcnR5DQpvcmlnaW5hbF9icmllcl9wYXJ0eSA8LSBsbShicmllcl9zY29yZSB+IHBvbF9hdHRlbnRpb24gKyANCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3Rfdm90ZV8yMDE3ICsgZXVfcmVmX3ZvdGUgDQogICAgICAgICAgICAgICAgICAgICAgKyBhZ2UgKyBlZHVjYXRpb24gKyBnZW5kZXIgKyByZWdpb24sIA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSB3ZWlnaHRzLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBwYXJ0aWVzX2JyaWVyKQ0KDQoNCiMjIyMjIyMjIERpY2hvdG9taXNlZCAjIyMjIyMjIyMjIw0KIyBkaWNobyBicmV4aXQNCm9yaWdpbmFsX2RpY2hvX2JyZXggPC0gbG0oZGljaG9fc2NvcmUgfiBwb2xfYXR0ZW50aW9uICsgDQogICAgICAgICAgICAgICAgICAgICAgICBwYXN0X3ZvdGVfMjAxNyArIGV1X3JlZl92b3RlIA0KICAgICAgICAgICAgICAgICAgICAgICsgYWdlICsgZWR1Y2F0aW9uICsgZ2VuZGVyICsgcmVnaW9uLCANCiAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHRzID0gd2VpZ2h0cywNCiAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gYnJleGl0X2RpY2hvKQ0KDQojIGRpY2hvIHBhcnR5DQpvcmlnaW5hbF9kaWNob19wYXJ0eSA8LSBsbShkaWNob19zY29yZSB+IHBvbF9hdHRlbnRpb24gKyANCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3Rfdm90ZV8yMDE3ICsgZXVfcmVmX3ZvdGUgDQogICAgICAgICAgICAgICAgICAgICAgKyBhZ2UgKyBlZHVjYXRpb24gKyBnZW5kZXIgKyByZWdpb24sIA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSB3ZWlnaHRzLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBwYXJ0aWVzX2RpY2hvKQ0KDQpgYGANCg0KDQpgYGB7ciBvcmlnaW5hbF9tb2RfdGFibGUsIGluY2x1ZGU9RkFMU0V9DQojIG9yaWdpbmFsIHRhYmxlIHJlc3VsdHMNCg0KdGFiX21vZGVsKA0KICBvcmlnaW5hbF9icmllcl9icmV4LCBvcmlnaW5hbF9icmllcl9wYXJ0eSwgb3JpZ2luYWxfZGljaG9fYnJleCwgb3JpZ2luYWxfZGljaG9fcGFydHksDQogIGZpbGUgPSAiT3V0cHV0cy9EYXRhQXBwZW5kaXhPdXRwdXRzL3RhYl9BMi5odG1sIiwNCiAgZGlnaXRzID0gMywNCiAgdGl0bGUgPSAiVGFibGUgQTIuIFZlcmlmaWNhdGlvbiBvZiBPcmlnaW5hbCBNb2RlbHMgd2l0aCBOZXcgRGF0YWZyYW1lcyBvZiBSZXNwb25kZW50IENoYXJhY3RlcmlzdGljcyBQcmVkaWN0aW5nIEd1ZXNzIEFjY3VyYWN5IEFjcm9zcyBCcmV4aXQgYW5kIFBhcnR5IEV4cGVyaW1lbnRzLiIsDQogIHByZWQubGFiZWxzID0gYygNCiAgICAiKEludGVyY2VwdCkiID0gIkludGVyY2VwdCIsDQogICAgInBvbF9hdHRlbnRpb24iID0gIlBvbGl0aWNhbCBBdHRlbnRpb24iLA0KICAgICJwYXN0X3ZvdGVfMjAxN0xhYm91ciIgPSAiUGFydHkgVm90ZTogTGFib3VyIiwNCiAgICAicGFzdF92b3RlXzIwMTdTY290dGlzaCBOYXRpb25hbCBQYXJ0eSAoU05QKSIgPSAiUGFydHkgVm90ZTogU05QIiwNCiAgICAicGFzdF92b3RlXzIwMTdMaWJlcmFsIERlbW9jcmF0IiA9ICJQYXJ0eSBWb3RlOiBMaWJlcmFsIERlbW9jcmF0IiwNCiAgICAicGFzdF92b3RlXzIwMTdHcmVlbiIgPSAiUGFydHkgVm90ZTogR3JlZW4iLA0KICAgICJwYXN0X3ZvdGVfMjAxN1BsYWlkIEN5bXJ1IiA9ICJQYXJ0eSBWb3RlOiBQbGFpZCBDeW1ydSIsDQogICAgInBhc3Rfdm90ZV8yMDE3VUsgSW5kZXBlbmRlbmNlIFBhcnR5IChVS0lQKSIgPSAiUGFydHkgVm90ZTogVUtJUCIsDQogICAgInBhc3Rfdm90ZV8yMDE3T3RoZXIiID0gIlBhcnR5IFZvdGU6IE90aGVyIiwNCiAgICAicGFzdF92b3RlXzIwMTdEb27DouKCrOKEonQga25vdyIgPSAiUGFydHkgVm90ZTogRG9uJ3QgS25vdyIsDQogICAgImV1X3JlZl92b3RlTGVhdmUiID0gIkVVIFJlZi4gVm90ZTogTGVhdmUiLA0KICAgICJldV9yZWZfdm90ZURpZCBub3Qgdm90ZSIgPSAiRVUgUmVmLiBWb3RlOiBEaWQgTm90IFZvdGUiLA0KICAgICJhZ2UiID0gIkFnZSIsDQogICAgImVkdWNhdGlvbkxldmVsIDEiID0gIkVkdWNhdGlvbiBMZXZlbDogMSIsDQogICAgImVkdWNhdGlvbkxldmVsIDIiID0gIkVkdWNhdGlvbiBMZXZlbDogMiIsDQogICAgImVkdWNhdGlvbkxldmVsIDMiID0gIkVkdWNhdGlvbiBMZXZlbDogMyIsDQogICAgImVkdWNhdGlvbkxldmVsIDQiID0gIkVkdWNhdGlvbiBMZXZlbDogNCIsDQogICAgImVkdWNhdGlvbkxldmVsIDUgYW5kIGFib3ZlIiA9ICJFZHVjYXRpb24gTGV2ZWw6IDUgYW5kIEFib3ZlIiwNCiAgICAiZWR1Y2F0aW9uT3RoZXIiID0gIkVkdWNhdGlvbiBMZXZlbDogT3RoZXIiLA0KICAgICJnZW5kZXJGZW1hbGUiID0gIkdlbmRlcjogRmVtYWxlIiwNCiAgICAicmVnaW9uTm9ydGggV2VzdCIgPSAiUmVnaW9uOiBOb3J0aCBXZXN0IiwNCiAgICAicmVnaW9uWW9ya3NoaXJlIGFuZCB0aGUgSHVtYmVyIiA9ICJSZWdpb246IFlvcmtzaGlyZSBhbmQgdGhlIEh1bWJlciIsDQogICAgInJlZ2lvbkVhc3QgTWlkbGFuZHMiID0gIlJlZ2lvbjogRWFzdCBNaWRsYW5kcyIsDQogICAgInJlZ2lvbldlc3QgTWlkbGFuZHMiID0gIlJlZ2lvbjogV2VzdCBNaWRsYW5kcyIsDQogICAgInJlZ2lvbkVhc3Qgb2YgRW5nbGFuZCIgPSAiUmVnaW9uOiBFYXN0IG9mIEVuZ2xhbmQiLA0KICAgICJyZWdpb25Mb25kb24iID0gIlJlZ2lvbjogTG9uZG9uIiwNCiAgICAicmVnaW9uU291dGggRWFzdCIgPSAiUmVnaW9uOiBTb3V0aCBFYXN0IiwNCiAgICAicmVnaW9uU291dGggV2VzdCIgPSAiUmVnaW9uOiBTb3V0aCBXZXN0IiwNCiAgICAicmVnaW9uV2FsZXMiID0gIlJlZ2lvbjogV2FsZXMiLA0KICAgICJyZWdpb25TY290bGFuZCIgPSAiUmVnaW9uOiBTY290bGFuZCINCiAgICApLA0KICBkdi5sYWJlbHMgPSBjKA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5CcmllciBTY29yZTwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+QnJleGl0IEV4cC48L2Rpdj4iLA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5CcmllciBTY29yZTwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+UGFydHkgRXhwLjwvZGl2PiIsIA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5Db3JyZWN0IERpY2hvdG9taXNlZCBHdWVzczwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+QnJleGl0IEV4cC48L2Rpdj4iLCANCiAgICAiPGRpdiBzdHlsZT0ndGV4dC1hbGlnbjogY2VudGVyOyc+PGI+Q29ycmVjdCBEaWNob3RvbWlzZWQgR3Vlc3M8L2I+PGhyIHN0eWxlPSdib3JkZXI6IG5vbmU7IGJvcmRlci10b3A6IDFweCBzb2xpZCBibGFjazsgbWFyZ2luOiA0cHggMDsnPlBhcnR5IEV4cC48L2Rpdj4iDQogICksDQogIHN0cmluZy5wcmVkID0gIkNvZWZmaWNpZW50IiwNCiAgc2hvdy5jaSA9IEZBTFNFLA0KICBzaG93LnNlID0gVFJVRSwNCiAgc3RyaW5nLnNlID0gIlN0YW5kYXJkIEVycm9yIiwNCiAgcC5zdHlsZSA9ICJzdGFycyIsDQogIENTUyA9IGxpc3QoDQogICAgY3NzLmNvbGhlYWRlcnMgPSAidGV4dC1hbGlnbjogY2VudGVyOyB2ZXJ0aWNhbC1hbGlnbjogYm90dG9tOyINCiAgKQ0KKQ0KDQoNCmBgYA0KDQoNCkEgcmFuZG9tIGludGVyY2VwdCB3YXMgaW50cm9kdWNlZCBmb3IgcmVzcG9uZGVudCBJRCAoMSB8IHJlc3BvbmRlbnRfaWQpLCBtb2RlbGxpbmcgaW5kaXZpZHVhbC1sZXZlbCBoZXRlcm9nZW5laXR5IGFuZCBzZXBhcmF0ZXMgYmV0d2Vlbi1yZXNwb25kZW50IGZyb20gd2l0aGluLXJlc3BvbmRlbnQgdmFyaWF0aW9uLiBSZXNwb25kZW50IGlkZW50aWZpZXJzIHdlcmUgcmV0YWluZWQgZnJvbSB0aGUgb3JpZ2luYWwgZGF0YSBhbmQgY29udmVydGVkIGludG8gYSBmYWN0b3IgdmFyaWFibGUgdG8gZW5hYmxlIHRoZSBtdWx0aWxldmVsIHNwZWNpZmljYXRpb24uIEludmVzdGlnYXRpb24gb2YgcmVzcG9uZGVudF9pZCB2YXJpYWJsZXMgc2hvdyAxLDY5NCBpbmRpdmlkdWFsIHJlc3BvbmRlbnRzIGluIHRoZSBCcmV4aXQgZXhwZXJpbWVudCwgYW5kIDEsNjg4IGluIHRoZSBwYXJ0eSBleHBlcmltZW50LiBGb3VyIG11bHRpbGV2ZWwgbW9kZWxzIHdlcmUgc3Vic2VxdWVudGx5IHJ1bi4NCg0KYGBge3IgbmVzdHMsIGluY2x1ZGU9RkFMU0V9DQojIGNoZWNrIGxlbmd0aCBvZiB1bmlxdWUgcmVzcG9uZGVudF9pZCANCmxlbmd0aCh1bmlxdWUoYnJleGl0X2JyaWVyJHJlc3BvbmRlbnRfaWQpKQ0KbGVuZ3RoKHVuaXF1ZShicmV4aXRfZGljaG8kcmVzcG9uZGVudF9pZCkpDQoNCmxlbmd0aCh1bmlxdWUocGFydGllc19kaWNobyRyZXNwb25kZW50X2lkKSkNCmxlbmd0aCh1bmlxdWUocGFydGllc19icmllciRyZXNwb25kZW50X2lkKSkNCmBgYA0KDQpgYGAge3IgbXVsdGlfbGV2ZWxfbW9kZWxzLCByZXN1bHRzPSdoaWRlJ30NCiMgUnVuIG11bHRpbGV2ZWwgbW9kZWwgd2l0aCByZXNwb25kZW50IElEJ3MgYXMgbmVzdGVkIHZhcmlhYmxlIA0KDQojIyMjIyMgQnJpZXIgIyMjIyMjIw0KIyBtaXhlZCBicmllciBicmV4aXQgbW9kZWwNCm1peGVkX2JyaWVyX2JyZXggPC0gbG1lcihicmllcl9zY29yZSB+IHBvbF9hdHRlbnRpb24gKyANCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3Rfdm90ZV8yMDE3ICsgZXVfcmVmX3ZvdGUgDQogICAgICAgICAgICAgICAgICAgICAgKyBhZ2UgKyBlZHVjYXRpb24gKyBnZW5kZXIgKyByZWdpb24gKw0KICAgICAgICAgICAgICAgICAgICAgICAgKDEgfCByZXNwb25kZW50X2lkKSwgDQogICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cyA9IHdlaWdodHMsDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGJyZXhpdF9icmllcikNCg0KIyBtaXhlZCBicmllciBwYXJ0eSBtb2RlbA0KbWl4ZWRfYnJpZXJfcGFydHkgPC0gbG1lcihicmllcl9zY29yZSB+IHBvbF9hdHRlbnRpb24gKyANCiAgICAgICAgICAgICAgICAgICAgICAgIHBhc3Rfdm90ZV8yMDE3ICsgZXVfcmVmX3ZvdGUgDQogICAgICAgICAgICAgICAgICAgICAgKyBhZ2UgKyBlZHVjYXRpb24gKyBnZW5kZXIgKyByZWdpb24gKw0KICAgICAgICAgICAgICAgICAgICAgICAgKDEgfCByZXNwb25kZW50X2lkKSwgDQogICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0cyA9IHdlaWdodHMsDQogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IHBhcnRpZXNfYnJpZXIpDQoNCiMjIyMjIyBkaWNobyAjIyMjIyMNCiMgZGljaG8gYnJleGl0DQptaXhlZF9kaWNob19icmV4IDwtIGxtZXIoZGljaG9fc2NvcmUgfiBwb2xfYXR0ZW50aW9uICsgDQogICAgICAgICAgICAgICAgICAgICAgICBwYXN0X3ZvdGVfMjAxNyArIGV1X3JlZl92b3RlIA0KICAgICAgICAgICAgICAgICAgICAgICsgYWdlICsgZWR1Y2F0aW9uICsgZ2VuZGVyICsgcmVnaW9uICsNCiAgICAgICAgICAgICAgICAgICAgICAgICgxIHwgcmVzcG9uZGVudF9pZCksIA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSB3ZWlnaHRzLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBicmV4aXRfZGljaG8pDQoNCiMgZGljaG8gcGFydHkNCm1peGVkX2RpY2hvX3BhcnR5IDwtIGxtZXIoZGljaG9fc2NvcmUgfiBwb2xfYXR0ZW50aW9uICsgDQogICAgICAgICAgICAgICAgICAgICAgICBwYXN0X3ZvdGVfMjAxNyArIGV1X3JlZl92b3RlIA0KICAgICAgICAgICAgICAgICAgICAgICsgYWdlICsgZWR1Y2F0aW9uICsgZ2VuZGVyICsgcmVnaW9uICsNCiAgICAgICAgICAgICAgICAgICAgICAgICgxIHwgcmVzcG9uZGVudF9pZCksIA0KICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSB3ZWlnaHRzLA0KICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBwYXJ0aWVzX2RpY2hvKQ0KDQpgYGANCg0KYGBge3IgbW9kZWxfY29tcGFyaXNvbiwgaW5jbHVkZT1GQUxTRX0NCiMgc3VtbWFyeSBjb29tcGFyaXNvbiBvZiBvcmlnaW5hbCBhbmQgbmV3IG11bHRpbGV2ZWwgc3BlY3MNCg0KIyBicmllciBicmV4aXQNCnN1bW1hcnkob3JpZ2luYWxfYnJpZXJfYnJleCkNCnN1bW1hcnkobWl4ZWRfYnJpZXJfYnJleCkNCg0KIyBicmllciBwYXJ0eSANCnN1bW1hcnkob3JpZ2luYWxfYnJpZXJfcGFydHkpDQpzdW1tYXJ5KG1peGVkX2JyaWVyX3BhcnR5KQ0KDQojIGRpY2hvIGJyZXhpdA0Kc3VtbWFyeShvcmlnaW5hbF9kaWNob19icmV4KQ0Kc3VtbWFyeShtaXhlZF9kaWNob19icmV4KQ0KDQojIGRpY2hvIHBhcnR5DQpzdW1tYXJ5KG9yaWdpbmFsX2RpY2hvX3BhcnR5KQ0Kc3VtbWFyeShtaXhlZF9kaWNob19wYXJ0eSkNCg0KYGBgDQoNCg0KTW9kZWwgZml0IHdhcyBhc3Nlc3NlZCB1c2luZyBBSUMgZGlmZmVyZW5jZXMgYW5kIGxpa2VsaWhvb2QgcmF0aW8gdGVzdHMgY29tcGFyaW5nIG1vZGVscyB3aXRoIGFuZCB3aXRob3V0IHJlc3BvbmRlbnQtbGV2ZWwgcmFuZG9tIGVmZmVjdHMgKEZpbmNoLCAyMDI2KS4gTXVsdGlsZXZlbCBhcHByb2FjaGVzIHdlcmUgYXBwcm9wcmlhdGUgZm9yIGNsdXN0ZXJlZCBleHBlcmltZW50YWwgZGF0YSBiZWNhdXNlIHRoZXkgcHJvdmlkZSBtb3JlIGFjY3VyYXRlIHVuY2VydGFpbnR5IGVzdGltYXRlcyBhbmQgcmVkdWNlIHRoZSByaXNrIG9mIG1pc2xlYWRpbmcgaW5mZXJlbmNlIGFyaXNpbmcgZnJvbSB1bmRlcmVzdGltYXRlZCBzdGFuZGFyZCBlcnJvcnMuIA0KDQpgYGB7ciBtb2RlbF9maXRfdGVzdHMsIHJlc3VsdHM9J2hpZGUnfQ0KIyBjb21wYXJpbmcgT0xTIG1vZGVsIGZpdCBhZ2FpbnN0IE11bHRpbGV2ZWwgbW9kZWwgZml0DQojIEJyaWVyIFNjb3JlIG1vZGVscw0KZml0X3Rlc3RfYnJleGl0X2JyaWVyIDwtIGFub3ZhKG1peGVkX2JyaWVyX2JyZXgsIG9yaWdpbmFsX2JyaWVyX2JyZXgpDQpmaXRfdGVzdF9wYXJ0eV9icmllciAgPC0gYW5vdmEobWl4ZWRfYnJpZXJfcGFydHksIG9yaWdpbmFsX2JyaWVyX3BhcnR5KQ0KDQojIERpY2hvIG1vZGVscw0KZml0X3Rlc3RfYnJleGl0X2RpY2hvIDwtIGFub3ZhKG1peGVkX2RpY2hvX2JyZXgsIG9yaWdpbmFsX2RpY2hvX2JyZXgpDQpmaXRfdGVzdF9wYXJ0eV9kaWNobyA8LSBhbm92YShtaXhlZF9kaWNob19wYXJ0eSwgb3JpZ2luYWxfZGljaG9fcGFydHkpDQoNCmBgYA0KDQpgYGB7ciBmaXRfdGVzdF9yZXN1bHRzLCBlY2hvPUZBTFNFfQ0KIyBEaXNwbGF5IHJlc3VsdHMNCnByaW50KGZpdF90ZXN0X2JyZXhpdF9icmllcikNCnByaW50KGZpdF90ZXN0X3BhcnR5X2JyaWVyKQ0KcHJpbnQoZml0X3Rlc3RfYnJleGl0X2RpY2hvKQ0KcHJpbnQoZml0X3Rlc3RfcGFydHlfZGljaG8pDQpgYGANCg0KDQojIyAqKlJlc3VsdHMqKiB7LX0NCg0KVGhlIG11bHRpbGV2ZWwgbW9kZWwgcmV2ZWFscyB0aGF0IGZvcmVjYXN0aW5nIGFjY3VyYWN5IGlzIHBhcnRseSBhbiBpbmRpdmlkdWFsLWxldmVsIHRyYWl0LiBJbmNvcnBvcmF0aW5nIGEgcmFuZG9tIGludGVyY2VwdCBmb3IgaW5kaXZpZHVhbCByZXNwb25kZW50cyByZXZlYWxzIHRoYXQgc2V2ZXJhbCBkZW1vZ3JhcGhpYyBhc3NvY2lhdGlvbnMgd2VyZSBsZXNzIHJvYnVzdCBvbmNlIHJlc3BvbmRlbnQtbGV2ZWwgY2x1c3RlcmluZyB3YXMgYWNjb3VudGVkIGZvci4gV2hlbiBpbnRlcnByZXRpbmcgVGFibGUgNCByZXN1bHRzLCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGEgbmVnYXRpdmUgYnJpZXIgY29lZmZpY2llbnQgYW5kIGEgcG9zaXRpdmUgZGljaG90b21pc2VkIGNvZWZmaWNpZW50IHJlcHJlc2VudCBpbXByb3ZlZCByZXNwb25kZW50IGFjY3VyYWN5Lg0KDQpgYGB7ciBtb2RlbF9maXRfY29tcGFyaXNvbl90YWJsZSwgZWNobz1GQUxTRX0NCiMgc3VtbWFyeSBvZiBtb2RlbCBmaXQgdGVzdHMgd2l0aCBwbHVnZ2VkIGluIHZhbHVlcyBmcm9tIHByaW50IG91dHB1dCBhYm92ZQ0KdGVzdF9zdW1tYXJ5X3RhYmxlIDwtIHRpYmJsZSgNCiAgYE1vZGVsIENvbnRleHQgJiBDb21wYXJpc29uYCA9IGMoDQogICAgIkJyZXhpdCBFeHBlcmltZW50OiBCcmllciBTY29yZSIsDQogICAgIlBhcnRpZXMgRXhwZXJpbWVudDogQnJpZXIgU2NvcmUiLA0KICAgICJCcmV4aXQgRXhwZXJpbWVudDogRGljaG90b21pc2VkIFNjb3JlIiwNCiAgICAiUGFydGllcyBFeHBlcmltZW50OiBEaWNob3RvbWlzZWQgU2NvcmUiDQogICksDQogIGBPTFMgQUlDYCA9IGMoMTY0Ni41LCAxNDgwLjgsIDQ4MDIuMiwgNTA0OS44KSwNCiAgYE11bHRpbGV2ZWwgQUlDYCA9IGMoMTU5Ny4zLCAxNDE5LjMsIDQ3ODEuOCwgNTAzOS4yKSwNCiAgYENoaS1TcXVhcmVgID0gYyg1MS4xNTksIDYzLjQ5OCwgMjIuMzIzLCAxMi42MzgpLA0KICBgU2lnbmlmaWNhbmNlIChwLXZhbHVlKWAgPSBjKCIqKioiLCAiKioqIiwgIioqKiIsICIqKioiKQ0KKQ0KDQojIFByaW50IGNvbnNvbGlkYXRlZCB0YWJsZQ0KdGFiMyA8LSBrbml0cjo6a2FibGUodGVzdF9zdW1tYXJ5X3RhYmxlLCBkaWdpdHMgPSAzLCANCiAgICAgICAgICAgICBjYXB0aW9uID0gIioqVGFibGUgMzogVGVzdCBGaXQgQ29tcGFyaXNvbnMgYWNyb3NzIE1vZGVsaW5nIFN0cnVjdHVyZXMqKiIpDQoNCnNhdmVfa2FibGUodGFiMywgIk91dHB1dHMvUmVzdWx0cy90YWJfMy5odG1sIikNCg0KdGFiMyANCmBgYA0KDQoNCmBgYHtyIG1sbV9tb2RlbF90YWJzLCBlY2hvPUZBTFNFfQ0KIyBzdW1tYXJ5IHRhYmxlIG9mIG5ldyBtdWx0aSBsZXZlbCBtb2RlbHMNCg0KdGFiX21vZGVsKA0KICBtaXhlZF9icmllcl9icmV4LCBtaXhlZF9icmllcl9wYXJ0eSwgbWl4ZWRfZGljaG9fYnJleCwgbWl4ZWRfZGljaG9fcGFydHksDQogIGZpbGUgPSAiT3V0cHV0cy9SZXN1bHRzL3RhYl80Lmh0bWwiLA0KICBkaWdpdHMgPSAzLA0KICB0aXRsZSA9ICJUYWJsZSA0LiBNdWx0aS1sZXZlbCBQcmVkaWN0b3JzIG9mIFJlc3BvbmRlbnQgQ2hhcmFjdGVyaXN0aWNzIFByZWRpY3RpbmcgR3Vlc3MgQWNjdXJhY3kgKEJyaWVyIFNjb3JlIGFuZCBDb3JyZWN0IERpY2hvdG9taXNlZCBHdWVzcykgQWNyb3NzIEJyZXhpdCBhbmQgUGFydHkgRXhwZXJpbWVudHMuIiwNCiAgcHJlZC5sYWJlbHMgPSBjKA0KICAgICIoSW50ZXJjZXB0KSIgPSAiSW50ZXJjZXB0IiwNCiAgICAicG9sX2F0dGVudGlvbiIgPSAiUG9saXRpY2FsIEF0dGVudGlvbiIsDQogICAgInBhc3Rfdm90ZV8yMDE3TGFib3VyIiA9ICJQYXJ0eSBWb3RlOiBMYWJvdXIiLA0KICAgICJwYXN0X3ZvdGVfMjAxN1Njb3R0aXNoIE5hdGlvbmFsIFBhcnR5IChTTlApIiA9ICJQYXJ0eSBWb3RlOiBTTlAiLA0KICAgICJwYXN0X3ZvdGVfMjAxN0xpYmVyYWwgRGVtb2NyYXQiID0gIlBhcnR5IFZvdGU6IExpYmVyYWwgRGVtb2NyYXQiLA0KICAgICJwYXN0X3ZvdGVfMjAxN0dyZWVuIiA9ICJQYXJ0eSBWb3RlOiBHcmVlbiIsDQogICAgInBhc3Rfdm90ZV8yMDE3UGxhaWQgQ3ltcnUiID0gIlBhcnR5IFZvdGU6IFBsYWlkIEN5bXJ1IiwNCiAgICAicGFzdF92b3RlXzIwMTdVSyBJbmRlcGVuZGVuY2UgUGFydHkgKFVLSVApIiA9ICJQYXJ0eSBWb3RlOiBVS0lQIiwNCiAgICAicGFzdF92b3RlXzIwMTdPdGhlciIgPSAiUGFydHkgVm90ZTogT3RoZXIiLA0KICAgICJwYXN0X3ZvdGVfMjAxN0RvbsOi4oKs4oSidCBrbm93IiA9ICJQYXJ0eSBWb3RlOiBEb24ndCBLbm93IiwNCiAgICAiZXVfcmVmX3ZvdGVMZWF2ZSIgPSAiRVUgUmVmLiBWb3RlOiBMZWF2ZSIsDQogICAgImV1X3JlZl92b3RlRGlkIG5vdCB2b3RlIiA9ICJFVSBSZWYuIFZvdGU6IERpZCBOb3QgVm90ZSIsDQogICAgImFnZSIgPSAiQWdlIiwNCiAgICAiZWR1Y2F0aW9uTGV2ZWwgMSIgPSAiRWR1Y2F0aW9uIExldmVsOiAxIiwNCiAgICAiZWR1Y2F0aW9uTGV2ZWwgMiIgPSAiRWR1Y2F0aW9uIExldmVsOiAyIiwNCiAgICAiZWR1Y2F0aW9uTGV2ZWwgMyIgPSAiRWR1Y2F0aW9uIExldmVsOiAzIiwNCiAgICAiZWR1Y2F0aW9uTGV2ZWwgNCIgPSAiRWR1Y2F0aW9uIExldmVsOiA0IiwNCiAgICAiZWR1Y2F0aW9uTGV2ZWwgNSBhbmQgYWJvdmUiID0gIkVkdWNhdGlvbiBMZXZlbDogNSBhbmQgQWJvdmUiLA0KICAgICJlZHVjYXRpb25PdGhlciIgPSAiRWR1Y2F0aW9uIExldmVsOiBPdGhlciIsDQogICAgImdlbmRlckZlbWFsZSIgPSAiR2VuZGVyOiBGZW1hbGUiLA0KICAgICJyZWdpb25Ob3J0aCBXZXN0IiA9ICJSZWdpb246IE5vcnRoIFdlc3QiLA0KICAgICJyZWdpb25Zb3Jrc2hpcmUgYW5kIHRoZSBIdW1iZXIiID0gIlJlZ2lvbjogWW9ya3NoaXJlIGFuZCB0aGUgSHVtYmVyIiwNCiAgICAicmVnaW9uRWFzdCBNaWRsYW5kcyIgPSAiUmVnaW9uOiBFYXN0IE1pZGxhbmRzIiwNCiAgICAicmVnaW9uV2VzdCBNaWRsYW5kcyIgPSAiUmVnaW9uOiBXZXN0IE1pZGxhbmRzIiwNCiAgICAicmVnaW9uRWFzdCBvZiBFbmdsYW5kIiA9ICJSZWdpb246IEVhc3Qgb2YgRW5nbGFuZCIsDQogICAgInJlZ2lvbkxvbmRvbiIgPSAiUmVnaW9uOiBMb25kb24iLA0KICAgICJyZWdpb25Tb3V0aCBFYXN0IiA9ICJSZWdpb246IFNvdXRoIEVhc3QiLA0KICAgICJyZWdpb25Tb3V0aCBXZXN0IiA9ICJSZWdpb246IFNvdXRoIFdlc3QiLA0KICAgICJyZWdpb25XYWxlcyIgPSAiUmVnaW9uOiBXYWxlcyIsDQogICAgInJlZ2lvblNjb3RsYW5kIiA9ICJSZWdpb246IFNjb3RsYW5kIg0KICAgICksDQogIGR2LmxhYmVscyA9IGMoDQogICAgIjxkaXYgc3R5bGU9J3RleHQtYWxpZ246IGNlbnRlcjsnPjxiPkJyaWVyIFNjb3JlPC9iPjxociBzdHlsZT0nYm9yZGVyOiBub25lOyBib3JkZXItdG9wOiAxcHggc29saWQgYmxhY2s7IG1hcmdpbjogNHB4IDA7Jz5CcmV4aXQgRXhwLjwvZGl2PiIsDQogICAgIjxkaXYgc3R5bGU9J3RleHQtYWxpZ246IGNlbnRlcjsnPjxiPkJyaWVyIFNjb3JlPC9iPjxociBzdHlsZT0nYm9yZGVyOiBub25lOyBib3JkZXItdG9wOiAxcHggc29saWQgYmxhY2s7IG1hcmdpbjogNHB4IDA7Jz5QYXJ0eSBFeHAuPC9kaXY+IiwgDQogICAgIjxkaXYgc3R5bGU9J3RleHQtYWxpZ246IGNlbnRlcjsnPjxiPkNvcnJlY3QgRGljaG90b21pc2VkIEd1ZXNzPC9iPjxociBzdHlsZT0nYm9yZGVyOiBub25lOyBib3JkZXItdG9wOiAxcHggc29saWQgYmxhY2s7IG1hcmdpbjogNHB4IDA7Jz5CcmV4aXQgRXhwLjwvZGl2PiIsIA0KICAgICI8ZGl2IHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48Yj5Db3JyZWN0IERpY2hvdG9taXNlZCBHdWVzczwvYj48aHIgc3R5bGU9J2JvcmRlcjogbm9uZTsgYm9yZGVyLXRvcDogMXB4IHNvbGlkIGJsYWNrOyBtYXJnaW46IDRweCAwOyc+UGFydHkgRXhwLjwvZGl2PiINCiAgKSwNCiAgc3RyaW5nLnByZWQgPSAiQ29lZmZpY2llbnQiLA0KICBzaG93LmNpID0gRkFMU0UsDQogIHNob3cuc2UgPSBUUlVFLA0KICBzdHJpbmcuc2UgPSAiU3RhbmRhcmQgRXJyb3IiLA0KICBwLnN0eWxlID0gInN0YXJzIiwNCiAgc2hvdy5pY2MgPSBUUlVFLA0KICBDU1MgPSBsaXN0KA0KICAgIGNzcy5jb2xoZWFkZXJzID0gInRleHQtYWxpZ246IGNlbnRlcjsgdmVydGljYWwtYWxpZ246IGJvdHRvbTsiLA0KICAgIGNzcy5yYW5kb21wYXJ0cyA9ICJmb250LXN0eWxlOiBub3JtYWw7IGZvbnQtd2VpZ2h0OiBib2xkOyBjb2xvcjogIzJjM2U1MDsiDQogICkNCikNCg0KYGBgDQoNCg0KVGhlIHJhbmRvbSBlZmZlY3QgZXN0aW1hdGVzIGluZGljYXRlIGNsdXN0ZXJpbmcgaW4gdGhlIGRhdGEsIGFzIHJlZmxlY3RlZCBpbiB0aGUgaW50ZXJjbGFzcyBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCAoSUNDKSwgc2hvd2luZyB0aGF0IGJldHdlZW4gNiUgYW5kIDEyJSBvZiB0b3RhbCB2YXJpYW5jZSBpcyBhdHRyaWJ1dGVkIHRvIHJlc3BvbmRlbnQtbGV2ZWwgY2x1c3RlcmluZyAoVGFibGUgNCkuIFRoaXMgdmFyaWFuY2UgaW5kaWNhdGVzIHRoYXQgdHJlYXRpbmcgcmVwZWF0ZWQgcmVzcG9uZGVudCBndWVzc2VzIGFzIGluZGVwZW5kZW50IG9ic2VydmF0aW9ucyB2aW9sYXRlcyBPTFMgYXNzdW1wdGlvbnMuIFRoZSBjb3JyZWN0IGRpY2hvdG9taXNlZCBndWVzcyB2YXJpYW5jZSAoNi04JSkgaXMsIGhvd2V2ZXIsIHNtYWxsZXIgdGhhbiB0aGUgcmFuZG9tIGVmZmVjdHMgZm9yIHJlc3BvbmRlbnRzIEJyaWVyIHNjb3JlcyAoMTEtMTIlKS4gVGhpcyBpcyBzdWJzdGFudGl2ZWx5IGltcG9ydGFudCB3aGVuIGNvbnNpZGVyaW5nIFRpdGVhbG1hbiBhbmQgTGF1ZGVyZGFsZSdzICgyMDIxYSwgcC4gMjcxKSBjbGFpbXMgb24gIndpc2RvbSBvZiBjcm93ZHMiIHZlcnN1cyBpbmRpdmlkdWFsIGluYWNjdXJhY3ksIHdoZXJlIHJlc3BvbmRlbnRzIG9uIGFnZ3JlZ2F0ZSBhcmUgbW9yZSBhY2N1cmF0ZSB0aGF0IGluZGl2aWR1YWwgYnJpZXIgYWNjdXJhY3kgc2NvcmVzLiBUaGUgdmFyaWFuY2Ugc3VnZ2VzdHMgdGhhdCwgZXZlbiB3aGVuIGFjY291bnRpbmcgZm9yIHVub2JzZXJ2ZWQgaW5kaXZpZHVhbCBkaWZmZXJlbmNlcywgdGhpcyBjcm93ZCB3aXNkb20gYXJndW1lbnQgaXMgbGVzcyBhZmZlY3RlZCBieSByYW5kb20gZWZmZWN0cy4gV2hlbiBjb25zaWRlcmluZyBpbmRpdmlkdWFsIHJlc3BvbnNlcywgZm9yZWNhc3RpbmcgYWJpbGl0eSBjYW4gbm90IGJlIGVudGlyZWx5IGV4cGxhaW5lZCBieSBvYnNlcnZhYmxlIGNoYXJhY3RlcmlzdGljcy4gU29tZSByZXNwb25kZW50cyBhcHBlYXIgdG8gaGF2ZSBhIGdlbmVyYWwgdGVuZGVuY3kgdG8gbWFrZSBtb3JlIGFjY3VyYXRlIHByZWRpY3Rpb25zIGFjcm9zcyB2b3Rpbmcgc2NlbmFyaW9zLCBwb3RlbnRpYWxseSByZWZsZWN0aW5nIHVubWVhc3VyZWQgY2hhcmFjdGVyaXN0aWNzLiANCg0KVGFibGUgMyBhbHNvIHNob3dzIGFuIGltcHJvdmVkIGZpdCwgd2l0aCBBSUMgcmVkdWN0aW9ucyByYW5naW5nIGZyb20gMTAuNiAtIDYzLjUgcG9pbnRzIGFuZCBzaWduaWZpY2FudCBsaWtlbGlob29kIHJhdGlvIHRlc3RzIChwIDwgLjAwMSkgYWNyb3NzIGFsbCBmb3VyIG11bHRpbGV2ZWwgc3BlY2lmaWNhdGlvbnMuICANCg0KQ29tcGFyaW5nIHRvIHRoZSBvcmlnaW5hbCBPTFMgZnJhbWV3b3JrIChzZWUgQXBwZW5kaXgsIFRhYmxlIEEyKSwgcG9saXRpY2FsIGF0dGVudGlvbiBpcyBzaG93biB0byBiZSBhIGhpZ2hseSByb2J1c3QgcHJlZGljdG9yIG9mIGZvcmVjYXN0aW5nIGFiaWxpdHksIGFjaGlldmluZyBoaWdoIHNpZ25pZmljYW5jZSBhY3Jvc3MgYm90aCBleHBlcmltZW50cy4gVW5kZXIgdGhlIG11bHRpbGV2ZWwgc3BlY2lmaWNhdGlvbiBob3dldmVyLCBjb2VmZmljaWVudHMgcmVkdWNlIGFjcm9zcyBhbGwgZm9yIG1vZGVscywgaW5jbHVkaW5nIGEgZ2xvYmFsIHJlZHVjdGlvbiBpbiBzaWduaWZpY2FuY2UgKFRhYmxlIDQpLiBUaG91Z2ggc3RyZW5ndGggYW5kIHNpZ25pZmljYW5jZSBkZWNyZWFzZXMsIHRoZSBnZW5lcmFsIHN0cmVuZ3RoIGFuZCB0cmVuZCByZW1haW5zLCBmaW5kaW5nIHRoZSBwYXJ0aWVzIGV4cGVyaW1lbnQgY29udGludWluZyB0byB5aWVsZCB0aGUgbW9zdCBhY2N1cmF0ZSBhbmQgc2lnbmlmaWNhbnQgcmVzdWx0cy4gVGhpcyBzdWdnZXN0cyB0aGF0LCB3aGlsZSBwb2xpdGljYWxseSBhdHRlbnRpdmUgY2l0aXplbnMgZG8gYXBwZWFyIHRvIGhhdmUgYW4gYWR2YW50YWdlIGluIGd1ZXNzaW5nIG90aGVyc+KAmSB2b3RpbmcgYmVoYXZpb3VyLCBlc3BlY2lhbGx5IGZvciBpbmZlcmluZyBwYXJ0aXNhbiBhbGlnbm1lbnQsIHdoZW4gYWNjb3VudGluZyBmb3IgbmVzdGVkIGRhdGEsIHRoaXMgYWR2YW50YWdlIG1heSBiZSBzbWFsbGVyIHRoYW4gVGl0ZWxtYW4gJiBMYXVkZXJkYWxlICgyMDIxYSkgaW5pdGlhbGx5IGVzdGltYXRlZC4gV2hpbGUgcG9saXRpY2FsIGVuZ2FnZW1lbnQgbGlrZWx5IGltcHJvdmVzIGV4cG9zdXJlIHRvIHBvbGl0aWNhbCBpbmZvcm1hdGlvbiBhbmQgYXdhcmVuZXNzIG9mIGVsZWN0b3JhbCBwYXR0ZXJucywgcGFydCBvZiB0aGlzIGFwcGFyZW50IHJlbGF0aW9uc2hpcCBtYXkgcmVmbGVjdCBicm9hZGVyIGluZGl2aWR1YWwgZGlmZmVyZW5jZXMgYW1vbmcgaGlnaGx5IGVuZ2FnZWQgcmVzcG9uZGVudHMuDQoNCkNvbmN1cnJlbnRseSwgZWR1Y2F0aW9uIGNvZWZmaWNpZW50cyByZW1haW5lZCBkaXJlY3Rpb25hbGx5IGNvbnNpc3RlbnQgYnV0IHNldmVyYWwgY2F0ZWdvcmllcyBzaG93ZWQgcmVkdWNlZCBzdGF0aXN0aWNhbCBjZXJ0YWludHkgKFRhYmxlIDQpLiBJbiB0aGUgQnJleGl0IE9MUyBiYXNlbGluZSwgaGlnaGVyIHRpZXJzIG9mIGVkdWNhdGlvbiBvcGVyYXRlIGFzIGhpZ2hseSBzaWduaWZpY2FudCBwcmVkaWN0b3JzIG9mIGFjY3VyYWN5LCBjb25zaXN0ZW50bHkgYWNoaWV2aW5nIGhpZ2ggc2lnbmlmaWNhbmNlIChwIDwgMC4wMDEpIChBcHBlbmRpeCBUYWJsZSBBMikuIFVuZGVyIHRoZSBtdWx0aWxldmVsIGVzdGltYXRpb24gKFRhYmxlIDQpLCBob3dldmVyLCBlZHVjYXRpb24gY29lZmZpY2llbnRzIHNob3cgYSByZWR1Y3Rpb24gaW4gcmVzcG9uZGVudCBhY2N1cmFjeSwgZmFsbGluZyBvbmNlIGhpZ2ggc2lnbmlmaWNhbmNlIHRocmVzaG9sZHMgKHAgPCAwLjAwMSkuIFNpbWlsYXIgdG8gdGhlIGNvbXBhcmF0aXZlIGZpbmRpbmdzIG9uIHBvbGl0aWNhbCBhdHRlbnRpb24sIHRoZSB0cmVuZCByZW1haW5zIHRoZSBzYW1lIGRlc3BpdGUgbG9zaW5nIHBvd2VyIGFuZCBzaWduaWZpY2FuY2UuIFdoaWxlIGhpZ2hlciBlZHVjYXRpb25hbCBhdHRhaW5tZW50IG1heSBwcm92aWRlIHJlc291cmNlcyB0aGF0IGZhY2lsaXRhdGUgdW5kZXJzdGFuZGluZyBwb2xpdGljYWwgYmVoYXZpb3VyLCB0aGlzIHN1Z2dlc3QgdGhhdCBlZHVjYXRpb24gYWxvbmUgZG9lcyBub3QgYXBwZWFyIHRvIGZ1bGx5IGV4cGxhaW4gd2h5IHNvbWUgY2l0aXplbnMgYXJlIGJldHRlciBhdCBwcmVkaWN0aW5nIG90aGVyIHZvdGVycy4gDQoNCkNvbnZlcnNlbHkgdG8gcmVzcG9uZGVudHMgbGV2ZWwgb2YgcG9saXRpY2FsIGF0dGVudGlvbiBtb3N0IGltcHJvdmluZyBpbiB0aGUgcGFydGllcyBleHBlcmltZW50LCBoaWdoZXIgZWR1Y2F0aW9uIGFwcGVhcnMgdG8gaW1wcm92ZSB0aGUgYWNjdXJhY3kgb2YgcmVzcG9uZGVudHMgZ3Vlc3NlcyBmb3IgQnJleGl0IHZvdGluZy4gVGhpcyBjb25zaXN0ZW50IHRyZW5kIHN1YnN0YW50aWF0ZXMgVGl0ZWxtYW4gYW5kIExhdWRlcmRhbGUncyAoMjAyMWEpIGZpbmRpbmdzIHRoYXQgaW5jcmVhc2VkIGNvZ25pdGl2ZSByZXNvdXJjZXMgaW1wcm92ZSBndWVzc2luZyBhYmlsaXR5LCB3aGVyZSBncmVhdGVyIGF0dGVudGlvbiB0byBwb2xpdGljcyBhcHBlYXJzIHRvIGFpZCBtb3JlIHdoZW4gaW5mZXJyaW5nIHBhcnRpc2FuIGFsaWdubWVudCwgYW5kIGhpZ2hlciBlZHVjYXRpb24gbGlua3MgbW9yZSB0byB1bmRlcnN0YW5kaW5nIHRoZSBjbGVhdmFnZXMgb2YgdGhlIEJyZXhpdCByZWZlcmVuZHVtLiBIb3dldmVyLCB3aGlsZSB0aGVzZSBjaGFyYWN0ZXJpc3RpY3MgYXBwZWFyIHRvIHN0aWxsIG1hdHRlciwgdGhlaXIgZWZmZWN0cyBhcmUgc21hbGxlciBhbmQgbGVzcyBjZXJ0YWluIHRoYW4gb3JpZ2luYWxseSBzdGF0ZWQuIA0KDQpGdXJ0aGVybW9yZSwgc29tZSBzdWJncm91cCBkaWZmZXJlbmNlcyBkaXNhcHBlYXIsIHN1Z2dlc3RpbmcgZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzIHdlcmUgY2FwdHVyaW5nIGluZGl2aWR1YWwgZGlmZmVyZW5jZXMgcmF0aGVyIHRoYW4gdHJ1ZSBncm91cCBlZmZlY3RzLiBGb3IgZXhhbXBsZSwgVUtJUCB2b3RlcnMgYXBwZWFyZWQgbGVzcyBhY2N1cmF0ZSBpbiB0aGUgb3JpZ2luYWwgbW9kZWwgKEFwcGVuZGl4IFRhYmxlIEExKSwgYnV0IGFmdGVyIGFjY291bnRpbmcgZm9yIHJlc3BvbmRlbnQtbGV2ZWwgdGVuZGVuY2llcywgdGhpcyBkaWZmZXJlbmNlIHdhcyBubyBsb25nZXIgZGlzdGluZ3Vpc2hhYmxlIGZyb20gcmFuZG9tIHZhcmlhdGlvbiAoVGFibGUgNCkuIFRoZXNlIGZpbmRpbmdzIHN1Z2dlc3QgdGhhdCBzb21lIGFwcGFyZW50IGRpZmZlcmVuY2VzIGJldHdlZW4gZ3JvdXBzIG1heSBub3QgcmVwcmVzZW50IGdlbnVpbmUgZGlmZmVyZW5jZXMgaW4gY2l0aXplbnMnIGFiaWxpdHkgdG8gdW5kZXJzdGFuZCBwb2xpdGljYWwgYmVoYXZpb3VyLCBidXQgaW5zdGVhZCByZWZsZWN0IGRpZmZlcmVuY2VzIGJldHdlZW4gaW5kaXZpZHVhbHMgd2hvIGhhcHBlbmVkIHRvIGJlIGNvbmNlbnRyYXRlZCB3aXRoaW4gdGhvc2UgZ3JvdXBzLiANCg0KVGhlc2UgZmluZGluZ3MgcXVhbGlmeSBUaXRlbG1hbiBhbmQgTGF1ZGVyZGFsZeKAmXMgKDIwMjFhKSBhcmd1bWVudCB0aGF0IHBvbGl0aWNhbCBzb3BoaXN0aWNhdGlvbiBlbmhhbmNlcyBwcmVkaWN0aW9uIG9mIHBvbGl0aWNhbCBkaWZmZXJlbmNlcyB0byBhbiBleHRlbnQsIHdoaWxlIHN1Z2dlc3RpbmcgYSBtb3JlIG51YW5jZWQgc3RvcnkgdGhhdCBpbmRpdmlkdWFsLWxldmVsIGNhcGFjaXRpZXMgYmV5b25kIG1lYXN1cmVkIGRlbW9ncmFwaGljIHJlc291cmNlcyBhbHNvIGNvbnRyaWJ1dGUgdG8gdGhpcyBhY2N1cmFjeS4gVGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBtYXJnaW5hbCBhbmQgY29uZGl0aW9uYWwgUsKyIGluIFRhYmxlIDQgKGVnLCBCcmV4aXQgLSAwLjAyMS8wLjEyOCBhbmQgMC4wMTQvMC4wODkpIGluZGljYXRlcyB0aGF0IHVubWVhc3VyZWQgcmVzcG9uZGVudC1sZXZlbCBjaGFyYWN0ZXJpc3RpY3MgZXhwbGFpbiBjb25zaWRlcmFibHkgbW9yZSB2YXJpYXRpb24gdGhhbiB0aGUgbWVhc3VyZWQgZGVtb2dyYXBoaWMgcHJlZGljdG9ycyBhbG9uZS4gVGhlcmVmb3JlLCB3aGlsZSBkZW1vZ3JhcGhpYyBmYWN0b3JzIGNvbnRyaWJ1dGUgdG8gZm9yZWNhc3RpbmcgYWNjdXJhY3ksIG11Y2ggb2YgdGhlIHZhcmlhdGlvbiBhcHBlYXJzIHRvIG9jY3VyIGF0IHRoZSBpbmRpdmlkdWFsIGxldmVsIHJhdGhlciB0aGFuIGJlaW5nIGV4cGxhaW5lZCBieSBvYnNlcnZhYmxlIGNoYXJhY3RlcmlzdGljcy4gDQoNCg0KIyMgKipDb25jbHVzaW9uKiogey19DQpUaGlzIHN0dWR5IGFpbWVkIHRvIGFzc2VzcyB0aGUgcm9idXN0bmVzcyBvZiBUaXRlbG1hbiBhbmQgTGF1ZGVyZGFsZSdzICgyMDIxYSkgZmluZGluZ3MgYnkgZXhhbWluaW5nIHdoZXRoZXIgdGhlaXIgY29uY2x1c2lvbnMgcmVnYXJkaW5nIHJlc3BvbmRlbnQgY2hhcmFjdGVyaXN0aWMgcHJlZGljdG9ycyBvZiBmb3JlY2FzdGluZyBhY2N1cmFjeSByZW1haW4gY29uc2lzdGVudCB1bmRlciBhbHRlcm5hdGl2ZSBtb2RlbGxpbmcgYXNzdW1wdGlvbnMuIFdoaWxlIHRoZSBvcmlnaW5hbCBhbmFseXNpcyBzdWdnZXN0cyB0aGF0IHBvbGl0aWNhbGx5IGF0dGVudGl2ZSBhbmQgaW5mb3JtZWQgcmVzcG9uZGVudHMgYXJlIGJldHRlciBhYmxlIHRvIHByZWRpY3Qgb3RoZXJzJyB2b3RpbmcgYmVoYXZpb3VyLCB0aGlzIHJlcGxpY2F0aW9uIGhhcyBkZW1vbnN0cmF0ZWQgdGhlc2UgZmluZGluZ3MgdG8gYmUgc29tZXdoYXQgc2Vuc2l0aXZlIHRvIG1vZGVsIHNwZWNpZmljYXRpb24uDQoNCkZpcnN0LCB0aGUgcmVwbGljYXRpb24gY29uZmlybWVkIHBhcnRpYWwgdmVyaWZpYWJpbGl0eSBvZiB0aGUgb3JpZ2luYWwgcmVzdWx0cy4gQWx0aG91Z2ggZXhhY3Qgc3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIG91dHB1dHMgY291bGQgbm90IGJlIGZ1bGx5IHJlcG9ydGVkIGR1ZSB0byBpbmNvbnNpc3RlbmNpZXMgaW4gcmVwbGljYXRpb24gbWF0ZXJpYWxzLCB0aGUgY29lZmZpY2llbnRzIHJldGFpbmVkIGNvbnNpc3RlbmN5IHdpdGggdGhlIHB1Ymxpc2hlZCBmaW5kaW5ncy4gVGhpcyBzdXBwb3J0cyB0aGUgYXV0aG9ycycgY2VudHJhbCBjbGFpbSB0aGF0IHBvbGl0aWNhbCBhdHRlbnRpb24gaXMgcG9zaXRpdmVseSBhc3NvY2lhdGVkIHdpdGggZm9yZWNhc3RpbmcgYWNjdXJhY3kgZm9yIGJvdGggcHJvYmFiaWxpc3RpYyBhbmQgZGlyZWN0aW9uYWwgYWNjdXJhY3kuIA0KDQpIb3dldmVyLCB0aGUgZXh0ZW5zaW9uIHRvIGEgbXVsdGlsZXZlbCBtb2RlbGxpbmcgZnJhbWV3b3JrIGhpZ2hsaWdodHMgYW4gaW1wb3J0YW50IGxpbWl0YXRpb25zIGluIHRoZSBvcmlnaW5hbCBhcHByb2FjaC4gQnkgYWNjb3VudGluZyBmb3IgdGhlIGNsdXN0ZXJlZCBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEsIHdoZXJlIG11bHRpcGxlIG9ic2VydmF0aW9ucyBhcmUgbmVzdGVkIHdpdGhpbiBpbmRpdmlkdWFsIHJlc3BvbmRlbnRzLCB0aGUgYW5hbHlzaXMgcHJvdmlkZXMgYSBtb3JlIGFwcHJvcHJpYXRlIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBleHBlcmltZW50YWwgZGF0YS1nZW5lcmF0aW5nIHByb2Nlc3MuIENvbnNpc3RlbnQgd2l0aCBleHBlY3RhdGlvbnMgZnJvbSBwcmlvciBtZXRob2RvbG9naWNhbCBsaXRlcmF0dXJlIChHZWxtYW4gJiBIaWxsLCAyMDA3OyBNYWFzICYgSG94LCAyMDA1KSwgdGhlIGluY2x1c2lvbnMgb2YgcmVzcG9uZGVudC1sZXZlbCByYW5kb20gZWZmZWN0cyByZXN1bHRlZCBpbiBpbmNyZWFzZWQgc3RhbmRhcmQgZXJyb3JzIGFuZCByZWR1Y2VkIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBmb3IgYWxsIHByZWRpY3RvcnMuIFRoaXMgc3VnZ2VzdHMgdGhhdCB0aGUgb3JpZ2luYWwgT0xTIG1vZGVscyBtYXkgaGF2ZSBvdmVyc3RhdGVkIHRoZSBwcmVjaXNpb24gb2YgdGhlaXIgZXN0aW1hdGVzIGJ5IGZhaWxpbmcgdG8gYWNjb3VudCBmb3Igd2l0aGluLXJlc3BvbmRlbnQgY29ycmVsYXRpb24uIA0KDQpTZXZlcmFsIGxpbWl0YXRpb25zIG9mIHRoaXMgcmVwbGljYXRpb24gc2hvdWxkIGJlIGFja25vd2xlZGdlZC4gRmlyc3QsIHRoZSBhbmFseXNpcyByZWxpZXMgZW50aXJlbHkgb24gdGhlIGF1dGhvcnMnIGNsZWFuZWQgZGF0YXNldHMsIHJhdGhlciB0aGFuIHJhdyBkYXRhOiBlaXRoZXIgbm9uLWltcHV0ZWQgQkVTIGRhdGEsIG9yIGNvbGxlY3RpbmcgbmV3IGV4cGVyaW1lbnRhbCBkYXRhLiBUaGlzIHJlc3RyaWN0cyB0aGUgYWJpbGl0eSB0byBmdWxseSBhc3Nlc3MgdGhlIHRyYW5zcGFyZW5jeSBhbmQgY29tcGxldGVuZXNzIG9mIHRoZSBkYXRhIHByZXBhcmF0aW9uIHByb2Nlc3MuIFNlY29uZCwgdGhlIHN0dWR5IGRvZXMgbm90IHRlc3QgZ2VuZXJhbGlzYWJpbGl0eSwgYXMgaXQgaXMgY29uZmluZWQgdG8gdGhlIG9yaWdpbmFsIFVLLWJhc2VkIGV4cGVyaW1lbnRhbCBjb250ZXh0LiBGaW5hbGx5LCB3aGlsZSB0aGUgbXVsdGlsZXZlbCBhcHByb2FjaCBpbXByb3ZlcyBtb2RlbCBzcGVjaWZpY2F0aW9uLCBpdCBkb2VzIG5vdCByZXNvbHZlIG90aGVyIHBvdGVudGlhbCBzb3VyY2VzIG9mIGJpYXMsIHN1Y2ggYXMgdGhlIHVzZSBvZiBNdWx0aXBsZSBJbXB1dGF0aW9uIGJ5IENoYWluZWQgRXF1YXRpb25zIChNSUNFKSBpbiBUaXRlbG1hbiBhbmQgTGF1ZGVyZGxlcydzICgyMDIxYSwgcC4gMjU4KSBvcmlnaW5hbCBoaWRkZW4gZGF0YSBwcmVwYXJhdGlvbiBwcm9jZXNzLCB3aGljaCBpbnRyb2R1Y2VzIGFkZGl0aW9uYWwgbW9kZWxsaW5nIGFzc3VtcHRpb25zIGludG8gdGhlIGNvbnN0cnVjdGlvbiBvZiB0aGUgcHJvZmlsZSBkYXRhLiAgDQoNCkltcG9ydGFudGx5LCB0aGUgZXh0ZW5zaW9uIGRvZXMgbm90IG92ZXJ0dXJuIHRoZSBzdWJzdGFudGl2ZSBjb25jbHVzaW9ucyBtYWRlIGJ5IFRpdGVsbWFuICYgTGF1ZGVyZGFsZSAoMjAyMWEpLiBJbnN0ZWFkLCBpdCByZWZpbmVzIHRoZWlyIGludGVycHJldGF0aW9uLCB3aGVyZSBkZW1vZ3JhcGhpYyBhbmQgcG9saXRpY2FsIHJlc291cmNlIHZhcmlhYmxlcyByZW1haW4gc29tZSBzaWduaWZpY2FudCBwcmVkaWN0b3JzIG9mIGZvcmVjYXN0aW5nIGFiaWxpdHksIGJ1dCB0aGV5IGV4cGxhaW4gbGVzcyB2YXJpYW5jZSBvbmNlIHVub2JzZXJ2ZWQgaW5kaXZpZHVhbHMgZGlmZmVyZW5jZXMgYXJlIHRha2VuIGludG8gYWNjb3VudC4gDQoNCk92ZXJhbGwsIHRoaXMgc3R1ZHkgaGlnaGxpZ2h0cyB0aGUgdmFsdWUgb2YgbW9kZWxsaW5nIGFzc3VtcHRpb25zIGluIHNoYXBpbmcgcXVhbnRpdGF0aXZlIHJlc2VhcmNoLiBXaGlsZSBUaXRlbG1hbiBhbmQgTGF1ZGVyZGFsZSdzICgyMDIxYSkgY29yZSBmaW5kaW5ncyBhcmUgYnJvYWRseSBzdXBwb3J0ZWQsIHRoZXkgYXJlIGxlc3Mgcm9idXN0IHRoYW4gaW5pdGlhbGx5IHByZXNlbnRlZCB3aGVuIGFjY291bnRpbmcgZm9yIHRoZSBoaWVyYXJjaGljYWwgc3RydWN0dXJlIG9mIHRoZSBkYXRhLiBUaGlzIHN1Z2dlc3RzIHRoYXQgY2l0aXplbnMnIGFiaWxpdHkgdG8gaW5mZXIgcGVvcGxlJ3Mgdm90aW5nIGJlaGF2aW91ciBpcyBzaGFwZWQgYnkgYm90aCBwb2xpdGljYWwgcmVzb3VyY2VzIGFuZCB1bm9ic2VydmVkIGluZGl2aWR1YWwgY2FwYWJpbGl0aWVzLiBUaGlzIGhpZ2hsaWdodHMgdGhlIHZhbHVlIG9mIHJlcGxpY2F0aW9uIGFuZCByb2J1c3RuZXNzIGNoZWNrcyBpbiBlbXBpcmljYWwgcmVzZWFyY2ggaW5kaWNhdGVkIGJ5IEZyZWVzZSBhbmQgUGV0ZXJzb24gKDIwMTcpLCBwYXJ0aWN1bGFybHkgd2hlbiB3b3JraW5nIHdpdGggbGF5ZXJlZCBleHBlcmltZW50YWwgZGVzaWducy4gRnV0dXJlIHJlc2VhcmNoIGNvdWxkIGV4dGVuZCB0aGlzIGFuYWx5c2lzIGJ5IGFwcGx5aW5nIHNpbWlsYXIgbW9kZWxzIHRvIGRpZmZlcmVudCBkYXRhc2V0cywgb3IgYnkgaW5jb3Jwb3JhdGluZyBhZGRpdGlvbmFsIHJlc3BvbmRlbnQtbGV2ZWwgY2hhcmFjdGVyaXN0aWNzIHRvIGJldHRlciBjYXB0dXJlIHRoZSBtZWNoYW5pc21zIHVuZGVybHlpbmcgcG9saXRpY2FsIGZvcmNhc3RpbmcgYWNjdXJhY3kuDQoNCg0KDQojIyAqKlJlZmVyZW5jZXMqKiB7LX0NCg0KIyMjIFJlZmVyZW5jZSBMaXN0DQoNCkJpY2tlbCwgUi4gKDIwMDcpICpNdWx0aWxldmVsIGFuYWx5c2lzIGZvciBhcHBsaWVkIHJlc2VhcmNoOiBJdCdzIGp1c3QgcmVncmVzc2lvbiEqIDFzdCBlZG4uIE5ldyBZb3JrOiBHdWlsZm9yZCBQcmVzcy4NCg0KRmllbGRob3VzZSwgRS4sIEdyZWVuLCBKLiwgRXZhbnMsIEcuLCBTY2htaXR0LCBILiwgdmFuIGRlciBFaWprLCBDLiwgTWVsbG9uLCBKLiwgUHJvc3NlciwgQy4gKDIwMTkpICpCcml0aXNoIEVsZWN0aW9uIFN0dWR5LCAyMDE3OiBGYWNlLXRvLUZhY2UgUG9zdC1FbGVjdGlvbiBTdXJ2ZXkqIFtkYXRhIGNvbGxlY3Rpb25dLiBVSyBEYXRhIFNlcnZpY2UuIEF2YWlsYWJsZSBhdDogaHR0cHM6Ly9kb2kub3JnLzEwLjUyNTUvVUtEQS1TTi04NDE4LTEuDQoNCkZpbmNoLCBILiAoMjAyNikgJ1VzaW5nIG11bHRpbGV2ZWwgbW9kZWxpbmcgdG8gdW5kZXJzdGFuZCBuZXN0ZWQgZGF0YScsICpQZXJzcGVjdGl2ZXMgb24gRWFybHkgQ2hpbGRob29kIFBzeWNob2xvZ3kgYW5kIEVkdWNhdGlvbiosIDgoMiksIEFydGljbGUgNC4gQXZhaWxhYmxlIGF0OiBodHRwczovL2RvaS5vcmcvMTAuNTg5NDgvMjgzNC04MjU3LjEwNzYuDQoNCkZyZWVzZSwgSi4gYW5kIFBldGVyc29uLCBELiAoMjAxNykgJ1JlcGxpY2F0aW9uIGluIHNvY2lhbCBzY2llbmNlJywgKkFubnVhbCBSZXZpZXcgb2YgU29jaW9sb2d5KiwgNDMsIHBwLiAxNDfigJMxNjUuIEF2YWlsYWJsZSBhdDogaHR0cHM6Ly9kb2kub3JnLzEwLjExNDYvYW5udXJldi1zb2MtMDYwMTE2LTA1MzQ1MC4NCg0KR2VsbWFuLCBBLiBhbmQgSGlsbCwgSi4gKDIwMDcpICpEYXRhIGFuYWx5c2lzIHVzaW5nIHJlZ3Jlc3Npb24gYW5kIG11bHRpbGV2ZWwvaGllcmFyY2hpY2FsIG1vZGVscyouIENhbWJyaWRnZTogQ2FtYnJpZGdlIFVuaXZlcnNpdHkgUHJlc3MuDQoNCkxld2lzLUJlY2ssIE0uIFMuIGFuZCBTdGVnbWFpZXIsIE0uICgyMDExKSBDaXRpemVuIGZvcmVjYXN0aW5nOiBjYW4gVUsgdm90ZXJzIHNlZSB0aGUgZnV0dXJlPy4gKkVsZWN0b3JhbCBTdHVkaWVzKiAzMCwgcHAuIDI2NOKAkzI2OC4NCg0KTWFhcywgQy5KLk0uIGFuZCBIb3gsIEouSi4gKDIwMDUpICdTdWZmaWNpZW50IHNhbXBsZSBzaXplcyBmb3IgbXVsdGlsZXZlbCBtb2RlbGluZycsICpNZXRob2RvbG9neSosIDEoMyksIHBwLiA4NuKAkzkyLiBBdmFpbGFibGUgYXQ6IGh0dHBzOi8vZG9pLm9yZy8xMC4xMDI3LzE2MTQtMTg4MS4xLjMuODYuDQoNClJhdWRlbmJ1c2gsIFMuVy4gYW5kIEJyeWssIEEuUy4gKDIwMDIpICpIaWVyYXJjaGljYWwgbGluZWFyIG1vZGVsczogQXBwbGljYXRpb25zIGFuZCBkYXRhIGFuYWx5c2lzIG1ldGhvZHMuKiAybmQgZWRuLiBUaG91c2FuZCBPYWtzLCBDQTogU2FnZSBQdWJsaWNhdGlvbnMuDQoNClNuaWpkZXJzLCBULkEuQi4gYW5kIEJvc2tlciwgUi5KLiAoMjAxMikgKk11bHRpbGV2ZWwgYW5hbHlzaXM6IEFuIGludHJvZHVjdGlvbiB0byBiYXNpYyBhbmQgYWR2YW5jZWQgbXVsdGlsZXZlbCBtb2RlbGluZyouIDJuZCBlZG4uIExvbmRvbjogU2FnZSBQdWJsaWNhdGlvbnMuDQoNClRpdGVsbWFuLCBOLiBhbmQgTGF1ZGVyZGFsZSwgQi5FLiAoMjAyMWEpICdDYW4gY2l0aXplbnMgZ3Vlc3MgaG93IG90aGVyIGNpdGl6ZW5zIHZvdGUgYmFzZWQgb24gZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzPycsICpQb2xpdGljYWwgU2NpZW5jZSBSZXNlYXJjaCBhbmQgTWV0aG9kcyosIDExKDIpLCBwcC4gMjU04oCTMjc0LiBBdmFpbGFibGUgYXQ6IGh0dHBzOi8vZG9pLm9yZy8xMC4xMDE3L1BTUk0uMjAyMS41My4NCg0KVGl0ZWxtYW4sIE4uIGFuZCBMYXVkZXJkYWxlLCBCLkUuICgyMDIxYikgKlJlcGxpY2F0aW9uIGRhdGEgZm9yOiBDYW4gY2l0aXplbnMgZ3Vlc3MgaG93IG90aGVyIGNpdGl6ZW5zIHZvdGUgYmFzZWQgb24gZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzPyogW0RhdGFzZXRdLiBIYXJ2YXJkIERhdGF2ZXJzZS4gQXZhaWxhYmxlIGF0OiBodHRwczovL2RvaS5vcmcvMTAuNzkxMC9EVk4vM09XRjZDLg0KDQpUaXRlbG1hbiwgTi4gYW5kIExhdWRlcmRhbGUsIEIuRS4gKDIwMjFjKSAqUmVwbGljYXRpb24gZGF0YSBmb3I6IENhbiBjaXRpemVucyBndWVzcyBob3cgb3RoZXIgY2l0aXplbnMgdm90ZWQgYmFzZWQgb24gZGVtb2dyYXBoaWMgY2hhcmFjdGVyaXN0aWNzPyogW2RhdGEgc2V0XS4gSGFydmFyZCBEYXRhdmVyc2UsIFYxOyAgYXBwZW5kaXgucm1kIFtmaWxlTmFtZV0uIEF2YWlsYWJsZSBhdDogaHR0cHM6Ly9kb2kub3JnLzEwLjc5MTAvRFZOLzNPV0Y2Qy4gDQoNCg0KDQojIyAqKkFwcGVuZGl4Kiogey19DQoNCiMjIyBBcHBlbmRpeCAxLiBFbnZpcm9ubWVudCAoZnVsbCBpbmZvcm1hdGlvbikgDQoNCmBgYHtyIHNlc3Npb259DQojIERldGFpbGVkIGluZm9ybWF0aW9uIG9mIGVudmlyb25tZW50DQpzZXNzaW9uSW5mbygpDQpgYGANCg0KDQojIyMgQXBwZW5kaXggMi4gRW50aXJlIFIgY29kZSB1c2VkIGluIHRoaXMgcHJvamVjdA0KDQpgYGB7ciByZWYubGFiZWw9a25pdHI6OmFsbF9sYWJlbHMoKSwgZWNobz1UUlVFLCBldmFsPUZBTFNFfQ0KYGBgDQo=