# Load the packages.
library(dplyr)
library(tidyr)
library(hector)
library(hectorcal)
library(ggplot2)
library(cluster) # clustering algorithms
library(factoextra)
# Set up the directories.
PROJECT_DIR <- "/Users/dorh012/Documents/2019/hectorcal"
INPUT_DIR <- file.path(PROJECT_DIR, 'analysis', 'best_fit', 'final_conc', 'ensemble')
# Define Functions
# Set up Hector core with the best fit paramter values
#
# Args
# input: a dataframe of 1 nrow that contains values for Hector parameters and model name
# Return: a datat frame containing the Hector results.
param_run_Hector <- function(input){
# Select the paramter values from the input data frame.
params <- as.numeric(input[names(input) %in% c(ECS(), DIFFUSIVITY(), VOLCANIC_SCALE(), AERO_SCALE())])
names(params) <- names(input)[names(input) %in% c(ECS(), DIFFUSIVITY(), VOLCANIC_SCALE(), AERO_SCALE())]
# Parmeterize the hector cores.
lapply(hector_cores, parameterize_core, params = params)
# Run Hector
lapply(hector_cores, reset)
lapply(hector_cores, run, runtodate = 2100)
bind_rows(lapply(hector_cores, function(c){
tryCatch({
parameterize_core(params = params, core = c)
reset(core = c)
run(core = c, runtodate = 2100)
cbind(method = input[['method']],
cluster = input[['cluster']],
fetchvars(core = c, dates = 1850:2100, vars = c(GLOBAL_TEMP(), HEAT_FLUX())))
}, error = function(e){NULL})
}))
}
# Import the hector output and the best fit parameters.
best_fits <- readRDS(file.path(INPUT_DIR, 'best_fit_params.rds'))
# The Hector temp and heatflux data from Hector runs using the best fit parameters.
best_fit_output <- readRDS(file.path(INPUT_DIR, 'best_fit_hector_output.rds')) %>%
rename(experiment = scenario) %>%
mutate(experiment = if_else(grepl(pattern = 'rcp', x = experiment) & year >= 2005, experiment, 'historical')) %>%
distinct() %>%
left_join(best_fits %>%
select(model, method), by = 'model')
What happens if we categorize models by their end of century heat flux value?
How does EOC heat flux look like?
esm_comparison %>%
filter(variable == 'heatflux' & !grepl('esm', experiment)) %>%
filter(year == 2100) %>%
group_by(experiment) %>%
summarise(cmip_min = mean(mina),
cmip_max = mean(maxb)) %>%
ungroup %>%
gather(stat, value, cmip_min, cmip_max) %>%
mutate(height = 1) ->
esm_bounds
# What does the 2090 to 2100 mean heat flux and temp look like??
best_fit_output %>%
filter(year == 2100 ) %>%
filter(experiment != 'historical' & variable == 'heatflux') %>%
group_by(model, experiment, variable, method) %>%
summarise(value = mean(value)) %>%
ungroup %>%
mutate(height = 1) %>%
ggplot() +
geom_vline(data = esm_bounds, aes(xintercept = value, color = stat)) +
geom_histogram(aes(value)) +
#geom_histogram(aes(value), bins = 40) +
#geom_dotplot(data = esm_bounds, aes(value, fill = stat, color = stat)) +
facet_grid(variable ~ experiment) +
labs(title = 'EOC heatflux',
y = 'count',
x = 'heat flux W/m^2')

The vertical lines reprsent the esm or cmip min and max range values. There are rcp60 and rcp85 EOC heatflux values that exceede the cmip range the distribtuion of the heat flux is broken up in rcp60 and rcp 85 but it is not obvious if that is clusetering or because there is small sample size of models used to make the histograms (30 models or observations).
So what happens if we categorize the models into where they fall within the IQR? Since there are a fair amount of models that have heatflux that is somewhere close to the mean.
esm_comparison %>%
filter(variable == 'heatflux' & !grepl('esm', experiment)) %>%
filter(year == 2100) %>%
mutate(Q0 = mina,
Q100 = maxb,
Q50 = (Q0 + Q100) / 2,
Q25 = (Q0 + Q50) / 2,
Q75 = (Q50 + Q100) / 2) %>%
select(year, variable, experiment, Q0, Q25, Q50, Q75, Q100) ->
quantile_df
best_fit_output %>%
filter(year == 2100 ) %>%
filter(experiment != 'historical' & variable == 'heatflux') %>%
inner_join(quantile_df, by = c('experiment', 'year', 'variable')) %>%
mutate(category = '1st quant') %>%
mutate(category = if_else(value >= Q25, '2nd quant', category)) %>%
mutate(category = if_else(value >= Q50, '3nd quant', category)) %>%
mutate(category = if_else(value >= Q75, '4th quant', category)) %>%
select(model, category, experiment) ->
heat_flux_categorized
xmid <- 0.5*(min(best_fits$S) + max(best_fits$S))
xrng <- 1.5*(max(best_fits$S) - min(best_fits$S))
xlo <- xmid - 0.5*xrng
xhi <- xmid + 0.5*xrng
best_fits %>%
left_join(heat_flux_categorized) %>%
ggplot() +
geom_label(aes(S, diff, label = model, fill = category)) +
labs(title = 'EOC heatflux') +
facet_wrap('experiment') +
theme(legend.position = 'bottom') +
theme_bw(base_size = 14) +
ggplot2::xlim(c(xlo,xhi))

