License

Copyright (c) 2020 Geosyntec Consultants, Inc.  Mozilla Public License Version 2.0

This software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.

Description

This notebook uses spatial predictors and outfall monitoring data to develop predictive linear regression relationships. This represents the first step in the watershed regression for the stormwater heatmap. The second step is a censored bayesian model, which adds some capabilities that simple linear models do not have. This first step is necessary to select the most predictive parameters in an efficient way.

Here are the steps in the code below:

  1. Code set up
  2. Get and prepare monitoring data
  3. Merge spatial data with monitoring data
  4. Remove mulitcolinear predictors
  5. Peform model selection
  6. Select the best model (these results are then used in the Bayesian model in step 2)

Setup

clear workspace and load libraries. (code not shown in html file )

Get and prepare monitoring data

The stormwater outfall data is available from the Department of Ecology at https://data.wa.gov/Natural-Resources-Environment/Municipal-Stormwater-Permit-Outfall-Data/d958-q2ci.

A .csv file is saved in WatershedRegression/data/S8_data.csv

all.S8.data <- read.csv("data/S8_data.csv",
                        stringsAsFactors = FALSE )

#filter out rejected data
all.S8.data <- (filter(all.S8.data,!result_data_qualifier %in% 'REJ'))

#filter out replicates 
all.S8.data <- (filter(all.S8.data,!sample_replicate_flag %in% 'Y'))

#change nondetect warnings to detects
warnings <- all.S8.data$nondetect_flag == "WARNING"
all.S8.data$nondetect_flag[warnings] <- FALSE 

#Change NA to detect
all.S8.data$nondetect_flag[is.na(all.S8.data$nondetect_flag)] <- FALSE

#Change season to factor 
all.S8.data$season <- as.factor(all.S8.data$season)

The chunk below makes a list of parameters we are interested in.

#Select Parameters
params <- c('Zinc - Water - Total',
 'Copper - Water - Total',
 'Nitrite-Nitrate - Water - Dissolved',
 'Lead - Water - Total',
 'Total Phosphorus - Water - Total',
 'Total Suspended Solids - Water - Total',
 'Total Phthalate - Water - Total',
'Total PAH - Water - Total',
#'Chrysene - Water - Total',
'CPAH - Water - Total',
'HPAH - Water - Total' 
#'Total Kjeldahl Nitrogen - Water - Total',
#'Total PCB - Water - Total'
)

#save a list of all the parameters in case we want to use mor. 
params.all <- data.frame(unique(all.S8.data$parameter))
s8data <- all.S8.data 
kable(params,col.names = "Constituents of concern")

Constituents of concern
Zinc - Water - Total
Copper - Water - Total
Nitrite-Nitrate - Water - Dissolved
Lead - Water - Total
Total Phosphorus - Water - Total
Total Suspended Solids - Water - Total
Total Phthalate - Water - Total
Total PAH - Water - Total
CPAH - Water - Total
HPAH - Water - Total

#

Clean up extracted data and rename columns:

s8data <- all.S8.data %>% 

  dplyr::select(
    study_name,
    location_id,parameter,
    type,
    season,
    new_result_value,
    nondetect_flag,
    study_id,
    access_id,
    field_collection_end_date,
    field_collection_start_date,
    type)


#rename some columns
colnames(s8data)[colnames(s8data) == "location_id"] <- "Location"
colnames(s8data)[colnames(s8data) == "new_result_value"] <-
  "concentration"
s8data$nondetect_flag <- as.logical(s8data$nondetect_flag)
s8data$concentration <- as.numeric(s8data$concentration)

Check for outliers

Set up a jitter plot of all the data to look for outliers:

#make a function for scatter plots 
scatter_cocs <- function(df.coc,title) {
 p <- ggplot(df.coc, aes(1, concentration)) + geom_jitter() + labs(
  title = title,
  subtitle = "Data collected 2009-2013",
  caption =
    " Data source: Ecology, 2015",
  x = "Observations"
  #y = y.title
)+
  theme_ipsum_rc()  
p + facet_wrap( ~ parameter, scales = 'free')+theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank()) 
}

scatter_cocs(s8data[which(s8data$parameter %in% params),],'All Observations')

outliers are apprent for TSS, TP, and no2-no3. Remove these.

#remove and replot 

outlierParams <- c("Total Suspended Solids - Water - Total", "Total Phosphorus - Water - Total", "Nitrite-Nitrate - Water - Dissolved")

#This removes the highest values 
outlierVals <-
  top_n(group_by(s8data[which(s8data$parameter %in% outlierParams), ], parameter), 1, concentration)$concentration

s8data <- s8data %>%
  group_by(parameter) %>%
  slice(which(!(
    parameter %in% outlierParams & concentration %in% outlierVals
  )))

scatter_cocs(s8data[which(s8data$parameter %in% params),],'All Observations - Outliers Removed')

Looks better. Move on to the next Chunk.

Get Spatial data and merge

#Spatial predcitors have been extracted and saved as a csv file. 
#spatial_data<-read_csv("data/spatialPredictors_4_13.csv", col_types = cols(X1 = col_skip()))
spatial_data <- read_csv("data/spatialPredictors_5_14.csv")
Parsed with column specification:
cols(
  `system:index` = col_character(),
  COM = col_double(),
  Location = col_character(),
  RES = col_double(),
  change_year_index = col_double(),
  depSplusN = col_double(),
  impervious = col_double(),
  logPopulation = col_double(),
  logTraffic = col_double(),
  nighttime_lights = col_double(),
  pm25 = col_double(),
  roadDensity = col_double(),
  soil = col_double(),
  .geo = col_character()
)
#RES and COM are compositional data. Change to a ratio
spatial_data$LU_ratio = spatial_data$COM/spatial_data$RES 
spatial_data <- dplyr::select(spatial_data, -c(RES,COM,.geo))
#merge spatial predictors with monitoring data 
s8data.wPredictors <<- merge(s8data, spatial_data)%>% 
  dplyr::select(-c(depSplusN))

kable(head(s8data.wPredictors),caption = "S8 Monitoring Data")
Location study_name parameter type season concentration nondetect_flag study_id access_id field_collection_end_date field_collection_start_date system:index change_year_index impervious logPopulation logTraffic nighttime_lights pm25 roadDensity soil LU_ratio
KICCOMS8D_OUT King County Phase I Municipal Stormwater Permit Copper - Water - Total COM 4 22.800 FALSE WAR044501_S8D 36149 11/2/2011 11/2/2011 0000000000000000000e -0.084 -0.039 -0.564 -0.002 -1.68 -0.75 0.033 -1.37 -0.335
KICCOMS8D_OUT King County Phase I Municipal Stormwater Permit LPAH - Water - Total COM 1 0.060 FALSE WAR044501_S8D 102225 NA 2/12/2011 0000000000000000000e -0.084 -0.039 -0.564 -0.002 -1.68 -0.75 0.033 -1.37 -0.335
KICCOMS8D_OUT King County Phase I Municipal Stormwater Permit Dichlobenil - Water - Total COM 4 0.047 TRUE WAR044501_S8D 35675 11/1/2010 11/1/2010 0000000000000000000e -0.084 -0.039 -0.564 -0.002 -1.68 -0.75 0.033 -1.37 -0.335
KICCOMS8D_OUT King County Phase I Municipal Stormwater Permit Cadmium - Water - Total COM 4 0.065 FALSE WAR044501_S8D 33689 10/19/2012 10/18/2012 0000000000000000000e -0.084 -0.039 -0.564 -0.002 -1.68 -0.75 0.033 -1.37 -0.335
KICCOMS8D_OUT King County Phase I Municipal Stormwater Permit Fecal coliform - Water - Total COM 4 530.000 FALSE WAR044501_S8D 39755 12/8/2010 12/8/2010 0000000000000000000e -0.084 -0.039 -0.564 -0.002 -1.68 -0.75 0.033 -1.37 -0.335
KICCOMS8D_OUT King County Phase I Municipal Stormwater Permit Bis(2-ethylhexyl) phthalate - Water - Total COM 4 1.270 TRUE WAR044501_S8D 38768 11/23/2012 11/23/2012 0000000000000000000e -0.084 -0.039 -0.564 -0.002 -1.68 -0.75 0.033 -1.37 -0.335
getBaseFormula <- function(df.coc) {
  #Function to make a formula for prediction
  predictors <- df.coc %>%
    select_if(is.numeric) %>%
    dplyr::select(-c(concentration, access_id)) %>%
    colnames()
  return(as.formula(paste(
    "(concentration) ~",  (paste((predictors),  collapse = " + ")), " + (1|Location)"
  )))
  
}

Perform Linear Regression

Below, we perform linear regression one parameter at a time.

Functions for regression

We set up a series of functions to make the analysis easier.

Data dictionary for these functions

coc: constituent of concern
df.coc: filtered dataframe
model: basic linear mixed model
log_model: linear-mixed model with log-transformed concentrations
model_with_seasonality: linear-mixed model with seasonality as an added factor
model_with_seasonality_log: linear-mixed model with seasonality as an added factor with log-transformed concentrations

Plotting function

Basic function to plot results by location

plot_s8 <- function(coc) {
    df.coc <- (base::subset(s8data.wPredictors,
                parameter == coc))
  #plot the data to inspect 
  plot <- ggplot(data=(df.coc))+geom_point(aes(x=Location,y=concentration),alpha = 0.5, color = "#0088a3")+scale_y_log10()+labs(
      y = "Concentration (µg/L)",
      x = "Location",
      title =  "Measured Concentrations",
      subtitle = coc ) + theme_ipsum_rc()
  return(plot)
}