NA
There is not an obvious or consistent pattern here, but it is interesting that as the rcp increases that the more of the models fall furhter away from the mean, there are less models in the 2nd and 3rd qunatile ranges.
What happens if we do the PCA by the 4 parmeters?
# Store the in a data frame where the rows are named by the model
df <- data.frame(S = best_fits$S, diff = best_fits$diff, row.names = best_fits$model,
alpha = best_fits$alpha, volscl = best_fits$volscl, stringsAsFactors = FALSE)
k_4params <- kmeans(df, centers = 5, nstart = 25)
fviz_cluster(k_4params, data = df, axes = c(1, 2))

But now plot diff and S on the axses grouped by cluster.
cluster_rslts <- data.frame(model = names(k_4params$cluster),
cluster = as.character(k_4params$cluster))
best_fits %>%
left_join(cluster_rslts) %>%
ggplot(aes(S, diff, label = model, fill = cluster)) +
geom_label() +
theme_bw(base_size = 14) +
ggplot2::xlim(c(xlo,xhi)) +
labs(title = 'diff vs S grouped by cluster')
Joining, by = "model"
Column `model` joining character vector and factor, coercing into character vector

What happens if we do the PCA by the 4 parmeters + EOC heat flux and temp values from rcp85?
best_fit_output %>%
filter(variable %in% c('heatflux', 'Tgav') & year == 2100 & experiment == 'rcp85') %>%
group_by(model, variable) %>%
summarise(value = mean(value)) %>%
ungroup %>%
spread(variable, value) %>%
select(model, heatflux, Tgav) ->
EOC_hf
# Store the in a data frame where the rows are named by the model
df <- data.frame(S = best_fits$S, diff = best_fits$diff, row.names = best_fits$model,
alpha = best_fits$alpha, volscl = best_fits$volscl, model = best_fits$model, stringsAsFactors = FALSE) %>%
left_join(EOC_hf) %>%
select(-model)
Joining, by = "model"
df <- as.data.frame(df, row.names = best_fits$model)
k_4params_eocHF <- kmeans(df, centers = 5, nstart = 25)
fviz_cluster(k_4params_eocHF, data = df, axes = c(1, 2))

cluster_rslts2 <- data.frame(model = names(k_4params_eocHF$cluster),
cluster = as.character(k_4params_eocHF$cluster))
best_fits %>%
left_join(cluster_rslts2) %>%
ggplot(aes(S, diff, label = model, fill = cluster)) +
geom_label() +
theme_bw(base_size = 14) +
ggplot2::xlim(c(xlo,xhi)) +
labs(title = 'diff vs S grouped by cluster')
Joining, by = "model"
Column `model` joining character vector and factor, coercing into character vector

Well it looks like the models are broken up into the same groups again but when we visualize it the groups are more obvious. I wonder if what happens when we run Hector with the different centriod values?
Hector with kmeans centriod params
tibble::as_tibble(k_4params_eocHF$centers) %>%
mutate(method = 'params + EOC ',
cluster = 1:nrow(k_4params_eocHF$centers)) %>%
bind_rows(tibble::as_tibble(k_4params$centers) %>%
mutate(method = 'params') %>%
mutate(cluster1 = 1:5) %>%
# Rename the clusters so that they are comparable to the cluster results from the other kmean method
mutate(cluster = if_else(cluster1 == 2, 5, as.double(cluster1)),
cluster = if_else(cluster1 == 4, 1, as.double(cluster)),
cluster = if_else(cluster1 == 1, 4, as.double(cluster)),
cluster = if_else(cluster1 == 5, 2, as.double(cluster)))) ->
center_df
# Create a list of the hector cores that we are going to want to run.
hector_cores <- list(newcore(system.file('input/hector_rcp85_constrained.ini', package = 'hector'), name = 'rcp85'))
apply(X = center_df, MARGIN = 1, FUN = param_run_Hector) %>%
bind_rows() ->
central_output
How much do the central points or the paramters change for the clusters based on the differnt methods?
center_df %>%
select(S, diff, alpha, volscl, method, cluster) %>%
gather(param, value, S, diff, alpha, volscl) %>%
ggplot(aes(value, cluster, color = method)) +
geom_point(size = 2, alpha = 0.7) +
facet_wrap('param', scales = 'free') +
labs(title = 'center param values based on the two k methods')

whelp it looks like including the EOC temp and heat flux data did not change were the kmeans centers were so it probably will not impact the Hector output either.
ggplot(data = central_output %>%
filter(scenario =='rcp85') %>%
mutate(metho = factor(method,levels = c('params', 'params + EOC')))) +
geom_line(aes(year, value, color = method, linetype = method, group = interaction(method, cluster, scenario)),
size = 0.75) +
facet_grid(variable ~ cluster, scales = 'free_y')