Function to remove mulitcolinear predictors in base model

To address multicolinearity, we calculate the variance inflation factor (VIF) and iteratively remove paramters with the highest VIF. We keep removing parameters one at a time until all VIF values are below 5.0.

# #Calculate variance-inflation factors. 
# 
# calc_vif_base <- function(coc) {
#   df.coc <- (base::subset(s8data.wPredictors,
#                 parameter == coc))
#   base_formula <- getBaseFormula(df.coc)#returns a formula with all predictors
#   model.1 <<- lmer(base_formula, data = df.coc, na.action = na.omit)
#   v <- sort(vif(model.1),decreasing=TRUE)
#   return(v)
#   #kable(v,caption="Variance Inflation Factors - not filtered") #display the vif of the dataset
# }

Function to iteratively remove mulitcolinear predictors

Same as above, expect multicolinear predictors are removed.

## *Check VIF
 
check_vif <- function(coc) {
  df.coc <- (base::subset(s8data.wPredictors,
                parameter == coc))
  base_formula <- getBaseFormula(df.coc)#returns a formula with all predictors
  model.1 <- lmer(base_formula, data = df.coc, na.action = na.omit) #make into a lmer object 
  v <- sort(vif(model.1),decreasing=TRUE)
  
  #if the VIF of the highest ranked predictor is >10 then iteratively remove
  model_object <- model.1 #start with model object as the base model (all predictors included)
  
  
  for (i in 1:20) {
    interim_v <- sort(vif(model_object), decreasing = TRUE)
    if (max(interim_v) < 10) {
      break
    }
    predictor_to_drop = as.name(names(interim_v)[which(interim_v == max(interim_v))])
    model_object <-
      stats::update(model_object, paste(".~ . -", predictor_to_drop))
    }
  
  m1Terms <- (labels(terms(model.1)))
  m2Terms <- labels(terms(model_object))
  
  #compare the terms to get a list of the dropped terms
  droppedTerms <- setdiff(m1Terms, m2Terms)
  
  #make a list of selected predictors
  predictors <- m2Terms#colnames(model.frame(model_object)) 

  
  #filter df.coc to remove dropped terms.
  df.coc = dplyr::select(df.coc, -(droppedTerms))
  return(list("vif" = interim_v,"dropped" = droppedTerms,"predictors" = predictors))
  #kable(droppedTerms,caption = "These terms were dropped")
  
  #kable(interim_v,caption="Variance Inflation Factors - multicolinear factors dropped")
}

Function to perform stepwise selection and return a series of best models

## Stepwise Selection

#forward_selection(TRUE,coc,model_info$predictors)

# Extract the model that step found:

#perform forward selection on model parameters. First for non-transformed data, then for log-transformed data
forward_selection <- function(seasonal.bin, coc,predictors) {
  #seasonal.bin = binary (T/F) if seasonal model should be used
  library(lmerTest)
  #make this a lmer object 
  df.coc <- (base::subset(s8data.wPredictors,
                parameter == coc)) 
  model_object_formula <- as.formula(paste(
    "concentration ~",  (paste((predictors),  collapse = " + ")), " + (1|Location)"))
  
  model_object <- lmer(model_object_formula,data=df.coc)
  
  step.2 <-  lmerTest::step(model_object,reduce.random=FALSE,data=df.coc)
  step.2.log <- lmerTest::step(stats::update(model_object, log(concentration)~.))
  
  #extract the models 
  model.3 <- get_model(step.2)
  model.3.log <- get_model(step.2.log)
  
  #perform forward selection on model parameters, this time add seasonality . First for non-transformed data, then for log-transformed data
  step.4 <- lmerTest::step(stats::update(model_object,.~.+season))
  step.4.log <- lmerTest::step(stats::update(model_object,log(concentration) ~.+season))
  model.4 <- get_model(step.4)
  model.4.log <- get_model(step.4.log)
  
  #get formulas 
  model.3.formula <- as.formula(model.3@call$formula)
  model.3.log.formula <- as.formula(model.3.log@call$formula)
  model.4.formula <- as.formula(model.4@call$formula)
  model.4.log.formula <- as.formula(model.4.log@call$formula)
  
  #detach lmer test and remove the models. Keep the formulas. 
  detach("package:lmerTest", unload=TRUE)
  rm(model.3,model.3.log,model.4,model.4.log)
  
  #use lmer for performing modeling
  #calculate base model 
  df.coc <- (base::subset(s8data.wPredictors,
                parameter == coc)) 
  model.base <- lmer(model_object_formula,data=df.coc)
  model <- lmer(model.3.formula,data=df.coc)
  log_model<-  lmer(model.3.log.formula,data=df.coc)
  
  if(seasonal.bin) {
      #if seasonal model switch is on, calc seasonal models
    model_with_seasonality <- lmer(model.4.formula,data=df.coc)
    model_with_seasonality_log <- lmer(model.4.log.formula,data=df.coc)
      #add to list 
    modelList <- c(model,log_model,model_with_seasonality,model_with_seasonality_log)
    
    }
    else {
    modelList <-c(model,log_model)
    modelLables <-c('linear','log-linear') 
    }
   

 
 return(modelList)#,show.aic = TRUE)# = FALSE, title=coc, dv.labels = modelLables) 
  # #make a table of coefficients 
  # tab_model(modelList,
  #   model_log,
  #   model_with_seasonality,
  #   model.with_seasonality_log,
  #   show.aic = TRUE,auto.label = FALSE, title=coc, dv.labels = c('linear','log-linear','linear seasonal','log-linear seasonal'))#file=paste0("results/",coc,".html"))
}

Run through model selection functions

Now we use the functions from above to return model tables. We wrap a helper function to call the others.


results = c()
plots = c()
vifs = c()
tabs = c()
for (i in 1:length(params)){
coc = params[i]
df.coc <- (base::subset(s8data.wPredictors,
                parameter == coc)) 
plots[[coc]] <- plot_s8(coc)
model_info <- check_vif(coc)
vifs[[coc]] <- model_info$vif

results[[coc]] = forward_selection(TRUE,coc,model_info$predictors)}
modelLabels <-c('linear','log-linear','linear seasonal','log-linear seasonal') 

Wrapper function for displaying results for individual cocs.

show_results <- function(j){
#get parameter label 
lab = names(results)[j]

#plot observed 
print(plots[[j]])

#show variance inflation factors 
print(kable(vifs[[j]],caption = paste("Variance inflation factors",lab),col.names = c("vif")))

#show raw summary of results 
models <- results[[j]]
for (k in 2:2){
  print((summary(models[[k]])))
  #plot(models[[k]],,main=paste(lab,"\n","Resididuals"))
  print(qqmath(models[[k]],main=paste(lab,"\n",modelLabels[k],"\n","QQ plot of resididuals")))
}
  

#formatted table of results 
tabs = (tab_model(models,title=lab,show.aic = TRUE,dv.labels =modelLabels))

kable(tabs$knitr) #displays the table inline

}

Now we can specify one parameters at a time and display the results. Generally, the model with the lowest AIC is used in Step 2 (Bayesian modeling).

Zinc

(show_results(1))

vif
impervious 9.21
change_year_index 8.94
nighttime_lights 4.00
roadDensity 3.60
logPopulation 2.70
LU_ratio 2.63

Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ change_year_index + (1 | Location)
   Data: df.coc

REML criterion at convergence: 912

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.886 -0.524 -0.038  0.466  3.302 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.468    0.684   
 Residual             0.472    0.687   
Number of obs: 414, groups:  Location, 14

Fixed effects:
                  Estimate Std. Error t value
(Intercept)          4.042      0.198   20.37
change_year_index    1.013      0.189    5.36

Correlation of Fixed Effects:
            (Intr)
chng_yr_ndx 0.341 

x
|
Zinc - Water - Total
  linear log-linear linear seasonal log-linear seasonal
Predictors Estimates CI p Estimates CI p Estimates CI p Estimates CI p
(Intercept) 92.44 67.34 – 117.55 <0.001 4.04 3.65 – 4.43 <0.001 77.16 48.87 – 105.45 <0.001 4.01 3.62 – 4.41 <0.001
impervious 64.81 40.18 – 89.43 <0.001 63.37 39.35 – 87.38 <0.001
change_year_index 1.01 0.64 – 1.38 <0.001 1.00 0.64 – 1.37 <0.001
season [2] 4.22 -25.91 – 34.34 0.784 -0.06 -0.26 – 0.13 0.521
season [3] 114.53 78.88 – 150.17 <0.001 0.39 0.16 – 0.62 0.001
season [4] 7.93 -16.74 – 32.60 0.529 0.00 -0.16 – 0.16 0.981
Random Effects
σ2 12496.95 0.47 11366.26 0.46
τ00 1809.55 Location 0.47 Location 1739.85 Location 0.46 Location
ICC 0.13 0.50 0.13 0.50
N 14 Location 14 Location 14 Location 14 Location
Observations 414 414 414 414
Marginal R2 / Conditional R2 0.229 / 0.326 0.484 / 0.741 0.290 / 0.384 0.491 / 0.745
AIC 5095.101 920.051 5038.048 920.701

show_results(2)

vif
impervious 9.29
change_year_index 9.26
nighttime_lights 3.99
roadDensity 3.79
LU_ratio 2.64
logPopulation 2.61

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 3141

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.124 -0.474 -0.107  0.175  5.633 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 47.5     6.89    
 Residual             70.5     8.40    
Number of obs: 438, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    20.30       2.31    8.80
roadDensity    15.11       2.74    5.52

Correlation of Fixed Effects:
            (Intr)
roadDensity 0.574 
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 932

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-5.397 -0.532 -0.001  0.552  3.051 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.313    0.559   
 Residual             0.444    0.667   
Number of obs: 438, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    2.559      0.187   13.68
roadDensity    1.263      0.222    5.69

Correlation of Fixed Effects:
            (Intr)
roadDensity 0.574 

Nitrite-Nitrate

vif
impervious 9.27
change_year_index 9.24
nighttime_lights 3.98
roadDensity 3.79
LU_ratio 2.63
logPopulation 2.60

show_results(3)
Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 6108

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-2.944 -0.408 -0.154  0.239  6.027 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 143431   379     
 Residual             185115   430     
Number of obs: 406, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)      480        104    4.63
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 963

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.181 -0.615 -0.017  0.591  3.238 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.571    0.755   
 Residual             0.558    0.747   
Number of obs: 406, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    5.689      0.206    27.6
Error in eval(parse(text = x, keep.source = FALSE)[[1L]]) : 
  object 'logseason' not found

Lead

show_results(4)

vif
impervious 9.22
change_year_index 9.00
nighttime_lights 3.99
roadDensity 3.63
logPopulation 2.67
LU_ratio 2.63

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ logPopulation + roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 3233

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.342 -0.453 -0.105  0.026  9.890 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept)  19.6     4.43   
 Residual             132.5    11.51   
Number of obs: 417, groups:  Location, 14

Fixed effects:
              Estimate Std. Error t value
(Intercept)      13.48       1.65    8.19
logPopulation    -4.93       1.55   -3.17
roadDensity      11.77       2.66    4.43

Correlation of Fixed Effects:
            (Intr) lgPplt
logPopulatn -0.247       
roadDensity  0.561 -0.684
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ logPopulation + roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 1077

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.732 -0.627 -0.016  0.577  3.301 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.858    0.926   
 Residual             0.689    0.830   
Number of obs: 417, groups:  Location, 14

Fixed effects:
              Estimate Std. Error t value
(Intercept)      2.039      0.317    6.44
logPopulation   -0.764      0.298   -2.56
roadDensity      2.129      0.500    4.26

Correlation of Fixed Effects:
            (Intr) lgPplt
logPopulatn -0.247       
roadDensity  0.574 -0.683

Total Phosphorus

show_results(5)

vif
impervious 9.27
change_year_index 9.25
nighttime_lights 3.98
roadDensity 3.78
LU_ratio 2.64
logPopulation 2.60

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 5118

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.922 -0.524 -0.213  0.278  7.274 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 3852     62.1    
 Residual             9811     99.1    
Number of obs: 424, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    183.0       21.1    8.66
roadDensity     88.3       25.1    3.52

Correlation of Fixed Effects:
            (Intr)
roadDensity 0.572 
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 905

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.637 -0.669  0.003  0.673  3.338 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.257    0.507   
 Residual             0.449    0.670   
Number of obs: 424, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    4.875      0.171   28.57
roadDensity    0.708      0.203    3.49

Correlation of Fixed Effects:
            (Intr)
roadDensity 0.574 

Total Suspended Solids

show_results(6)

vif
impervious 9.27
change_year_index 9.20
nighttime_lights 3.98
roadDensity 3.77
LU_ratio 2.64
logPopulation 2.64

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 10253

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-2.071 -0.457 -0.266  0.048  6.526 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 6.76e+08 26004   
 Residual             3.26e+09 57092   
Number of obs: 415, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    64942       9143    7.10
roadDensity    28652      10909    2.63

Correlation of Fixed Effects:
            (Intr)
roadDensity 0.569 
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 1234

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.196 -0.584  0.016  0.557  3.276 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.263    0.513   
 Residual             1.064    1.031   
Number of obs: 415, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)   10.553      0.178   59.13
roadDensity    0.883      0.213    4.15

Correlation of Fixed Effects:
            (Intr)
roadDensity 0.570 

Total Phthalate

show_results(7)

vif
impervious 9.21
change_year_index 9.04
nighttime_lights 3.99
roadDensity 3.65
logPopulation 2.66
LU_ratio 2.63

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ change_year_index + impervious + (1 | Location)
   Data: df.coc

REML criterion at convergence: 2333

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.501 -0.305 -0.150 -0.003  9.027 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept)  2.06    1.44    
 Residual             16.21    4.03    
Number of obs: 412, groups:  Location, 14

Fixed effects:
                  Estimate Std. Error t value
(Intercept)          2.239      0.524    4.27
change_year_index   -2.551      1.099   -2.32
impervious           3.167      1.044    3.03

Correlation of Fixed Effects:
            (Intr) chng__
chng_yr_ndx  0.546       
impervious  -0.478 -0.911
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 1008

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.095 -0.717 -0.114  0.485  4.008 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.580    0.762   
 Residual             0.604    0.777   
Number of obs: 412, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    0.418      0.208    2.01

Total PAH

show_results(8)

vif
impervious 9.22
change_year_index 9.07
nighttime_lights 3.99
roadDensity 3.68
logPopulation 2.65
LU_ratio 2.63

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 1267

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.648 -0.311 -0.159  0.016  9.223 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.271    0.521   
 Residual             1.182    1.087   
Number of obs: 412, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)    0.592      0.151    3.93
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 1193

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.676 -0.448 -0.183  0.542  4.240 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.566    0.752   
 Residual             0.960    0.980   
Number of obs: 412, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)   -1.417      0.208   -6.82
Error in eval(parse(text = x, keep.source = FALSE)[[1L]]) : 
  object 'logseason' not found

CPAH

show_results(9)

vif
impervious 9.19
change_year_index 8.91
nighttime_lights 4.00
roadDensity 3.56
logPopulation 2.70
LU_ratio 2.64

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: -528

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.064 -0.266 -0.122 -0.046 10.387 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.00242  0.0492  
 Residual             0.01512  0.1230  
Number of obs: 412, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)   0.1164     0.0147    7.94
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 856

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.461 -0.274 -0.056  0.129  4.487 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.589    0.768   
 Residual             0.412    0.642   
Number of obs: 412, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)   -2.518      0.208   -12.1

HPAH

show_results(10)

vif
impervious 9.19
change_year_index 8.90
nighttime_lights 4.00
roadDensity 3.55
logPopulation 2.71
LU_ratio 2.65

Linear mixed model fit by REML ['lmerMod']
Formula: concentration ~ logPopulation + roadDensity + (1 | Location)
   Data: df.coc

REML criterion at convergence: 757

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.493 -0.248 -0.119  0.064 11.596 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.0346   0.186   
 Residual             0.3454   0.588   
Number of obs: 412, groups:  Location, 14

Fixed effects:
              Estimate Std. Error t value
(Intercept)     0.1854     0.0721    2.57
logPopulation   0.2016     0.0679    2.97
roadDensity    -0.2976     0.1172   -2.54

Correlation of Fixed Effects:
            (Intr) lgPplt
logPopulatn -0.248       
roadDensity  0.557 -0.683
Linear mixed model fit by REML ['lmerMod']
Formula: log(concentration) ~ (1 | Location)
   Data: df.coc

REML criterion at convergence: 1162

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-3.548 -0.362 -0.128  0.249  4.158 

Random effects:
 Groups   Name        Variance Std.Dev.
 Location (Intercept) 0.497    0.705   
 Residual             0.892    0.944   
Number of obs: 412, groups:  Location, 14

Fixed effects:
            Estimate Std. Error t value
(Intercept)   -2.140      0.195     -11

Total Kjeldahl Nitrogen - Water - Total

vif
impervious 9.26
change_year_index 8.69
nighttime_lights 4.00
roadDensity 3.35
logPopulation 2.80
LU_ratio 2.73

show_results(12)
Error in results[[j]] : subscript out of bounds