Wrap Up
Well I offically have a case of the Friday brain mush and I am going to head out. I need to still look at the results by grouping the models the physics information, when I was running the calibrations earlier I was having issues getting the runs to converge :( and it looks like when we break up the models by physics we quickly run out of our data requirements (hist tas + future tas)
LS0tCnRpdGxlOiAiS21lYW5zIFMgdnMgZGlmZiBpbnZlc3RpZ2F0aW9uIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgTG9hZCB0aGUgcGFja2FnZXMuCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoaGVjdG9yKQpsaWJyYXJ5KGhlY3RvcmNhbCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGNsdXN0ZXIpICAgICMgY2x1c3RlcmluZyBhbGdvcml0aG1zCmxpYnJhcnkoZmFjdG9leHRyYSkKCgojIFNldCB1cCB0aGUgZGlyZWN0b3JpZXMuClBST0pFQ1RfRElSIDwtICIvVXNlcnMvZG9yaDAxMi9Eb2N1bWVudHMvMjAxOS9oZWN0b3JjYWwiCklOUFVUX0RJUiAgIDwtIGZpbGUucGF0aChQUk9KRUNUX0RJUiwgJ2FuYWx5c2lzJywgJ2Jlc3RfZml0JywgJ2ZpbmFsX2NvbmMnLCAnZW5zZW1ibGUnKQoKCiMgRGVmaW5lIEZ1bmN0aW9ucyAKIyBTZXQgdXAgSGVjdG9yIGNvcmUgd2l0aCB0aGUgYmVzdCBmaXQgcGFyYW10ZXIgdmFsdWVzCiMKIyBBcmdzCiMgICBpbnB1dDogYSBkYXRhZnJhbWUgb2YgMSBucm93IHRoYXQgY29udGFpbnMgdmFsdWVzIGZvciBIZWN0b3IgcGFyYW1ldGVycyBhbmQgbW9kZWwgbmFtZQojIFJldHVybjogYSBkYXRhdCBmcmFtZSBjb250YWluaW5nIHRoZSBIZWN0b3IgcmVzdWx0cy4KcGFyYW1fcnVuX0hlY3RvciA8LSBmdW5jdGlvbihpbnB1dCl7CgogICAgIyBTZWxlY3QgdGhlIHBhcmFtdGVyIHZhbHVlcyBmcm9tIHRoZSBpbnB1dCBkYXRhIGZyYW1lLgogICAgcGFyYW1zIDwtIGFzLm51bWVyaWMoaW5wdXRbbmFtZXMoaW5wdXQpICVpbiUgYyhFQ1MoKSwgRElGRlVTSVZJVFkoKSwgVk9MQ0FOSUNfU0NBTEUoKSwgQUVST19TQ0FMRSgpKV0pCiAgICBuYW1lcyhwYXJhbXMpIDwtIG5hbWVzKGlucHV0KVtuYW1lcyhpbnB1dCkgJWluJSBjKEVDUygpLCBESUZGVVNJVklUWSgpLCBWT0xDQU5JQ19TQ0FMRSgpLCBBRVJPX1NDQUxFKCkpXQoKICAgICMgUGFybWV0ZXJpemUgdGhlIGhlY3RvciBjb3Jlcy4KICAgIGxhcHBseShoZWN0b3JfY29yZXMsIHBhcmFtZXRlcml6ZV9jb3JlLCBwYXJhbXMgPSBwYXJhbXMpCgogICAgIyBSdW4gSGVjdG9yCiAgICBsYXBwbHkoaGVjdG9yX2NvcmVzLCByZXNldCkKICAgIGxhcHBseShoZWN0b3JfY29yZXMsIHJ1biwgcnVudG9kYXRlID0gMjEwMCkKCiAgICBiaW5kX3Jvd3MobGFwcGx5KGhlY3Rvcl9jb3JlcywgZnVuY3Rpb24oYyl7CgogICAgICAgIHRyeUNhdGNoKHsKICAgICAgICAgICAgcGFyYW1ldGVyaXplX2NvcmUocGFyYW1zID0gcGFyYW1zLCBjb3JlID0gYykKICAgICAgICAgICAgcmVzZXQoY29yZSA9IGMpCiAgICAgICAgICAgIHJ1bihjb3JlID0gYywgcnVudG9kYXRlID0gMjEwMCkKICAgICAgICAgICAgY2JpbmQobWV0aG9kID0gaW5wdXRbWydtZXRob2QnXV0sCiAgICAgICAgICAgICAgICAgIGNsdXN0ZXIgPSBpbnB1dFtbJ2NsdXN0ZXInXV0sCiAgICAgICAgICAgICAgICAgIGZldGNodmFycyhjb3JlID0gYywgZGF0ZXMgPSAxODUwOjIxMDAsIHZhcnMgPSBjKEdMT0JBTF9URU1QKCksIEhFQVRfRkxVWCgpKSkpCiAgICAgICAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSl7TlVMTH0pCgoKICAgIH0pKQoKCn0KCmBgYAoKCmBgYHtyfQojIEltcG9ydCB0aGUgaGVjdG9yIG91dHB1dCBhbmQgdGhlIGJlc3QgZml0IHBhcmFtZXRlcnMuIApiZXN0X2ZpdHMgICAgICAgPC0gcmVhZFJEUyhmaWxlLnBhdGgoSU5QVVRfRElSLCAnYmVzdF9maXRfcGFyYW1zLnJkcycpKSAKIyBUaGUgSGVjdG9yIHRlbXAgYW5kIGhlYXRmbHV4IGRhdGEgZnJvbSBIZWN0b3IgcnVucyB1c2luZyB0aGUgYmVzdCBmaXQgcGFyYW1ldGVycy4KYmVzdF9maXRfb3V0cHV0IDwtIHJlYWRSRFMoZmlsZS5wYXRoKElOUFVUX0RJUiwgJ2Jlc3RfZml0X2hlY3Rvcl9vdXRwdXQucmRzJykpICU+JSAgCiAgICByZW5hbWUoZXhwZXJpbWVudCA9IHNjZW5hcmlvKSAlPiUgCiAgICBtdXRhdGUoZXhwZXJpbWVudCA9IGlmX2Vsc2UoZ3JlcGwocGF0dGVybiA9ICdyY3AnLCB4ID0gZXhwZXJpbWVudCkgJiB5ZWFyID49IDIwMDUsIGV4cGVyaW1lbnQsICdoaXN0b3JpY2FsJykpICU+JSAKICAgIGRpc3RpbmN0KCkgJT4lIAogICAgbGVmdF9qb2luKGJlc3RfZml0cyAlPiUgCiAgICAgICAgICAgICAgICAgIHNlbGVjdChtb2RlbCwgbWV0aG9kKSwgYnkgPSAnbW9kZWwnKQpgYGAKCgojIyMgV2hhdCBoYXBwZW5zIGlmIHdlIGNhdGVnb3JpemUgbW9kZWxzIGJ5IHRoZWlyIGVuZCBvZiBjZW50dXJ5IGhlYXQgZmx1eCB2YWx1ZT8gIAoKSG93IGRvZXMgRU9DIGhlYXQgZmx1eCBsb29rIGxpa2U/IAoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTQsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQoKZXNtX2NvbXBhcmlzb24gJT4lIAogICAgZmlsdGVyKHZhcmlhYmxlID09ICdoZWF0Zmx1eCcgJiAhZ3JlcGwoJ2VzbScsIGV4cGVyaW1lbnQpKSAlPiUgCiAgICBmaWx0ZXIoeWVhciA9PSAyMTAwKSAlPiUgIAogICAgZ3JvdXBfYnkoZXhwZXJpbWVudCkgJT4lIAogICAgc3VtbWFyaXNlKGNtaXBfbWluID0gbWVhbihtaW5hKSwgCiAgICAgICAgICAgICAgY21pcF9tYXggPSBtZWFuKG1heGIpKSAlPiUgCiAgICB1bmdyb3VwICU+JSAKICAgIGdhdGhlcihzdGF0LCB2YWx1ZSwgY21pcF9taW4sIGNtaXBfbWF4KSAlPiUgCiAgICBtdXRhdGUoaGVpZ2h0ID0gMSkgLT4gCiAgICBlc21fYm91bmRzCgoKIyBXaGF0IGRvZXMgdGhlIDIwOTAgdG8gMjEwMCBtZWFuIGhlYXQgZmx1eCBhbmQgdGVtcCBsb29rIGxpa2U/PyAKYmVzdF9maXRfb3V0cHV0ICU+JSAKICAgIGZpbHRlcih5ZWFyID09IDIxMDAgKSAlPiUgCiAgICBmaWx0ZXIoZXhwZXJpbWVudCAhPSAnaGlzdG9yaWNhbCcgJiB2YXJpYWJsZSA9PSAnaGVhdGZsdXgnKSAlPiUgCiAgICBncm91cF9ieShtb2RlbCwgZXhwZXJpbWVudCwgdmFyaWFibGUsIG1ldGhvZCkgJT4lIAogICAgc3VtbWFyaXNlKHZhbHVlID0gbWVhbih2YWx1ZSkpICU+JSAKICAgIHVuZ3JvdXAgJT4lIAogICAgbXV0YXRlKGhlaWdodCA9IDEpICU+JSAKICAgIGdncGxvdCgpICsgCiAgICAgICAgICAgIGdlb21fdmxpbmUoZGF0YSA9IGVzbV9ib3VuZHMsIGFlcyh4aW50ZXJjZXB0ID0gdmFsdWUsIGNvbG9yID0gc3RhdCkpICsKICAgIGdlb21faGlzdG9ncmFtKGFlcyh2YWx1ZSkpICsKICAgICNnZW9tX2hpc3RvZ3JhbShhZXModmFsdWUpLCBiaW5zID0gNDApICsgCiAgICAjZ2VvbV9kb3RwbG90KGRhdGEgPSBlc21fYm91bmRzLCBhZXModmFsdWUsIGZpbGwgPSBzdGF0LCBjb2xvciA9IHN0YXQpKSArIAoKICAgIGZhY2V0X2dyaWQodmFyaWFibGUgfiBleHBlcmltZW50KSArIAogICAgbGFicyh0aXRsZSA9ICdFT0MgaGVhdGZsdXgnLCAKICAgICAgICAgeSA9ICdjb3VudCcsIAogICAgICAgICB4ID0gJ2hlYXQgZmx1eCBXL21eMicpIApgYGAKCgpUaGUgdmVydGljYWwgbGluZXMgcmVwcnNlbnQgdGhlIGVzbSBvciBjbWlwIG1pbiBhbmQgbWF4IHJhbmdlIHZhbHVlcy4gVGhlcmUgYXJlIHJjcDYwIGFuZCByY3A4NSBFT0MgaGVhdGZsdXggdmFsdWVzIHRoYXQgZXhjZWVkZSB0aGUgY21pcCByYW5nZSB0aGUgZGlzdHJpYnR1aW9uIG9mIHRoZSBoZWF0IGZsdXggaXMgYnJva2VuIHVwIGluIHJjcDYwIGFuZCByY3AgODUgYnV0IGl0IGlzIG5vdCBvYnZpb3VzIGlmIHRoYXQgaXMgY2x1c2V0ZXJpbmcgb3IgYmVjYXVzZSB0aGVyZSBpcyBzbWFsbCBzYW1wbGUgc2l6ZSBvZiBtb2RlbHMgdXNlZCB0byBtYWtlIHRoZSBoaXN0b2dyYW1zICgzMCBtb2RlbHMgb3Igb2JzZXJ2YXRpb25zKS4gCgoKPGJyPgoKClNvIHdoYXQgaGFwcGVucyBpZiB3ZSBjYXRlZ29yaXplIHRoZSBtb2RlbHMgaW50byB3aGVyZSB0aGV5IGZhbGwgd2l0aGluIHRoZSBJUVI/IFNpbmNlIHRoZXJlIGFyZSBhIGZhaXIgYW1vdW50IG9mIG1vZGVscyB0aGF0IGhhdmUgaGVhdGZsdXggdGhhdCBpcyBzb21ld2hlcmUgY2xvc2UgdG8gdGhlIG1lYW4uIAoKCmBgYHtyLCBmaWcud2lkdGg9MTYsIGZpZy5oZWlnaHQ9OSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9Cgplc21fY29tcGFyaXNvbiAlPiUgCmZpbHRlcih2YXJpYWJsZSA9PSAnaGVhdGZsdXgnICYgIWdyZXBsKCdlc20nLCBleHBlcmltZW50KSkgJT4lICAKZmlsdGVyKHllYXIgPT0gMjEwMCkgJT4lIAogICAgICAgIG11dGF0ZShRMCA9IG1pbmEsIAogICAgICAgICAgICAgICBRMTAwID0gbWF4YiwKICAgICAgICAgICAgICAgUTUwID0gKFEwICsgUTEwMCkgLyAyLCAKICAgICAgICAgICAgICAgUTI1ID0gKFEwICsgUTUwKSAvIDIsIAogICAgICAgICAgICAgICBRNzUgPSAoUTUwICsgUTEwMCkgLyAyKSAlPiUgCiAgICAgICAgc2VsZWN0KHllYXIsIHZhcmlhYmxlLCBleHBlcmltZW50LCBRMCwgUTI1LCBRNTAsIFE3NSwgUTEwMCkgLT4gCiAgICAgICAgcXVhbnRpbGVfZGYKICAgIAoKYmVzdF9maXRfb3V0cHV0ICU+JSAKICAgIGZpbHRlcih5ZWFyID09IDIxMDAgKSAlPiUgCiAgICBmaWx0ZXIoZXhwZXJpbWVudCAhPSAnaGlzdG9yaWNhbCcgJiB2YXJpYWJsZSA9PSAnaGVhdGZsdXgnKSAlPiUgCiAgICAgICAgaW5uZXJfam9pbihxdWFudGlsZV9kZiwgYnkgPSBjKCdleHBlcmltZW50JywgJ3llYXInLCAndmFyaWFibGUnKSkgJT4lIAogICAgICAgIG11dGF0ZShjYXRlZ29yeSA9ICcxc3QgcXVhbnQnKSAlPiUgCiAgICAgICAgbXV0YXRlKGNhdGVnb3J5ID0gaWZfZWxzZSh2YWx1ZSA+PSBRMjUsICcybmQgcXVhbnQnLCBjYXRlZ29yeSkpICU+JSAKICAgICAgICBtdXRhdGUoY2F0ZWdvcnkgPSBpZl9lbHNlKHZhbHVlID49IFE1MCwgJzNuZCBxdWFudCcsIGNhdGVnb3J5KSkgJT4lIAogICAgICAgIG11dGF0ZShjYXRlZ29yeSA9IGlmX2Vsc2UodmFsdWUgPj0gUTc1LCAnNHRoIHF1YW50JywgY2F0ZWdvcnkpKSAlPiUgCiAgICAgICAgc2VsZWN0KG1vZGVsLCBjYXRlZ29yeSwgZXhwZXJpbWVudCkgLT4gCiAgICAgICAgaGVhdF9mbHV4X2NhdGVnb3JpemVkCgp4bWlkIDwtIDAuNSoobWluKGJlc3RfZml0cyRTKSArIG1heChiZXN0X2ZpdHMkUykpCnhybmcgPC0gMS41KihtYXgoYmVzdF9maXRzJFMpIC0gbWluKGJlc3RfZml0cyRTKSkKeGxvIDwtIHhtaWQgLSAwLjUqeHJuZwp4aGkgPC0geG1pZCArIDAuNSp4cm5nCgpiZXN0X2ZpdHMgJT4lICAKICAgIGxlZnRfam9pbihoZWF0X2ZsdXhfY2F0ZWdvcml6ZWQpICU+JSAKICAgIGdncGxvdCgpICsgCiAgICBnZW9tX2xhYmVsKGFlcyhTLCBkaWZmLCBsYWJlbCA9IG1vZGVsLCBmaWxsID0gY2F0ZWdvcnkpKSArIAogICAgbGFicyh0aXRsZSA9ICdFT0MgaGVhdGZsdXgnKSArIAogICAgZmFjZXRfd3JhcCgnZXhwZXJpbWVudCcpICsgCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJykgKyAKICAgIHRoZW1lX2J3KGJhc2Vfc2l6ZSA9IDE0KSArIAogICAgZ2dwbG90Mjo6eGxpbShjKHhsbyx4aGkpKQogICAgICAgIApgYGAKCgpUaGVyZSBpcyBub3QgYW4gb2J2aW91cyBvciBjb25zaXN0ZW50IHBhdHRlcm4gaGVyZSwgYnV0IGl0IGlzIGludGVyZXN0aW5nIHRoYXQgYXMgdGhlIHJjcCBpbmNyZWFzZXMgdGhhdCB0aGUgbW9yZSBvZiB0aGUgbW9kZWxzIGZhbGwgZnVyaHRlciBhd2F5IGZyb20gdGhlIG1lYW4sIHRoZXJlIGFyZSBsZXNzIG1vZGVscyBpbiB0aGUgMm5kIGFuZCAzcmQgcXVuYXRpbGUgcmFuZ2VzLiAKCgoKIyMjIFdoYXQgaGFwcGVucyBpZiB3ZSBkbyB0aGUgUENBIGJ5IHRoZSA0IHBhcm1ldGVycz8gCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMgU3RvcmUgdGhlIGluIGEgZGF0YSBmcmFtZSB3aGVyZSB0aGUgcm93cyBhcmUgbmFtZWQgYnkgdGhlIG1vZGVsIApkZiA8LSBkYXRhLmZyYW1lKFMgPSBiZXN0X2ZpdHMkUywgZGlmZiA9IGJlc3RfZml0cyRkaWZmLCByb3cubmFtZXMgPSBiZXN0X2ZpdHMkbW9kZWwsIAogICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IGJlc3RfZml0cyRhbHBoYSwgdm9sc2NsID0gYmVzdF9maXRzJHZvbHNjbCwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQprXzRwYXJhbXMgPC0ga21lYW5zKGRmLCBjZW50ZXJzID0gNSwgbnN0YXJ0ID0gMjUpCmZ2aXpfY2x1c3RlcihrXzRwYXJhbXMsIGRhdGEgPSBkZiwgYXhlcyA9IGMoMSwgMikpCmBgYAoKQnV0IG5vdyBwbG90IGRpZmYgYW5kIFMgb24gdGhlIGF4c2VzIGdyb3VwZWQgYnkgY2x1c3Rlci4gCgpgYGB7cn0KY2x1c3Rlcl9yc2x0cyA8LSAgZGF0YS5mcmFtZShtb2RlbCA9IG5hbWVzKGtfNHBhcmFtcyRjbHVzdGVyKSwgCiAgICAgICAgICBjbHVzdGVyID0gYXMuY2hhcmFjdGVyKGtfNHBhcmFtcyRjbHVzdGVyKSkKICAgIAoKYmVzdF9maXRzICU+JSAKICAgIGxlZnRfam9pbihjbHVzdGVyX3JzbHRzKSAlPiUgCiAgICBnZ3Bsb3QoYWVzKFMsIGRpZmYsIGxhYmVsID0gbW9kZWwsIGZpbGwgPSBjbHVzdGVyKSkgKyAKICAgIGdlb21fbGFiZWwoKSArIAogICAgdGhlbWVfYncoYmFzZV9zaXplID0gMTQpICsgCiAgICBnZ3Bsb3QyOjp4bGltKGMoeGxvLHhoaSkpICsgCiAgICBsYWJzKHRpdGxlID0gJ2RpZmYgdnMgUyBncm91cGVkIGJ5IGNsdXN0ZXInKQoKYGBgCgojIyMgV2hhdCBoYXBwZW5zIGlmIHdlIGRvIHRoZSBQQ0EgYnkgdGhlIDQgcGFybWV0ZXJzICsgRU9DIGhlYXQgZmx1eCBhbmQgdGVtcCB2YWx1ZXMgZnJvbSByY3A4NT8KCgpgYGB7cn0KYmVzdF9maXRfb3V0cHV0ICU+JSAKICAgIGZpbHRlcih2YXJpYWJsZSAlaW4lIGMoJ2hlYXRmbHV4JywgJ1RnYXYnKSAmIHllYXIgPT0gMjEwMCAmIGV4cGVyaW1lbnQgPT0gJ3JjcDg1JykgJT4lIAogICAgZ3JvdXBfYnkobW9kZWwsIHZhcmlhYmxlKSAlPiUgCiAgICBzdW1tYXJpc2UodmFsdWUgPSBtZWFuKHZhbHVlKSkgJT4lIAogICAgdW5ncm91cCAlPiUgCiAgICBzcHJlYWQodmFyaWFibGUsIHZhbHVlKSAlPiUgCiAgICBzZWxlY3QobW9kZWwsIGhlYXRmbHV4LCBUZ2F2KSAtPiAKICAgIEVPQ19oZgoKIyBTdG9yZSB0aGUgaW4gYSBkYXRhIGZyYW1lIHdoZXJlIHRoZSByb3dzIGFyZSBuYW1lZCBieSB0aGUgbW9kZWwgCmRmIDwtIGRhdGEuZnJhbWUoUyA9IGJlc3RfZml0cyRTLCBkaWZmID0gYmVzdF9maXRzJGRpZmYsIHJvdy5uYW1lcyA9IGJlc3RfZml0cyRtb2RlbCwgCiAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gYmVzdF9maXRzJGFscGhhLCB2b2xzY2wgPSBiZXN0X2ZpdHMkdm9sc2NsLCBtb2RlbCA9IGJlc3RfZml0cyRtb2RlbCwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAlPiUgCiAgICBsZWZ0X2pvaW4oRU9DX2hmKSAlPiUgIAogICAgc2VsZWN0KC1tb2RlbCkKICAgIAogIGRmIDwtIGFzLmRhdGEuZnJhbWUoZGYsIHJvdy5uYW1lcyA9IGJlc3RfZml0cyRtb2RlbCkgIAogICAgCiAgICAKa180cGFyYW1zX2VvY0hGIDwtIGttZWFucyhkZiwgY2VudGVycyA9IDUsIG5zdGFydCA9IDI1KQpmdml6X2NsdXN0ZXIoa180cGFyYW1zX2VvY0hGLCBkYXRhID0gZGYsIGF4ZXMgPSBjKDEsIDIpKQpgYGAKCgpgYGB7cn0KY2x1c3Rlcl9yc2x0czIgPC0gIGRhdGEuZnJhbWUobW9kZWwgPSBuYW1lcyhrXzRwYXJhbXNfZW9jSEYkY2x1c3RlciksIAogICAgICAgICAgY2x1c3RlciA9IGFzLmNoYXJhY3RlcihrXzRwYXJhbXNfZW9jSEYkY2x1c3RlcikpCiAgICAKCmJlc3RfZml0cyAlPiUgCiAgICBsZWZ0X2pvaW4oY2x1c3Rlcl9yc2x0czIpICU+JSAKICAgIGdncGxvdChhZXMoUywgZGlmZiwgbGFiZWwgPSBtb2RlbCwgZmlsbCA9IGNsdXN0ZXIpKSArIAogICAgZ2VvbV9sYWJlbCgpICsgCiAgICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNCkgKyAKICAgIGdncGxvdDI6OnhsaW0oYyh4bG8seGhpKSkgKyAKICAgIGxhYnModGl0bGUgPSAnZGlmZiB2cyBTIGdyb3VwZWQgYnkgY2x1c3RlcicpCgpgYGAKCgoKV2VsbCBpdCBsb29rcyBsaWtlIHRoZSBtb2RlbHMgYXJlIGJyb2tlbiB1cCBpbnRvIHRoZSBzYW1lIGdyb3VwcyBhZ2FpbiBidXQgd2hlbiB3ZSB2aXN1YWxpemUgaXQgdGhlIGdyb3VwcyBhcmUgbW9yZSBvYnZpb3VzLiBJIHdvbmRlciBpZiB3aGF0IGhhcHBlbnMgd2hlbiB3ZSBydW4gSGVjdG9yIHdpdGggdGhlIGRpZmZlcmVudCBjZW50cmlvZCB2YWx1ZXM/IAoKIyMgSGVjdG9yIHdpdGgga21lYW5zIGNlbnRyaW9kIHBhcmFtcwoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NzYWdlID0gRkFMU0V9Cgp0aWJibGU6OmFzX3RpYmJsZShrXzRwYXJhbXNfZW9jSEYkY2VudGVycykgJT4lCiAgICBtdXRhdGUobWV0aG9kID0gJ3BhcmFtcyArIEVPQyAnLCAKICAgICAgICAgICBjbHVzdGVyID0gMTpucm93KGtfNHBhcmFtc19lb2NIRiRjZW50ZXJzKSkgJT4lIAogICAgYmluZF9yb3dzKHRpYmJsZTo6YXNfdGliYmxlKGtfNHBhcmFtcyRjZW50ZXJzKSAlPiUKICAgICAgICAgICAgICAgICAgbXV0YXRlKG1ldGhvZCA9ICdwYXJhbXMnKSAlPiUgCiAgICAgICAgICAgICAgICAgIG11dGF0ZShjbHVzdGVyMSA9IDE6NSkgJT4lIAogICAgICAgICAgICAgICAgICAjIFJlbmFtZSB0aGUgY2x1c3RlcnMgc28gdGhhdCB0aGV5IGFyZSBjb21wYXJhYmxlIHRvIHRoZSBjbHVzdGVyIHJlc3VsdHMgZnJvbSB0aGUgb3RoZXIga21lYW4gbWV0aG9kCiAgICAgICAgICAgICAgICAgIG11dGF0ZShjbHVzdGVyID0gaWZfZWxzZShjbHVzdGVyMSA9PSAyLCA1LCBhcy5kb3VibGUoY2x1c3RlcjEpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyID0gaWZfZWxzZShjbHVzdGVyMSA9PSA0LCAxLCBhcy5kb3VibGUoY2x1c3RlcikpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXIgPSBpZl9lbHNlKGNsdXN0ZXIxID09IDEsIDQsIGFzLmRvdWJsZShjbHVzdGVyKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3RlciA9IGlmX2Vsc2UoY2x1c3RlcjEgPT0gNSwgMiwgYXMuZG91YmxlKGNsdXN0ZXIpKSkpIC0+IAogICAgY2VudGVyX2RmCiAgICAKIyBDcmVhdGUgYSBsaXN0IG9mIHRoZSBoZWN0b3IgY29yZXMgdGhhdCB3ZSBhcmUgZ29pbmcgdG8gd2FudCB0byBydW4uCmhlY3Rvcl9jb3JlcyA8LSBsaXN0KG5ld2NvcmUoc3lzdGVtLmZpbGUoJ2lucHV0L2hlY3Rvcl9yY3A4NV9jb25zdHJhaW5lZC5pbmknLCBwYWNrYWdlID0gJ2hlY3RvcicpLCBuYW1lID0gJ3JjcDg1JykpCgphcHBseShYID0gY2VudGVyX2RmLCBNQVJHSU4gPSAxLCBGVU4gPSBwYXJhbV9ydW5fSGVjdG9yKSAlPiUgCiAgICBiaW5kX3Jvd3MoKSAtPiAKICAgIGNlbnRyYWxfb3V0cHV0CgpgYGAKCkhvdyBtdWNoIGRvIHRoZSBjZW50cmFsIHBvaW50cyBvciB0aGUgcGFyYW10ZXJzIGNoYW5nZSBmb3IgdGhlIGNsdXN0ZXJzIGJhc2VkIG9uIHRoZSBkaWZmZXJudCBtZXRob2RzPyAKYGBge3J9CmNlbnRlcl9kZiAlPiUgCiAgICBzZWxlY3QoUywgZGlmZiwgYWxwaGEsIHZvbHNjbCwgbWV0aG9kLCBjbHVzdGVyKSAlPiUgCiAgICBnYXRoZXIocGFyYW0sIHZhbHVlLCBTLCBkaWZmLCBhbHBoYSwgdm9sc2NsKSAlPiUgIAogICAgZ2dwbG90KGFlcyh2YWx1ZSwgY2x1c3RlciwgY29sb3IgPSBtZXRob2QpKSArIAogICAgZ2VvbV9wb2ludChzaXplID0gMiwgYWxwaGEgPSAwLjcpICsgCiAgICBmYWNldF93cmFwKCdwYXJhbScsIHNjYWxlcyA9ICdmcmVlJykgKyAKICAgIGxhYnModGl0bGUgPSAnY2VudGVyIHBhcmFtIHZhbHVlcyBiYXNlZCBvbiB0aGUgdHdvIGsgbWV0aG9kcycpCgpgYGAKCndoZWxwIGl0IGxvb2tzIGxpa2UgaW5jbHVkaW5nIHRoZSBFT0MgdGVtcCBhbmQgaGVhdCBmbHV4IGRhdGEgZGlkIG5vdCBjaGFuZ2Ugd2VyZSB0aGUga21lYW5zIGNlbnRlcnMgd2VyZSBzbyBpdCBwcm9iYWJseSB3aWxsIG5vdCBpbXBhY3QgdGhlIEhlY3RvciBvdXRwdXQgZWl0aGVyLiAKCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY2VudHJhbF9vdXRwdXQgJT4lIAogICAgICAgICAgIGZpbHRlcihzY2VuYXJpbyA9PSdyY3A4NScpICU+JSAgCiAgICAgICAgICAgbXV0YXRlKG1ldGhvID0gZmFjdG9yKG1ldGhvZCxsZXZlbHMgPSAgYygncGFyYW1zJywgJ3BhcmFtcyArIEVPQycpKSkpICsgCiAgICBnZW9tX2xpbmUoYWVzKHllYXIsIHZhbHVlLCBjb2xvciA9IG1ldGhvZCwgbGluZXR5cGUgPSBtZXRob2QsIGdyb3VwID0gaW50ZXJhY3Rpb24obWV0aG9kLCBjbHVzdGVyLCBzY2VuYXJpbykpLCAKICAgICAgICAgICAgICBzaXplID0gMC43NSkgKyAKICAgIGZhY2V0X2dyaWQodmFyaWFibGUgfiBjbHVzdGVyLCBzY2FsZXMgPSAnZnJlZV95JykKCmBgYAoKIyMgV3JhcCBVcCAKCgpXZWxsIEkgb2ZmaWNhbGx5IGhhdmUgYSBjYXNlIG9mIHRoZSBGcmlkYXkgYnJhaW4gbXVzaCBhbmQgSSBhbSBnb2luZyB0byBoZWFkIG91dC4gSSBuZWVkIHRvIHN0aWxsIGxvb2sgYXQgdGhlIHJlc3VsdHMgYnkgZ3JvdXBpbmcgdGhlIG1vZGVscyB0aGUgcGh5c2ljcyBpbmZvcm1hdGlvbiwgd2hlbiBJIHdhcyBydW5uaW5nIHRoZSBjYWxpYnJhdGlvbnMgZWFybGllciBJIHdhcyBoYXZpbmcgaXNzdWVzIGdldHRpbmcgdGhlIHJ1bnMgdG8gY29udmVyZ2UgOiggYW5kIGl0IGxvb2tzIGxpa2Ugd2hlbiB3ZSBicmVhayB1cCB0aGUgbW9kZWxzIGJ5IHBoeXNpY3Mgd2UgcXVpY2tseSBydW4gb3V0IG9mIG91ciBkYXRhIHJlcXVpcmVtZW50cyAoaGlzdCB0YXMgKyBmdXR1cmUgdGFzKQo=