LS0tDQp0aXRsZTogJ1dhdGVyc2hlZCBSZWdyZXNzaW9uIC0gUGFydCAxOiBMaW5lYXIgTWl4ZWQgTW9kZWxzIChhZGFwdGVkIHRvIEdMUyBTdXJ2aXZhbCBBbmFseXNpcykgJw0KYXV0aG9yOiAiQ2hyaXN0aWFuIE5pbHNlbiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBodG1sX25vdGVib29rOiANCiAgICB0b2M6IHllcw0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCi0tLQ0KIyMgTGljZW5zZQ0KDQo+IENvcHlyaWdodCAoYykgMjAyMCBHZW9zeW50ZWMgQ29uc3VsdGFudHMsIEluYy4gDQpbTW96aWxsYSBQdWJsaWMgTGljZW5zZSBWZXJzaW9uIDIuMF0oaHR0cHM6Ly9jaG9vc2VhbGljZW5zZS5jb20vbGljZW5zZXMvbXBsLTIuMC8pDQoNCg0KVGhpcyBzb2Z0d2FyZSBpcyBwcm92aWRlZCAiYXMgaXMiLCB3aXRob3V0IHdhcnJhbnR5IG9mIGFueSBraW5kLCBleHByZXNzIG9yIGltcGxpZWQsIGluY2x1ZGluZyBidXQgbm90IGxpbWl0ZWQgdG8gdGhlIHdhcnJhbnRpZXMgb2YgbWVyY2hhbnRhYmlsaXR5LCBmaXRuZXNzIGZvciBhIHBhcnRpY3VsYXIgcHVycG9zZSBhbmQgbm9uaW5mcmluZ2VtZW50LiBJbiBubyBldmVudCBzaGFsbCB0aGUgYXV0aG9ycyBvciBjb3B5cmlnaHQgaG9sZGVycyBiZSBsaWFibGUgZm9yIGFueSBjbGFpbSwgZGFtYWdlcyBvciBvdGhlciBsaWFiaWxpdHksIHdoZXRoZXIgaW4gYW4gYWN0aW9uIG9mIGNvbnRyYWN0LCB0b3J0IG9yIG90aGVyd2lzZSwgYXJpc2luZyBmcm9tLCBvdXQgb2Ygb3IgaW4gY29ubmVjdGlvbiB3aXRoIHRoZSBzb2Z0d2FyZSBvciB0aGUgdXNlIG9yIG90aGVyIGRlYWxpbmdzIGluIHRoZSBzb2Z0d2FyZS4gIA0KDQo8IS0tIA0KDQpUbyBkb3MgaGVyZTogDQoNCg0KDQotLT4NCg0KDQojIERlc2NyaXB0aW9uIA0KDQpUaGlzIG5vdGVib29rIHVzZXMgc3BhdGlhbCBwcmVkaWN0b3JzIGFuZCBvdXRmYWxsIG1vbml0b3JpbmcgZGF0YSB0byBkZXZlbG9wIHByZWRpY3RpdmUgbGluZWFyIHJlZ3Jlc3Npb24gcmVsYXRpb25zaGlwcy4gVGhpcyByZXByZXNlbnRzIHRoZSBmaXJzdCBzdGVwIGluIHRoZSB3YXRlcnNoZWQgcmVncmVzc2lvbiBmb3IgdGhlIHN0b3Jtd2F0ZXIgaGVhdG1hcC4gVGhlIHNlY29uZCBzdGVwIGlzIGEgY2Vuc29yZWQgYmF5ZXNpYW4gbW9kZWwsIHdoaWNoIGFkZHMgc29tZSBjYXBhYmlsaXRpZXMgdGhhdCBzaW1wbGUgbGluZWFyIG1vZGVscyBkbyBub3QgaGF2ZS4gVGhpcyBmaXJzdCBzdGVwIGlzIG5lY2Vzc2FyeSB0byBzZWxlY3QgdGhlIG1vc3QgcHJlZGljdGl2ZSBwYXJhbWV0ZXJzIGluIGFuIGVmZmljaWVudCB3YXkuIA0KDQpIZXJlIGFyZSB0aGUgc3RlcHMgaW4gdGhlIGNvZGUgYmVsb3c6DQogICANCjEuIENvZGUgc2V0IHVwICANCjIuIEdldCBhbmQgcHJlcGFyZSBtb25pdG9yaW5nIGRhdGEgIA0KMy4gTWVyZ2Ugc3BhdGlhbCBkYXRhIHdpdGggbW9uaXRvcmluZyBkYXRhICANCjQuIFJlbW92ZSBtdWxpdGNvbGluZWFyIHByZWRpY3RvcnMgICANCjUuIFBlZm9ybSBtb2RlbCBzZWxlY3Rpb24gICANCjYuIFNlbGVjdCB0aGUgYmVzdCBtb2RlbCAodGhlc2UgcmVzdWx0cyBhcmUgdGhlbiB1c2VkIGluIHRoZSBCYXllc2lhbiBtb2RlbCBpbiBzdGVwIDIpICANCiAgDQoNCg0KIyBTZXR1cCANCg0KY2xlYXIgd29ya3NwYWNlIGFuZCBsb2FkIGxpYnJhcmllcy4gKGNvZGUgbm90IHNob3duIGluIGh0bWwgZmlsZSApICANCmBgYHtyIGtuaXRyX2luaXQsIGNhY2hlPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KI3JtKGxpc3QgPSBscygpKQ0KI3NldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgDQojc2V0d2QoIn4vcmVwb3Mvc3Rvcm13YXRlcmhlYXRtYXAvUi1zY3JpcHRzL1dhdGVyc2hlZFJlZ3Jlc3Npb24iKQ0KDQojbG9hZCBwYWNrYWdlcw0KbGlicmFyeShrbml0cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShjYXIpDQpsaWJyYXJ5KGNhcmV0KQ0KbGlicmFyeShwc3ljaCkNCmxpYnJhcnkoRGF0YUV4cGxvcmVyKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGxtZTQpDQpsaWJyYXJ5KG5sbWUpDQpsaWJyYXJ5KGhyYnJ0aGVtZXMpDQpsaWJyYXJ5KHNqUGxvdCkNCmxpYnJhcnkoTWV0cmljcykNCg0KDQoNCiMjIEdsb2JhbCBvcHRpb25zDQoNCm9wdHNfY2h1bmskc2V0KHByb21wdD1GQUxTRSwNCiAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsDQogICAgICAgICAgICAgICB3YXJuaW5nPUZBTFNFKQ0Kb3B0aW9ucyhzY2lwZW4gPSAxLCBkaWdpdHMgPSAzKQ0KDQojc2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eSANCnNldC5zZWVkKDUwKQ0KYGBgDQoNCg0KIyBHZXQgYW5kIHByZXBhcmUgbW9uaXRvcmluZyBkYXRhDQoNClRoZSBzdG9ybXdhdGVyIG91dGZhbGwgZGF0YSBpcyBhdmFpbGFibGUgZnJvbSB0aGUgRGVwYXJ0bWVudCBvZiBFY29sb2d5IGF0IGh0dHBzOi8vZGF0YS53YS5nb3YvTmF0dXJhbC1SZXNvdXJjZXMtRW52aXJvbm1lbnQvTXVuaWNpcGFsLVN0b3Jtd2F0ZXItUGVybWl0LU91dGZhbGwtRGF0YS9kOTU4LXEyY2kuDQoNCkEgLmNzdiBmaWxlIGlzIHNhdmVkIGluIGBgYFdhdGVyc2hlZFJlZ3Jlc3Npb24vZGF0YS9TOF9kYXRhLmNzdmBgYCANCg0KYGBge3IgZWNobz1UUlVFfQ0KYWxsLlM4LmRhdGEgPC0gcmVhZC5jc3YoImRhdGEvUzhfZGF0YS5jc3YiLA0KICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFICkNCg0KI2ZpbHRlciBvdXQgcmVqZWN0ZWQgZGF0YQ0KYWxsLlM4LmRhdGEgPC0gKGZpbHRlcihhbGwuUzguZGF0YSwhcmVzdWx0X2RhdGFfcXVhbGlmaWVyICVpbiUgJ1JFSicpKQ0KDQojZmlsdGVyIG91dCByZXBsaWNhdGVzIA0KYWxsLlM4LmRhdGEgPC0gKGZpbHRlcihhbGwuUzguZGF0YSwhc2FtcGxlX3JlcGxpY2F0ZV9mbGFnICVpbiUgJ1knKSkNCg0KI2NoYW5nZSBub25kZXRlY3Qgd2FybmluZ3MgdG8gZGV0ZWN0cw0Kd2FybmluZ3MgPC0gYWxsLlM4LmRhdGEkbm9uZGV0ZWN0X2ZsYWcgPT0gIldBUk5JTkciDQphbGwuUzguZGF0YSRub25kZXRlY3RfZmxhZ1t3YXJuaW5nc10gPC0gRkFMU0UgDQoNCiNDaGFuZ2UgTkEgdG8gZGV0ZWN0DQphbGwuUzguZGF0YSRub25kZXRlY3RfZmxhZ1tpcy5uYShhbGwuUzguZGF0YSRub25kZXRlY3RfZmxhZyldIDwtIEZBTFNFDQoNCiNDaGFuZ2Ugc2Vhc29uIHRvIGZhY3RvciANCmFsbC5TOC5kYXRhJHNlYXNvbiA8LSBhcy5mYWN0b3IoYWxsLlM4LmRhdGEkc2Vhc29uKQ0KDQoNCmBgYA0KDQpUaGUgY2h1bmsgYmVsb3cgbWFrZXMgYSBsaXN0IG9mIHBhcmFtZXRlcnMgd2UgYXJlIGludGVyZXN0ZWQgaW4uIA0KDQpgYGB7ciBTZWxlY3QgUGFyYW1ldGVyczogfQ0KI1NlbGVjdCBQYXJhbWV0ZXJzDQpwYXJhbXMgPC0gYygnWmluYyAtIFdhdGVyIC0gVG90YWwnLA0KICdDb3BwZXIgLSBXYXRlciAtIFRvdGFsJywNCiAnTml0cml0ZS1OaXRyYXRlIC0gV2F0ZXIgLSBEaXNzb2x2ZWQnLA0KICdMZWFkIC0gV2F0ZXIgLSBUb3RhbCcsDQogJ1RvdGFsIFBob3NwaG9ydXMgLSBXYXRlciAtIFRvdGFsJywNCiAnVG90YWwgU3VzcGVuZGVkIFNvbGlkcyAtIFdhdGVyIC0gVG90YWwnLA0KICdUb3RhbCBQaHRoYWxhdGUgLSBXYXRlciAtIFRvdGFsJywNCidUb3RhbCBQQUggLSBXYXRlciAtIFRvdGFsJywNCiMnQ2hyeXNlbmUgLSBXYXRlciAtIFRvdGFsJywNCidDUEFIIC0gV2F0ZXIgLSBUb3RhbCcsDQonSFBBSCAtIFdhdGVyIC0gVG90YWwnIA0KIydUb3RhbCBLamVsZGFobCBOaXRyb2dlbiAtIFdhdGVyIC0gVG90YWwnLA0KIydUb3RhbCBQQ0IgLSBXYXRlciAtIFRvdGFsJw0KKQ0KDQojc2F2ZSBhIGxpc3Qgb2YgYWxsIHRoZSBwYXJhbWV0ZXJzIGluIGNhc2Ugd2Ugd2FudCB0byB1c2UgbW9yLiANCnBhcmFtcy5hbGwgPC0gZGF0YS5mcmFtZSh1bmlxdWUoYWxsLlM4LmRhdGEkcGFyYW1ldGVyKSkNCnM4ZGF0YSA8LSBhbGwuUzguZGF0YSANCmthYmxlKHBhcmFtcyxjb2wubmFtZXMgPSAiQ29uc3RpdHVlbnRzIG9mIGNvbmNlcm4iKQ0KIw0KYGBgDQoNCkNsZWFuIHVwIGV4dHJhY3RlZCBkYXRhIGFuZCByZW5hbWUgY29sdW1uczogDQoNCmBgYHtyIH0NCnM4ZGF0YSA8LSBhbGwuUzguZGF0YSAlPiUgDQoNCiAgZHBseXI6OnNlbGVjdCgNCiAgICBzdHVkeV9uYW1lLA0KICAgIGxvY2F0aW9uX2lkLHBhcmFtZXRlciwNCiAgICB0eXBlLA0KICAgIHNlYXNvbiwNCiAgICBuZXdfcmVzdWx0X3ZhbHVlLA0KICAgIG5vbmRldGVjdF9mbGFnLA0KICAgIHN0dWR5X2lkLA0KICAgIGFjY2Vzc19pZCwNCiAgICBmaWVsZF9jb2xsZWN0aW9uX2VuZF9kYXRlLA0KICAgIGZpZWxkX2NvbGxlY3Rpb25fc3RhcnRfZGF0ZSwNCiAgICB0eXBlKQ0KDQoNCiNyZW5hbWUgc29tZSBjb2x1bW5zDQpjb2xuYW1lcyhzOGRhdGEpW2NvbG5hbWVzKHM4ZGF0YSkgPT0gImxvY2F0aW9uX2lkIl0gPC0gIkxvY2F0aW9uIg0KY29sbmFtZXMoczhkYXRhKVtjb2xuYW1lcyhzOGRhdGEpID09ICJuZXdfcmVzdWx0X3ZhbHVlIl0gPC0NCiAgImNvbmNlbnRyYXRpb24iDQpzOGRhdGEkbm9uZGV0ZWN0X2ZsYWcgPC0gYXMubG9naWNhbChzOGRhdGEkbm9uZGV0ZWN0X2ZsYWcpDQpzOGRhdGEkY29uY2VudHJhdGlvbiA8LSBhcy5udW1lcmljKHM4ZGF0YSRjb25jZW50cmF0aW9uKQ0KDQoNCmBgYA0KDQojIyBDaGVjayBmb3Igb3V0bGllcnMNCg0KU2V0IHVwIGEgaml0dGVyIHBsb3Qgb2YgYWxsIHRoZSBkYXRhIHRvIGxvb2sgZm9yIG91dGxpZXJzOiANCiAgDQpgYGB7ciBmaWcuaGVpZ2h0PTh9DQojbWFrZSBhIGZ1bmN0aW9uIGZvciBzY2F0dGVyIHBsb3RzIA0Kc2NhdHRlcl9jb2NzIDwtIGZ1bmN0aW9uKGRmLmNvYyx0aXRsZSkgew0KIHAgPC0gZ2dwbG90KGRmLmNvYywgYWVzKDEsIGNvbmNlbnRyYXRpb24pKSArIGdlb21faml0dGVyKCkgKyBsYWJzKA0KICB0aXRsZSA9IHRpdGxlLA0KICBzdWJ0aXRsZSA9ICJEYXRhIGNvbGxlY3RlZCAyMDA5LTIwMTMiLA0KICBjYXB0aW9uID0NCiAgICAiIERhdGEgc291cmNlOiBFY29sb2d5LCAyMDE1IiwNCiAgeCA9ICJPYnNlcnZhdGlvbnMiDQogICN5ID0geS50aXRsZQ0KKSsNCiAgdGhlbWVfaXBzdW1fcmMoKSAgDQpwICsgZmFjZXRfd3JhcCggfiBwYXJhbWV0ZXIsIHNjYWxlcyA9ICdmcmVlJykrdGhlbWUoYXhpcy50aXRsZS54PWVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLng9ZWxlbWVudF9ibGFuaygpKSANCn0NCg0Kc2NhdHRlcl9jb2NzKHM4ZGF0YVt3aGljaChzOGRhdGEkcGFyYW1ldGVyICVpbiUgcGFyYW1zKSxdLCdBbGwgT2JzZXJ2YXRpb25zJykNCmBgYA0KICAgDQpvdXRsaWVycyBhcmUgYXBwcmVudCBmb3IgVFNTLCBUUCwgYW5kIG5vMi1ubzMuIFJlbW92ZSB0aGVzZS4gDQpgYGB7ciByZW1vdmUgb3V0bGllcnMsIGZpZy5oZWlnaHQ9OH0NCiNyZW1vdmUgYW5kIHJlcGxvdCANCg0Kb3V0bGllclBhcmFtcyA8LSBjKCJUb3RhbCBTdXNwZW5kZWQgU29saWRzIC0gV2F0ZXIgLSBUb3RhbCIsICJUb3RhbCBQaG9zcGhvcnVzIC0gV2F0ZXIgLSBUb3RhbCIsICJOaXRyaXRlLU5pdHJhdGUgLSBXYXRlciAtIERpc3NvbHZlZCIpDQoNCiNUaGlzIHJlbW92ZXMgdGhlIGhpZ2hlc3QgdmFsdWVzIA0Kb3V0bGllclZhbHMgPC0NCiAgdG9wX24oZ3JvdXBfYnkoczhkYXRhW3doaWNoKHM4ZGF0YSRwYXJhbWV0ZXIgJWluJSBvdXRsaWVyUGFyYW1zKSwgXSwgcGFyYW1ldGVyKSwgMSwgY29uY2VudHJhdGlvbikkY29uY2VudHJhdGlvbg0KDQpzOGRhdGEgPC0gczhkYXRhICU+JQ0KICBncm91cF9ieShwYXJhbWV0ZXIpICU+JQ0KICBzbGljZSh3aGljaCghKA0KICAgIHBhcmFtZXRlciAlaW4lIG91dGxpZXJQYXJhbXMgJiBjb25jZW50cmF0aW9uICVpbiUgb3V0bGllclZhbHMNCiAgKSkpDQoNCnNjYXR0ZXJfY29jcyhzOGRhdGFbd2hpY2goczhkYXRhJHBhcmFtZXRlciAlaW4lIHBhcmFtcyksXSwnQWxsIE9ic2VydmF0aW9ucyAtIE91dGxpZXJzIFJlbW92ZWQnKQ0KDQpgYGANCiAgIA0KTG9va3MgYmV0dGVyLiBNb3ZlIG9uIHRvIHRoZSBuZXh0IENodW5rLiANCg0KIyBHZXQgU3BhdGlhbCBkYXRhIGFuZCBtZXJnZSAgDQoNCmBgYHtyfQ0KI1NwYXRpYWwgcHJlZGNpdG9ycyBoYXZlIGJlZW4gZXh0cmFjdGVkIGFuZCBzYXZlZCBhcyBhIGNzdiBmaWxlLiANCiNzcGF0aWFsX2RhdGE8LXJlYWRfY3N2KCJkYXRhL3NwYXRpYWxQcmVkaWN0b3JzXzRfMTMuY3N2IiwgY29sX3R5cGVzID0gY29scyhYMSA9IGNvbF9za2lwKCkpKQ0Kc3BhdGlhbF9kYXRhIDwtIHJlYWRfY3N2KCJkYXRhL3NwYXRpYWxQcmVkaWN0b3JzXzVfMTQuY3N2IikNCiNSRVMgYW5kIENPTSBhcmUgY29tcG9zaXRpb25hbCBkYXRhLiBDaGFuZ2UgdG8gYSByYXRpbw0Kc3BhdGlhbF9kYXRhJExVX3JhdGlvID0gc3BhdGlhbF9kYXRhJENPTS9zcGF0aWFsX2RhdGEkUkVTIA0Kc3BhdGlhbF9kYXRhIDwtIGRwbHlyOjpzZWxlY3Qoc3BhdGlhbF9kYXRhLCAtYyhSRVMsQ09NLC5nZW8pKQ0KI21lcmdlIHNwYXRpYWwgcHJlZGljdG9ycyB3aXRoIG1vbml0b3JpbmcgZGF0YSANCnM4ZGF0YS53UHJlZGljdG9ycyA8PC0gbWVyZ2UoczhkYXRhLCBzcGF0aWFsX2RhdGEpJT4lIA0KICBkcGx5cjo6c2VsZWN0KC1jKGRlcFNwbHVzTikpDQoNCmthYmxlKGhlYWQoczhkYXRhLndQcmVkaWN0b3JzKSxjYXB0aW9uID0gIlM4IE1vbml0b3JpbmcgRGF0YSIpDQpgYGANCg0KDQoNCg0KYGBge3IgZnVuY3Rpb25zfQ0KZ2V0QmFzZUZvcm11bGEgPC0gZnVuY3Rpb24oZGYuY29jKSB7DQogICNGdW5jdGlvbiB0byBtYWtlIGEgZm9ybXVsYSBmb3IgcHJlZGljdGlvbg0KICBwcmVkaWN0b3JzIDwtIGRmLmNvYyAlPiUNCiAgICBzZWxlY3RfaWYoaXMubnVtZXJpYykgJT4lDQogICAgZHBseXI6OnNlbGVjdCgtYyhjb25jZW50cmF0aW9uLCBhY2Nlc3NfaWQpKSAlPiUNCiAgICBjb2xuYW1lcygpDQogIHJldHVybihhcy5mb3JtdWxhKHBhc3RlKA0KICAgICIoY29uY2VudHJhdGlvbikgfiIsICAocGFzdGUoKHByZWRpY3RvcnMpLCAgY29sbGFwc2UgPSAiICsgIikpLCAiICsgKDF8TG9jYXRpb24pIg0KICApKSkNCiAgDQp9DQpgYGANCg0KIyBQZXJmb3JtIExpbmVhciBSZWdyZXNzaW9uDQpCZWxvdywgd2UgcGVyZm9ybSBsaW5lYXIgcmVncmVzc2lvbiBvbmUgcGFyYW1ldGVyIGF0IGEgdGltZS4gDQoNCiMjIEZ1bmN0aW9ucyBmb3IgcmVncmVzc2lvbiANCldlIHNldCB1cCBhIHNlcmllcyBvZiBmdW5jdGlvbnMgdG8gbWFrZSB0aGUgYW5hbHlzaXMgZWFzaWVyLiANCg0KDQojIyMjIERhdGEgZGljdGlvbmFyeSBmb3IgdGhlc2UgZnVuY3Rpb25zDQpgYGBjb2NgYGA6IGNvbnN0aXR1ZW50IG9mIGNvbmNlcm4gIA0KYGBgZGYuY29jYGBgOiBmaWx0ZXJlZCBkYXRhZnJhbWUgICANCmBgYG1vZGVsYGBgOiBiYXNpYyBsaW5lYXIgbWl4ZWQgbW9kZWwgICANCmBgYGxvZ19tb2RlbGBgYDogIGxpbmVhci1taXhlZCBtb2RlbCB3aXRoIGxvZy10cmFuc2Zvcm1lZCBjb25jZW50cmF0aW9ucyAgICANCmBgYG1vZGVsX3dpdGhfc2Vhc29uYWxpdHlgYGA6IGxpbmVhci1taXhlZCBtb2RlbCB3aXRoIHNlYXNvbmFsaXR5IGFzIGFuIGFkZGVkIGZhY3RvciAgICANCmBgYG1vZGVsX3dpdGhfc2Vhc29uYWxpdHlfbG9nYGBgOiBsaW5lYXItbWl4ZWQgbW9kZWwgd2l0aCBzZWFzb25hbGl0eSBhcyBhbiBhZGRlZCBmYWN0b3Igd2l0aCBsb2ctdHJhbnNmb3JtZWQgY29uY2VudHJhdGlvbnMgICAgIA0KDQojIyMgUGxvdHRpbmcgZnVuY3Rpb24NCkJhc2ljIGZ1bmN0aW9uIHRvIHBsb3QgcmVzdWx0cyBieSBsb2NhdGlvbg0KYGBge3J9DQpwbG90X3M4IDwtIGZ1bmN0aW9uKGNvYykgew0KICAgIGRmLmNvYyA8LSAoYmFzZTo6c3Vic2V0KHM4ZGF0YS53UHJlZGljdG9ycywNCiAgICAgICAgICAgICAgICBwYXJhbWV0ZXIgPT0gY29jKSkNCiAgI3Bsb3QgdGhlIGRhdGEgdG8gaW5zcGVjdCANCiAgcGxvdCA8LSBnZ3Bsb3QoZGF0YT0oZGYuY29jKSkrZ2VvbV9wb2ludChhZXMoeD1Mb2NhdGlvbix5PWNvbmNlbnRyYXRpb24pKStzY2FsZV95X2xvZzEwKCkrbGFicygNCiAgICAgIHkgPSAiQ29uY2VudHJhdGlvbiAowrVnL0wpIiwNCiAgICAgIHggPSAiTG9jYXRpb24iLA0KICAgICAgdGl0bGUgPSAgIk1lYXN1cmVkIENvbmNlbnRyYXRpb25zIiwNCiAgICAgIHN1YnRpdGxlID0gY29jICkgKyB0aGVtZV9pcHN1bV9yYygpDQogIHJldHVybihwbG90KQ0KfQ0KYGBgDQoNCiMjIEZ1bmN0aW9uIHRvIHJlbW92ZSBtdWxpdGNvbGluZWFyIHByZWRpY3RvcnMgaW4gYmFzZSBtb2RlbCANClRvIGFkZHJlc3MgbXVsdGljb2xpbmVhcml0eSwgd2UgY2FsY3VsYXRlIHRoZSB2YXJpYW5jZSBpbmZsYXRpb24gZmFjdG9yIChWSUYpIGFuZCBpdGVyYXRpdmVseSByZW1vdmUgcGFyYW10ZXJzIHdpdGggdGhlIGhpZ2hlc3QgVklGLiBXZSBrZWVwIHJlbW92aW5nIHBhcmFtZXRlcnMgb25lIGF0IGEgdGltZSB1bnRpbCBhbGwgVklGIHZhbHVlcyBhcmUgYmVsb3cgNS4wLiANCg0KYGBge3J9DQojICNDYWxjdWxhdGUgdmFyaWFuY2UtaW5mbGF0aW9uIGZhY3RvcnMuIA0KIyANCiMgY2FsY192aWZfYmFzZSA8LSBmdW5jdGlvbihjb2MpIHsNCiMgICBkZi5jb2MgPC0gKGJhc2U6OnN1YnNldChzOGRhdGEud1ByZWRpY3RvcnMsDQojICAgICAgICAgICAgICAgICBwYXJhbWV0ZXIgPT0gY29jKSkNCiMgICBiYXNlX2Zvcm11bGEgPC0gZ2V0QmFzZUZvcm11bGEoZGYuY29jKSNyZXR1cm5zIGEgZm9ybXVsYSB3aXRoIGFsbCBwcmVkaWN0b3JzDQojICAgbW9kZWwuMSA8PC0gbG1lcihiYXNlX2Zvcm11bGEsIGRhdGEgPSBkZi5jb2MsIG5hLmFjdGlvbiA9IG5hLm9taXQpDQojICAgdiA8LSBzb3J0KHZpZihtb2RlbC4xKSxkZWNyZWFzaW5nPVRSVUUpDQojICAgcmV0dXJuKHYpDQojICAgI2thYmxlKHYsY2FwdGlvbj0iVmFyaWFuY2UgSW5mbGF0aW9uIEZhY3RvcnMgLSBub3QgZmlsdGVyZWQiKSAjZGlzcGxheSB0aGUgdmlmIG9mIHRoZSBkYXRhc2V0DQojIH0NCg0KYGBgDQoNCg0KIyMgRnVuY3Rpb24gdG8gaXRlcmF0aXZlbHkgcmVtb3ZlIG11bGl0Y29saW5lYXIgcHJlZGljdG9ycyANClNhbWUgYXMgYWJvdmUsIGV4cGVjdCBtdWx0aWNvbGluZWFyIHByZWRpY3RvcnMgYXJlIHJlbW92ZWQuIA0KYGBge3J9DQojIyAqQ2hlY2sgVklGDQogDQpjaGVja192aWYgPC0gZnVuY3Rpb24oY29jKSB7DQogIGRmLmNvYyA8LSAoYmFzZTo6c3Vic2V0KHM4ZGF0YS53UHJlZGljdG9ycywNCiAgICAgICAgICAgICAgICBwYXJhbWV0ZXIgPT0gY29jKSkNCiAgYmFzZV9mb3JtdWxhIDwtIGdldEJhc2VGb3JtdWxhKGRmLmNvYykjcmV0dXJucyBhIGZvcm11bGEgd2l0aCBhbGwgcHJlZGljdG9ycw0KICBtb2RlbC4xIDwtIGxtZXIoYmFzZV9mb3JtdWxhLCBkYXRhID0gZGYuY29jLCBuYS5hY3Rpb24gPSBuYS5vbWl0KSAjbWFrZSBpbnRvIGEgbG1lciBvYmplY3QgDQogIHYgPC0gc29ydCh2aWYobW9kZWwuMSksZGVjcmVhc2luZz1UUlVFKQ0KICANCiAgI2lmIHRoZSBWSUYgb2YgdGhlIGhpZ2hlc3QgcmFua2VkIHByZWRpY3RvciBpcyA+MTAgdGhlbiBpdGVyYXRpdmVseSByZW1vdmUNCiAgbW9kZWxfb2JqZWN0IDwtIG1vZGVsLjEgI3N0YXJ0IHdpdGggbW9kZWwgb2JqZWN0IGFzIHRoZSBiYXNlIG1vZGVsIChhbGwgcHJlZGljdG9ycyBpbmNsdWRlZCkNCiAgDQogIA0KICBmb3IgKGkgaW4gMToyMCkgew0KICAgIGludGVyaW1fdiA8LSBzb3J0KHZpZihtb2RlbF9vYmplY3QpLCBkZWNyZWFzaW5nID0gVFJVRSkNCiAgICBpZiAobWF4KGludGVyaW1fdikgPCAxMCkgew0KICAgICAgYnJlYWsNCiAgICB9DQogICAgcHJlZGljdG9yX3RvX2Ryb3AgPSBhcy5uYW1lKG5hbWVzKGludGVyaW1fdilbd2hpY2goaW50ZXJpbV92ID09IG1heChpbnRlcmltX3YpKV0pDQogICAgbW9kZWxfb2JqZWN0IDwtDQogICAgICBzdGF0czo6dXBkYXRlKG1vZGVsX29iamVjdCwgcGFzdGUoIi5+IC4gLSIsIHByZWRpY3Rvcl90b19kcm9wKSkNCiAgICB9DQogIA0KICBtMVRlcm1zIDwtIChsYWJlbHModGVybXMobW9kZWwuMSkpKQ0KICBtMlRlcm1zIDwtIGxhYmVscyh0ZXJtcyhtb2RlbF9vYmplY3QpKQ0KICANCiAgI2NvbXBhcmUgdGhlIHRlcm1zIHRvIGdldCBhIGxpc3Qgb2YgdGhlIGRyb3BwZWQgdGVybXMNCiAgZHJvcHBlZFRlcm1zIDwtIHNldGRpZmYobTFUZXJtcywgbTJUZXJtcykNCiAgDQogICNtYWtlIGEgbGlzdCBvZiBzZWxlY3RlZCBwcmVkaWN0b3JzDQogIHByZWRpY3RvcnMgPC0gbTJUZXJtcyNjb2xuYW1lcyhtb2RlbC5mcmFtZShtb2RlbF9vYmplY3QpKSANCg0KICANCiAgI2ZpbHRlciBkZi5jb2MgdG8gcmVtb3ZlIGRyb3BwZWQgdGVybXMuDQogIGRmLmNvYyA9IGRwbHlyOjpzZWxlY3QoZGYuY29jLCAtKGRyb3BwZWRUZXJtcykpDQogIHJldHVybihsaXN0KCJ2aWYiID0gaW50ZXJpbV92LCJkcm9wcGVkIiA9IGRyb3BwZWRUZXJtcywicHJlZGljdG9ycyIgPSBwcmVkaWN0b3JzKSkNCiAgI2thYmxlKGRyb3BwZWRUZXJtcyxjYXB0aW9uID0gIlRoZXNlIHRlcm1zIHdlcmUgZHJvcHBlZCIpDQogIA0KICAja2FibGUoaW50ZXJpbV92LGNhcHRpb249IlZhcmlhbmNlIEluZmxhdGlvbiBGYWN0b3JzIC0gbXVsdGljb2xpbmVhciBmYWN0b3JzIGRyb3BwZWQiKQ0KfQ0KDQpgYGANCg0KIyMgRnVuY3Rpb24gdG8gcGVyZm9ybSBzdGVwd2lzZSBzZWxlY3Rpb24gYW5kIHJldHVybiBhIHNlcmllcyBvZiBiZXN0IG1vZGVscyANCg0KYGBge3J9DQojIyBTdGVwd2lzZSBTZWxlY3Rpb24NCg0KI2ZvcndhcmRfc2VsZWN0aW9uKFRSVUUsY29jLG1vZGVsX2luZm8kcHJlZGljdG9ycykNCg0KIyBFeHRyYWN0IHRoZSBtb2RlbCB0aGF0IHN0ZXAgZm91bmQ6DQoNCiNwZXJmb3JtIGZvcndhcmQgc2VsZWN0aW9uIG9uIG1vZGVsIHBhcmFtZXRlcnMuIEZpcnN0IGZvciBub24tdHJhbnNmb3JtZWQgZGF0YSwgdGhlbiBmb3IgbG9nLXRyYW5zZm9ybWVkIGRhdGENCmZvcndhcmRfc2VsZWN0aW9uIDwtIGZ1bmN0aW9uKHNlYXNvbmFsLmJpbiwgY29jLHByZWRpY3RvcnMpIHsNCiAgI3NlYXNvbmFsLmJpbiA9IGJpbmFyeSAoVC9GKSBpZiBzZWFzb25hbCBtb2RlbCBzaG91bGQgYmUgdXNlZA0KICBsaWJyYXJ5KGxtZXJUZXN0KQ0KICAjbWFrZSB0aGlzIGEgbG1lciBvYmplY3QgDQogIGRmLmNvYyA8LSAoYmFzZTo6c3Vic2V0KHM4ZGF0YS53UHJlZGljdG9ycywNCiAgICAgICAgICAgICAgICBwYXJhbWV0ZXIgPT0gY29jKSkgDQogIG1vZGVsX29iamVjdF9mb3JtdWxhIDwtIGFzLmZvcm11bGEocGFzdGUoDQogICAgImNvbmNlbnRyYXRpb24gfiIsICAocGFzdGUoKHByZWRpY3RvcnMpLCAgY29sbGFwc2UgPSAiICsgIikpLCAiICsgKDF8TG9jYXRpb24pIikpDQogIA0KICBtb2RlbF9vYmplY3QgPC0gbG1lcihtb2RlbF9vYmplY3RfZm9ybXVsYSxkYXRhPWRmLmNvYykNCiAgDQogIHN0ZXAuMiA8LSAgbG1lclRlc3Q6OnN0ZXAobW9kZWxfb2JqZWN0LHJlZHVjZS5yYW5kb209RkFMU0UsZGF0YT1kZi5jb2MpDQogIHN0ZXAuMi5sb2cgPC0gbG1lclRlc3Q6OnN0ZXAoc3RhdHM6OnVwZGF0ZShtb2RlbF9vYmplY3QsIGxvZyhjb25jZW50cmF0aW9uKX4uKSkNCiAgDQogICNleHRyYWN0IHRoZSBtb2RlbHMgDQogIG1vZGVsLjMgPC0gZ2V0X21vZGVsKHN0ZXAuMikNCiAgbW9kZWwuMy5sb2cgPC0gZ2V0X21vZGVsKHN0ZXAuMi5sb2cpDQogIA0KICAjcGVyZm9ybSBmb3J3YXJkIHNlbGVjdGlvbiBvbiBtb2RlbCBwYXJhbWV0ZXJzLCB0aGlzIHRpbWUgYWRkIHNlYXNvbmFsaXR5IC4gRmlyc3QgZm9yIG5vbi10cmFuc2Zvcm1lZCBkYXRhLCB0aGVuIGZvciBsb2ctdHJhbnNmb3JtZWQgZGF0YQ0KICBzdGVwLjQgPC0gbG1lclRlc3Q6OnN0ZXAoc3RhdHM6OnVwZGF0ZShtb2RlbF9vYmplY3QsLn4uK3NlYXNvbikpDQogIHN0ZXAuNC5sb2cgPC0gbG1lclRlc3Q6OnN0ZXAoc3RhdHM6OnVwZGF0ZShtb2RlbF9vYmplY3QsbG9nKGNvbmNlbnRyYXRpb24pIH4uK3NlYXNvbikpDQogIG1vZGVsLjQgPC0gZ2V0X21vZGVsKHN0ZXAuNCkNCiAgbW9kZWwuNC5sb2cgPC0gZ2V0X21vZGVsKHN0ZXAuNC5sb2cpDQogIA0KICAjZ2V0IGZvcm11bGFzIA0KICBtb2RlbC4zLmZvcm11bGEgPC0gYXMuZm9ybXVsYShtb2RlbC4zQGNhbGwkZm9ybXVsYSkNCiAgbW9kZWwuMy5sb2cuZm9ybXVsYSA8LSBhcy5mb3JtdWxhKG1vZGVsLjMubG9nQGNhbGwkZm9ybXVsYSkNCiAgbW9kZWwuNC5mb3JtdWxhIDwtIGFzLmZvcm11bGEobW9kZWwuNEBjYWxsJGZvcm11bGEpDQogIG1vZGVsLjQubG9nLmZvcm11bGEgPC0gYXMuZm9ybXVsYShtb2RlbC40LmxvZ0BjYWxsJGZvcm11bGEpDQogIA0KICAjZGV0YWNoIGxtZXIgdGVzdCBhbmQgcmVtb3ZlIHRoZSBtb2RlbHMuIEtlZXAgdGhlIGZvcm11bGFzLiANCiAgZGV0YWNoKCJwYWNrYWdlOmxtZXJUZXN0IiwgdW5sb2FkPVRSVUUpDQogIHJtKG1vZGVsLjMsbW9kZWwuMy5sb2csbW9kZWwuNCxtb2RlbC40LmxvZykNCiAgDQogICN1c2UgbG1lciBmb3IgcGVyZm9ybWluZyBtb2RlbGluZw0KICAjY2FsY3VsYXRlIGJhc2UgbW9kZWwgDQogIGRmLmNvYyA8LSAoYmFzZTo6c3Vic2V0KHM4ZGF0YS53UHJlZGljdG9ycywNCiAgICAgICAgICAgICAgICBwYXJhbWV0ZXIgPT0gY29jKSkgDQogIG1vZGVsLmJhc2UgPC0gbG1lcihtb2RlbF9vYmplY3RfZm9ybXVsYSxkYXRhPWRmLmNvYykNCiAgbW9kZWwgPC0gbG1lcihtb2RlbC4zLmZvcm11bGEsZGF0YT1kZi5jb2MpDQogIGxvZ19tb2RlbDwtICBsbWVyKG1vZGVsLjMubG9nLmZvcm11bGEsZGF0YT1kZi5jb2MpDQogIA0KICBpZihzZWFzb25hbC5iaW4pIHsNCiAgICAgICNpZiBzZWFzb25hbCBtb2RlbCBzd2l0Y2ggaXMgb24sIGNhbGMgc2Vhc29uYWwgbW9kZWxzDQogICAgbW9kZWxfd2l0aF9zZWFzb25hbGl0eSA8LSBsbWVyKG1vZGVsLjQuZm9ybXVsYSxkYXRhPWRmLmNvYykNCiAgICBtb2RlbF93aXRoX3NlYXNvbmFsaXR5X2xvZyA8LSBsbWVyKG1vZGVsLjQubG9nLmZvcm11bGEsZGF0YT1kZi5jb2MpDQogICAgICAjYWRkIHRvIGxpc3QgDQogICAgbW9kZWxMaXN0IDwtIGMobW9kZWwsbG9nX21vZGVsLG1vZGVsX3dpdGhfc2Vhc29uYWxpdHksbW9kZWxfd2l0aF9zZWFzb25hbGl0eV9sb2cpDQogICAgDQogICAgfQ0KICAgIGVsc2Ugew0KICAgIG1vZGVsTGlzdCA8LWMobW9kZWwsbG9nX21vZGVsKQ0KICAgIG1vZGVsTGFibGVzIDwtYygnbGluZWFyJywnbG9nLWxpbmVhcicpIA0KICAgIH0NCiAgIA0KDQogDQogcmV0dXJuKG1vZGVsTGlzdCkjLHNob3cuYWljID0gVFJVRSkjID0gRkFMU0UsIHRpdGxlPWNvYywgZHYubGFiZWxzID0gbW9kZWxMYWJsZXMpIA0KICAjICNtYWtlIGEgdGFibGUgb2YgY29lZmZpY2llbnRzIA0KICAjIHRhYl9tb2RlbChtb2RlbExpc3QsDQogICMgICBtb2RlbF9sb2csDQogICMgICBtb2RlbF93aXRoX3NlYXNvbmFsaXR5LA0KICAjICAgbW9kZWwud2l0aF9zZWFzb25hbGl0eV9sb2csDQogICMgICBzaG93LmFpYyA9IFRSVUUsYXV0by5sYWJlbCA9IEZBTFNFLCB0aXRsZT1jb2MsIGR2LmxhYmVscyA9IGMoJ2xpbmVhcicsJ2xvZy1saW5lYXInLCdsaW5lYXIgc2Vhc29uYWwnLCdsb2ctbGluZWFyIHNlYXNvbmFsJykpI2ZpbGU9cGFzdGUwKCJyZXN1bHRzLyIsY29jLCIuaHRtbCIpKQ0KfQ0KYGBgDQoNCg0KDQojIyBSdW4gdGhyb3VnaCBtb2RlbCBzZWxlY3Rpb24gZnVuY3Rpb25zDQoNCk5vdyB3ZSB1c2UgdGhlIGZ1bmN0aW9ucyBmcm9tIGFib3ZlIHRvIHJldHVybiBtb2RlbCB0YWJsZXMuDQpXZSB3cmFwIGEgaGVscGVyIGZ1bmN0aW9uIHRvIGNhbGwgdGhlIG90aGVycy4NCmBgYHtyfQ0KDQpyZXN1bHRzID0gYygpDQpwbG90cyA9IGMoKQ0KdmlmcyA9IGMoKQ0KdGFicyA9IGMoKQ0KZm9yIChpIGluIDE6bGVuZ3RoKHBhcmFtcykpew0KY29jID0gcGFyYW1zW2ldDQpkZi5jb2MgPC0gKGJhc2U6OnN1YnNldChzOGRhdGEud1ByZWRpY3RvcnMsDQogICAgICAgICAgICAgICAgcGFyYW1ldGVyID09IGNvYykpIA0KcGxvdHNbW2NvY11dIDwtIHBsb3RfczgoY29jKQ0KbW9kZWxfaW5mbyA8LSBjaGVja192aWYoY29jKQ0Kdmlmc1tbY29jXV0gPC0gbW9kZWxfaW5mbyR2aWYNCg0KcmVzdWx0c1tbY29jXV0gPSBmb3J3YXJkX3NlbGVjdGlvbihUUlVFLGNvYyxtb2RlbF9pbmZvJHByZWRpY3RvcnMpfQ0KbW9kZWxMYWJlbHMgPC1jKCdsaW5lYXInLCdsb2ctbGluZWFyJywnbGluZWFyIHNlYXNvbmFsJywnbG9nLWxpbmVhciBzZWFzb25hbCcpIA0KDQpgYGANCg0KV3JhcHBlciBmdW5jdGlvbiBmb3IgZGlzcGxheWluZyByZXN1bHRzIGZvciBpbmRpdmlkdWFsIGNvY3MuIA0KYGBge3J9DQpzaG93X3Jlc3VsdHMgPC0gZnVuY3Rpb24oail7DQojZ2V0IHBhcmFtZXRlciBsYWJlbCANCmxhYiA9IG5hbWVzKHJlc3VsdHMpW2pdDQoNCiNwbG90IG9ic2VydmVkIA0KcHJpbnQocGxvdHNbW2pdXSkNCg0KI3Nob3cgdmFyaWFuY2UgaW5mbGF0aW9uIGZhY3RvcnMgDQpwcmludChrYWJsZSh2aWZzW1tqXV0sY2FwdGlvbiA9IHBhc3RlKCJWYXJpYW5jZSBpbmZsYXRpb24gZmFjdG9ycyIsbGFiKSxjb2wubmFtZXMgPSBjKCJ2aWYiKSkpDQoNCiNzaG93IHJhdyBzdW1tYXJ5IG9mIHJlc3VsdHMgDQptb2RlbHMgPC0gcmVzdWx0c1tbal1dDQpmb3IgKGsgaW4gMjoyKXsNCiAgcHJpbnQoKHN1bW1hcnkobW9kZWxzW1trXV0pKSkNCiAgI3Bsb3QobW9kZWxzW1trXV0sLG1haW49cGFzdGUobGFiLCJcbiIsIlJlc2lkaWR1YWxzIikpDQogIHByaW50KHFxbWF0aChtb2RlbHNbW2tdXSxtYWluPXBhc3RlKGxhYiwiXG4iLG1vZGVsTGFiZWxzW2tdLCJcbiIsIlFRIHBsb3Qgb2YgcmVzaWRpZHVhbHMiKSkpDQp9DQogIA0KDQojZm9ybWF0dGVkIHRhYmxlIG9mIHJlc3VsdHMgDQp0YWJzID0gKHRhYl9tb2RlbChtb2RlbHMsdGl0bGU9bGFiLHNob3cuYWljID0gVFJVRSxkdi5sYWJlbHMgPW1vZGVsTGFiZWxzKSkNCg0Ka2FibGUodGFicyRrbml0cikgI2Rpc3BsYXlzIHRoZSB0YWJsZSBpbmxpbmUNCg0KfQ0KDQoNCg0KYGBgDQoNCk5vdyB3ZSBjYW4gc3BlY2lmeSBvbmUgcGFyYW1ldGVycyBhdCBhIHRpbWUgYW5kIGRpc3BsYXkgdGhlIHJlc3VsdHMuIEdlbmVyYWxseSwgdGhlIG1vZGVsIHdpdGggdGhlIGxvd2VzdCBBSUMgaXMgdXNlZCBpbiBTdGVwIDIgKEJheWVzaWFuIG1vZGVsaW5nKS4NCg0KIyMgWmluYw0KYGBge3J9DQooc2hvd19yZXN1bHRzKDEpKQ0KDQpgYGANCg0KDQpgYGB7cn0NCnNob3dfcmVzdWx0cygyKQ0KDQpgYGANCg0KIyMgTml0cml0ZS1OaXRyYXRlDQoNCmBgYHtyfQ0Kc2hvd19yZXN1bHRzKDMpDQpgYGANCg0KIyMgTGVhZA0KDQpgYGB7cn0NCnNob3dfcmVzdWx0cyg0KQ0KYGBgDQoNCiMjIFRvdGFsIFBob3NwaG9ydXMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNob3dfcmVzdWx0cyg1KQ0KDQpgYGANCg0KIyMgVG90YWwgU3VzcGVuZGVkIFNvbGlkcyANCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNob3dfcmVzdWx0cyg2KQ0KDQpgYGANCg0KIyMgVG90YWwgUGh0aGFsYXRlDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzaG93X3Jlc3VsdHMoNykNCmBgYA0KDQoNCiMjIFRvdGFsIFBBSA0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2hvd19yZXN1bHRzKDgpDQpgYGANCg0KIyMgQ1BBSA0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNob3dfcmVzdWx0cyg5KQ0KYGBgDQoNCiMjIEhQQUgNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpzaG93X3Jlc3VsdHMoMTApDQpgYGANCiMjIFRvdGFsIEtqZWxkYWhsIE5pdHJvZ2VuIC0gV2F0ZXIgLSBUb3RhbA0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNob3dfcmVzdWx0cygxMSkNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg==