Dataset

Canadian Wind Turbines from Government of Canada, shared by TidyTuesday.

Contents

Section 1: Data visualization exercise

Section 2: Modeling exercise (turbine capacity prediction)

#load packages
library(tidyverse)
library(tidymodels)
library(doParallel)
library(vip)
library(parttree) 
library(ggsci)
library(wesanderson)
library(psych)

#load data
turbines <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-10-27/wind-turbine.csv')

Section 1: Data visualization

Summary of turbines in a wind farm (project)

turbines %>% group_by(project_name) %>% tally(sort=T) %>% rename(turbine_n=n) %>%  psych::describe(quant=c(.25,.75))
#histogram
#wind_turbine %>% group_by(project_name) %>% tally(sort=T) %>% rename(turbine_n=n) %>% group_by(turbine_n) %>% tally() %>% rename(projects_n=n) %>% ggplot(aes(x=turbine_n, y=projects_n)) + geom_col() + theme_light()

Project capacity by province

(inspired by toeb18)

turbines %>% group_by(province_territory) %>% summarise(project_capacity = sum(total_project_capacity_mw)) %>%
  ggplot(aes(x = province_territory,y = project_capacity)) +  geom_segment(aes(x=reorder(province_territory, project_capacity), xend=province_territory, y=0, yend=project_capacity), color="#6096ba") + geom_point(size=2, color="#277da1") +
  coord_flip() + theme_light() + theme(
    panel.grid.major.x = element_blank(),
    panel.border = element_blank(),
    axis.ticks.x = element_blank(),
    axis.ticks.y = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.minor.y = element_blank()) +
    labs(x= "Canadian Province", y="Cummulative turbine projects capacity in MW", title="Candian Wind Turbines", subtitle="Cummulative project capacity by province")

Correlation between number of turbines and wind farm capacity (inspired by kustav_sen)

turbines %>% group_by(province_territory, project_name) %>% summarise(turbines_n = n(), project_cap = mean(total_project_capacity_mw)) %>% ggplot(aes(x=project_cap, y=turbines_n, color=province_territory)) + geom_point(size=1.5) + facet_wrap(~province_territory) + labs(x= "Number of Turbines", y = "Project Capacity in MW") + theme_minimal() + theme(legend.position="none") + labs(title= "Project capacity and number of turbines by Canadian province") + scale_color_futurama()

Manufacturer

#find top manufacturers
manufacturers <- turbines %>% 
  group_by(manufacturer) %>% 
  summarise(num_turbines = n()) %>%
  mutate(manu_ranking = min_rank(desc(num_turbines))) %>% 
  arrange(desc(num_turbines))

topmanufacturers = as_vector(manufacturers[1:4, 'manufacturer'])
wind_turbine2 = turbines %>% mutate(manufacturer = replace(manufacturer, manufacturer %in% topmanufacturers, "Others"))
wind_turbine2 <- turbines %>% 
  left_join(manufacturers) %>% 
  select(-num_turbines) %>% 
  mutate(manu_ranking = replace(manu_ranking, manufacturer == "Others", 6))

The top four manufacturers with the most turbines are Vestas, GE, Siemens, Enercon

#separate strings in commission date 
wind_turbine2 <- wind_turbine2 %>% 
  separate(commissioning_date, sep = "/", into = c("com_date_1", "com_date_2", "com_date_3")) %>%
  mutate(com_date_1 = as.numeric(com_date_1),
         com_date_2 = as.numeric(com_date_2),
         com_date_3 = as.numeric(com_date_3), 
         most_recent_com_date = pmax(com_date_1, com_date_2, com_date_3, na.rm = TRUE)
         )
#get annual capacity added and annual turbines added
wind_turbine2 <- wind_turbine2 %>% 
  group_by(most_recent_com_date) %>% 
  mutate(annual_capacity_added = sum(turbine_rated_capacity_k_w, na.rm = TRUE),
         annual_turbines_added = n()
         ) %>% 
  ungroup()
#get proportion by manufacturer
wind_turbine2 <-  wind_turbine2 %>% 
  group_by(most_recent_com_date, manufacturer) %>%
  summarise(capacity_added = sum(turbine_rated_capacity_k_w, na.rm = TRUE),
            turbines_added = n(),
            prop_capacity_added = capacity_added / annual_capacity_added,
            prop_turbines_added = turbines_added / annual_turbines_added,
            annual_capacity_added = annual_capacity_added,
            annual_turbines_added = annual_turbines_added,
            manu_ranking = manu_ranking
            ) %>% 
  distinct() %>% 
  ungroup()
#ggplot(wind_turbine2, aes(x=most_recent_com_date, y= annual_turbines_added)) + geom_col(fill="#2a9d8f") + theme_light() + labs(x="Most recent comissioning date", y= "Turbines added", title= "Annual wind turbines added", subtitle = "Canadian wind turbines")
#ggplot(wind_turbine2, aes(x=most_recent_com_date, y= annual_capacity_added)) + geom_col(fill="#15616d") + theme_light() + labs(x="Most recent comissioning date", y= "Capacity added", title= "Annual capacity added", subtitle = "Canadian wind turbines")

Proportion of the total wind energy generation capacity commissioned in a given year

(inspired by analytics_urban)

wind_turbine2$manu_ranking[wind_turbine2$manu_ranking >4] <- 'Others'
ggplot(wind_turbine2, aes(x = most_recent_com_date, y= prop_capacity_added, fill=factor(manu_ranking, labels=c("1:Vestas", "2:GE", "3:Siemens", "4:Enercon", "Others")))) + geom_col()  + scale_fill_jama() + theme_light() + scale_x_continuous(breaks = seq(from=1993, to=2019, by=5)) + labs(fill="Manufacturer", title= "Canadian wind turbines: Four largest manufacturers", subtitle="Proportion of the total wind energy generation capacity commissioned in a given year", x="", y= "Proportion") + theme(
    panel.grid.major.x = element_blank(),
    panel.border = element_blank(),
    axis.ticks.x = element_blank(),
    axis.ticks.y = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.minor.y = element_blank())

Total capacity commissioned across the years

(inspired by analytics_urban)

ggplot(wind_turbine2, aes(x=most_recent_com_date, y=annual_capacity_added)) + geom_line(color="slategrey") + geom_point(color="white",shape=24,aes(fill=annual_capacity_added, size=3)) + labs(y="Total capacity commissioned (megawatts)", x="", title="Canadian wind turbines",subtitle="Total capacity commissioned across the years") + theme_light() + scale_x_continuous(breaks = seq(from=1993, to=2019, by=5)) + theme(
    panel.grid.major.x = element_blank(),
    panel.border = element_blank(),
    axis.ticks.x = element_blank(),
    axis.ticks.y = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.minor.y = element_blank(),
    legend.position="none") + scale_fill_viridis(option="cividis")

Section 2: Modeling

Clean data

turbines_df <- turbines %>%
  transmute(
    turbine_capacity = turbine_rated_capacity_k_w,
    rotor_diameter_m,
    hub_height_m,
    commissioning_date = parse_number(commissioning_date),
    province_territory = fct_lump_n(province_territory, 10),
    model = fct_lump_n(model, 10)
  ) %>%
  filter(!is.na(turbine_capacity)) %>%
  mutate_if(is.character, factor)

Visualize capacity with year, hub height and rotor diameter

turbines_df %>%
  select(turbine_capacity:commissioning_date) %>%
  pivot_longer(rotor_diameter_m:commissioning_date) %>%
  ggplot(aes(turbine_capacity, value)) +
  geom_hex(bins = 15, alpha = 0.8) +
  geom_smooth(method = "lm",color="orange") +
  facet_wrap(~name, scales = "free_y") +
  labs(y = NULL) +
  scale_fill_gradient(high = "olivedrab3") + theme_minimal()

Partition data

set.seed(123)
wind_split <- initial_split(turbines_df, strata = turbine_capacity)
wind_train <- training(wind_split)
wind_test <- testing(wind_split)

set.seed(234)
wind_folds <- vfold_cv(wind_train, strata = turbine_capacity)
wind_folds
#  10-fold cross-validation using stratification 

Decision tree base model

tree_spec <- decision_tree(
  cost_complexity = tune(),
  tree_depth = tune(),
  min_n = tune()
) %>%
  set_engine("rpart") %>%
  set_mode("regression")

tree_spec
Decision Tree Model Specification (regression)

Main Arguments:
  cost_complexity = tune()
  tree_depth = tune()
  min_n = tune()

Computational engine: rpart 

Grid

tree_grid <- grid_regular(cost_complexity(), tree_depth(), min_n(), levels = 4)
tree_grid

Tune model specs

doParallel::registerDoParallel()

set.seed(345)
tree_rs <- tune_grid(
  tree_spec,
  turbine_capacity ~ .,
  resamples = wind_folds,
  grid = tree_grid,
  metrics = metric_set(rmse, rsq, mae, mape)
)

Attaching package: ‘rlang’

The following objects are masked from ‘package:purrr’:

    %@%, as_function, flatten, flatten_chr, flatten_dbl, flatten_int, flatten_lgl, flatten_raw, invoke,
    list_along, modify, prepend, splice


Attaching package: ‘vctrs’

The following object is masked from ‘package:dplyr’:

    data_frame

The following object is masked from ‘package:tibble’:

    data_frame


Attaching package: ‘rpart’

The following object is masked from ‘package:dials’:

    prune
tree_rs
# Tuning results
# 10-fold cross-validation using stratification 

Model evaluation

collect_metrics(tree_rs)
autoplot(tree_rs) + theme_minimal() + scale_color_jco()

show_best(tree_rs, "mape")
select_best(tree_rs, "rmse")

Finalize tree

final_tree <- finalize_model(tree_spec, select_best(tree_rs, "rmse"))
final_tree
Decision Tree Model Specification (regression)

Main Arguments:
  cost_complexity = 1e-10
  tree_depth = 15
  min_n = 2

Computational engine: rpart 

Fit and predict

final_fit <- fit(final_tree, turbine_capacity ~ ., wind_train)
final_rs <- last_fit(final_tree, turbine_capacity ~ ., wind_split)

predict(final_fit, wind_train[144, ])
predict(final_rs$.workflow[[1]], wind_train[144, ])

Variable importance

final_fit %>%
  vip(geom = "col", aesthetics = list(fill = "#264653", alpha = 0.8)) +
  scale_y_continuous(expand = c(0, 0)) + theme_minimal()

Visualize tree results

ex_fit <- fit(
  final_tree,
  turbine_capacity ~ rotor_diameter_m + commissioning_date,
  wind_train
)

pal <- wes_palette("Zissou1", 100, type = "continuous")

wind_train %>%
  ggplot(aes(rotor_diameter_m, commissioning_date)) +
  geom_parttree(data = ex_fit, aes(fill = turbine_capacity), alpha = 0.3) +
  geom_jitter(alpha = 0.7, width = 1, height = 0.5, aes(color = turbine_capacity)) +
  scale_fill_gradientn(aesthetics = c("color", "fill"),colors=pal)

collect_metrics(final_rs)

final_rs %>%
  collect_predictions() %>%
  ggplot(aes(turbine_capacity, .pred)) +
  geom_abline(slope = 1, lty = 2, color = "gray50", alpha = 0.5) +
  geom_point(alpha = 0.6, color = "#006d77") +
  coord_fixed() + theme_minimal()

LS0tCnRpdGxlOiAiV2luZCBUdXJiaW5lcyAtIEVEQSBhbmQgTW9kZWxpbmcgRXhlcmNpc2UiCmRhdGU6ICJEZWMgMjAyMCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMjIERhdGFzZXQgCltDYW5hZGlhbiBXaW5kIFR1cmJpbmVzXShodHRwczovL2dpdGh1Yi5jb20vcmZvcmRhdGFzY2llbmNlL3RpZHl0dWVzZGF5L2Jsb2IvbWFzdGVyL2RhdGEvMjAyMC8yMDIwLTEwLTI3L3JlYWRtZS5tZCkgZnJvbSBbR292ZXJubWVudCBvZiBDYW5hZGFdKGh0dHBzOi8vb3Blbi5jYW5hZGEuY2EvZGF0YS9lbi9kYXRhc2V0Lzc5ZmRhZDkzLTkwMjUtNDlhZC1iYTE2LWMyNmQ3MThjYzA3MCksIHNoYXJlZCBieSBUaWR5VHVlc2RheS4KCiMjIyBDb250ZW50cwoKU2VjdGlvbiAxOiBEYXRhIHZpc3VhbGl6YXRpb24gZXhlcmNpc2UKClNlY3Rpb24gMjogTW9kZWxpbmcgZXhlcmNpc2UgKHR1cmJpbmUgY2FwYWNpdHkgcHJlZGljdGlvbikgICAKCiAgKiByZWZlcmVuY2U6IFtKdWxpYSBTaWxnZSdzIHRpZHltb2RlbHMgdHV0b3JpYWxdKGh0dHBzOi8vanVsaWFzaWxnZS5jb20vYmxvZy93aW5kLXR1cmJpbmUvKSAKCgpgYGB7ciBzZXR1cCwgbWVzc2FnZT1GQUxTRX0KI2xvYWQgcGFja2FnZXMKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeW1vZGVscykKbGlicmFyeShkb1BhcmFsbGVsKQpsaWJyYXJ5KHZpcCkKbGlicmFyeShwYXJ0dHJlZSkgCmxpYnJhcnkoZ2dzY2kpCmxpYnJhcnkod2VzYW5kZXJzb24pCmxpYnJhcnkocHN5Y2gpCgojbG9hZCBkYXRhCnR1cmJpbmVzIDwtIHJlYWRyOjpyZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIwLzIwMjAtMTAtMjcvd2luZC10dXJiaW5lLmNzdicpCmBgYAoKIyMjIFNlY3Rpb24gMTogRGF0YSB2aXN1YWxpemF0aW9uCiMjIyMgU3VtbWFyeSBvZiB0dXJiaW5lcyBpbiBhIHdpbmQgZmFybSAocHJvamVjdCkKYGBge3J9CnR1cmJpbmVzICU+JSBncm91cF9ieShwcm9qZWN0X25hbWUpICU+JSB0YWxseShzb3J0PVQpICU+JSByZW5hbWUodHVyYmluZV9uPW4pICU+JSAgcHN5Y2g6OmRlc2NyaWJlKHF1YW50PWMoLjI1LC43NSkpCiNoaXN0b2dyYW0KI3dpbmRfdHVyYmluZSAlPiUgZ3JvdXBfYnkocHJvamVjdF9uYW1lKSAlPiUgdGFsbHkoc29ydD1UKSAlPiUgcmVuYW1lKHR1cmJpbmVfbj1uKSAlPiUgZ3JvdXBfYnkodHVyYmluZV9uKSAlPiUgdGFsbHkoKSAlPiUgcmVuYW1lKHByb2plY3RzX249bikgJT4lIGdncGxvdChhZXMoeD10dXJiaW5lX24sIHk9cHJvamVjdHNfbikpICsgZ2VvbV9jb2woKSArIHRoZW1lX2xpZ2h0KCkKYGBgCgojIyMjIFByb2plY3QgY2FwYWNpdHkgYnkgcHJvdmluY2UgCihpbnNwaXJlZCBieSB0b2ViMTgpCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CnR1cmJpbmVzICU+JSBncm91cF9ieShwcm92aW5jZV90ZXJyaXRvcnkpICU+JSBzdW1tYXJpc2UocHJvamVjdF9jYXBhY2l0eSA9IHN1bSh0b3RhbF9wcm9qZWN0X2NhcGFjaXR5X213KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcHJvdmluY2VfdGVycml0b3J5LHkgPSBwcm9qZWN0X2NhcGFjaXR5KSkgKyAgZ2VvbV9zZWdtZW50KGFlcyh4PXJlb3JkZXIocHJvdmluY2VfdGVycml0b3J5LCBwcm9qZWN0X2NhcGFjaXR5KSwgeGVuZD1wcm92aW5jZV90ZXJyaXRvcnksIHk9MCwgeWVuZD1wcm9qZWN0X2NhcGFjaXR5KSwgY29sb3I9IiM2MDk2YmEiKSArIGdlb21fcG9pbnQoc2l6ZT0yLCBjb2xvcj0iIzI3N2RhMSIpICsKICBjb29yZF9mbGlwKCkgKyB0aGVtZV9saWdodCgpICsgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGxhYnMoeD0gIkNhbmFkaWFuIFByb3ZpbmNlIiwgeT0iQ3VtbXVsYXRpdmUgdHVyYmluZSBwcm9qZWN0cyBjYXBhY2l0eSBpbiBNVyIsIHRpdGxlPSJDYW5kaWFuIFdpbmQgVHVyYmluZXMiLCBzdWJ0aXRsZT0iQ3VtbXVsYXRpdmUgcHJvamVjdCBjYXBhY2l0eSBieSBwcm92aW5jZSIpCmBgYAoKIyMjIyBDb3JyZWxhdGlvbiBiZXR3ZWVuIG51bWJlciBvZiB0dXJiaW5lcyBhbmQgd2luZCBmYXJtIGNhcGFjaXR5IChpbnNwaXJlZCBieSBrdXN0YXZfc2VuKQpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQp0dXJiaW5lcyAlPiUgZ3JvdXBfYnkocHJvdmluY2VfdGVycml0b3J5LCBwcm9qZWN0X25hbWUpICU+JSBzdW1tYXJpc2UodHVyYmluZXNfbiA9IG4oKSwgcHJvamVjdF9jYXAgPSBtZWFuKHRvdGFsX3Byb2plY3RfY2FwYWNpdHlfbXcpKSAlPiUgZ2dwbG90KGFlcyh4PXByb2plY3RfY2FwLCB5PXR1cmJpbmVzX24sIGNvbG9yPXByb3ZpbmNlX3RlcnJpdG9yeSkpICsgZ2VvbV9wb2ludChzaXplPTEuNSkgKyBmYWNldF93cmFwKH5wcm92aW5jZV90ZXJyaXRvcnkpICsgbGFicyh4PSAiTnVtYmVyIG9mIFR1cmJpbmVzIiwgeSA9ICJQcm9qZWN0IENhcGFjaXR5IGluIE1XIikgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIGxhYnModGl0bGU9ICJQcm9qZWN0IGNhcGFjaXR5IGFuZCBudW1iZXIgb2YgdHVyYmluZXMgYnkgQ2FuYWRpYW4gcHJvdmluY2UiKSArIHNjYWxlX2NvbG9yX2Z1dHVyYW1hKCkKYGBgCgojIyMjIE1hbnVmYWN0dXJlcgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQojZmluZCB0b3AgbWFudWZhY3R1cmVycwptYW51ZmFjdHVyZXJzIDwtIHR1cmJpbmVzICU+JSAKICBncm91cF9ieShtYW51ZmFjdHVyZXIpICU+JSAKICBzdW1tYXJpc2UobnVtX3R1cmJpbmVzID0gbigpKSAlPiUKICBtdXRhdGUobWFudV9yYW5raW5nID0gbWluX3JhbmsoZGVzYyhudW1fdHVyYmluZXMpKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhudW1fdHVyYmluZXMpKQoKdG9wbWFudWZhY3R1cmVycyA9IGFzX3ZlY3RvcihtYW51ZmFjdHVyZXJzWzE6NCwgJ21hbnVmYWN0dXJlciddKQp3aW5kX3R1cmJpbmUyID0gdHVyYmluZXMgJT4lIG11dGF0ZShtYW51ZmFjdHVyZXIgPSByZXBsYWNlKG1hbnVmYWN0dXJlciwgbWFudWZhY3R1cmVyICVpbiUgdG9wbWFudWZhY3R1cmVycywgIk90aGVycyIpKQp3aW5kX3R1cmJpbmUyIDwtIHR1cmJpbmVzICU+JSAKICBsZWZ0X2pvaW4obWFudWZhY3R1cmVycykgJT4lIAogIHNlbGVjdCgtbnVtX3R1cmJpbmVzKSAlPiUgCiAgbXV0YXRlKG1hbnVfcmFua2luZyA9IHJlcGxhY2UobWFudV9yYW5raW5nLCBtYW51ZmFjdHVyZXIgPT0gIk90aGVycyIsIDYpKQpgYGAKVGhlIHRvcCBmb3VyIG1hbnVmYWN0dXJlcnMgd2l0aCB0aGUgbW9zdCB0dXJiaW5lcyBhcmUgVmVzdGFzLCBHRSwgU2llbWVucywgRW5lcmNvbgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsbWVzc2FnZSA9IEZBTFNFfQojc2VwYXJhdGUgc3RyaW5ncyBpbiBjb21taXNzaW9uIGRhdGUgCndpbmRfdHVyYmluZTIgPC0gd2luZF90dXJiaW5lMiAlPiUgCiAgc2VwYXJhdGUoY29tbWlzc2lvbmluZ19kYXRlLCBzZXAgPSAiLyIsIGludG8gPSBjKCJjb21fZGF0ZV8xIiwgImNvbV9kYXRlXzIiLCAiY29tX2RhdGVfMyIpKSAlPiUKICBtdXRhdGUoY29tX2RhdGVfMSA9IGFzLm51bWVyaWMoY29tX2RhdGVfMSksCiAgICAgICAgIGNvbV9kYXRlXzIgPSBhcy5udW1lcmljKGNvbV9kYXRlXzIpLAogICAgICAgICBjb21fZGF0ZV8zID0gYXMubnVtZXJpYyhjb21fZGF0ZV8zKSwgCiAgICAgICAgIG1vc3RfcmVjZW50X2NvbV9kYXRlID0gcG1heChjb21fZGF0ZV8xLCBjb21fZGF0ZV8yLCBjb21fZGF0ZV8zLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICkKI2dldCBhbm51YWwgY2FwYWNpdHkgYWRkZWQgYW5kIGFubnVhbCB0dXJiaW5lcyBhZGRlZAp3aW5kX3R1cmJpbmUyIDwtIHdpbmRfdHVyYmluZTIgJT4lIAogIGdyb3VwX2J5KG1vc3RfcmVjZW50X2NvbV9kYXRlKSAlPiUgCiAgbXV0YXRlKGFubnVhbF9jYXBhY2l0eV9hZGRlZCA9IHN1bSh0dXJiaW5lX3JhdGVkX2NhcGFjaXR5X2tfdywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgYW5udWFsX3R1cmJpbmVzX2FkZGVkID0gbigpCiAgICAgICAgICkgJT4lIAogIHVuZ3JvdXAoKQojZ2V0IHByb3BvcnRpb24gYnkgbWFudWZhY3R1cmVyCndpbmRfdHVyYmluZTIgPC0gIHdpbmRfdHVyYmluZTIgJT4lIAogIGdyb3VwX2J5KG1vc3RfcmVjZW50X2NvbV9kYXRlLCBtYW51ZmFjdHVyZXIpICU+JQogIHN1bW1hcmlzZShjYXBhY2l0eV9hZGRlZCA9IHN1bSh0dXJiaW5lX3JhdGVkX2NhcGFjaXR5X2tfdywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgdHVyYmluZXNfYWRkZWQgPSBuKCksCiAgICAgICAgICAgIHByb3BfY2FwYWNpdHlfYWRkZWQgPSBjYXBhY2l0eV9hZGRlZCAvIGFubnVhbF9jYXBhY2l0eV9hZGRlZCwKICAgICAgICAgICAgcHJvcF90dXJiaW5lc19hZGRlZCA9IHR1cmJpbmVzX2FkZGVkIC8gYW5udWFsX3R1cmJpbmVzX2FkZGVkLAogICAgICAgICAgICBhbm51YWxfY2FwYWNpdHlfYWRkZWQgPSBhbm51YWxfY2FwYWNpdHlfYWRkZWQsCiAgICAgICAgICAgIGFubnVhbF90dXJiaW5lc19hZGRlZCA9IGFubnVhbF90dXJiaW5lc19hZGRlZCwKICAgICAgICAgICAgbWFudV9yYW5raW5nID0gbWFudV9yYW5raW5nCiAgICAgICAgICAgICkgJT4lIAogIGRpc3RpbmN0KCkgJT4lIAogIHVuZ3JvdXAoKQojZ2dwbG90KHdpbmRfdHVyYmluZTIsIGFlcyh4PW1vc3RfcmVjZW50X2NvbV9kYXRlLCB5PSBhbm51YWxfdHVyYmluZXNfYWRkZWQpKSArIGdlb21fY29sKGZpbGw9IiMyYTlkOGYiKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHg9Ik1vc3QgcmVjZW50IGNvbWlzc2lvbmluZyBkYXRlIiwgeT0gIlR1cmJpbmVzIGFkZGVkIiwgdGl0bGU9ICJBbm51YWwgd2luZCB0dXJiaW5lcyBhZGRlZCIsIHN1YnRpdGxlID0gIkNhbmFkaWFuIHdpbmQgdHVyYmluZXMiKQojZ2dwbG90KHdpbmRfdHVyYmluZTIsIGFlcyh4PW1vc3RfcmVjZW50X2NvbV9kYXRlLCB5PSBhbm51YWxfY2FwYWNpdHlfYWRkZWQpKSArIGdlb21fY29sKGZpbGw9IiMxNTYxNmQiKSArIHRoZW1lX2xpZ2h0KCkgKyBsYWJzKHg9Ik1vc3QgcmVjZW50IGNvbWlzc2lvbmluZyBkYXRlIiwgeT0gIkNhcGFjaXR5IGFkZGVkIiwgdGl0bGU9ICJBbm51YWwgY2FwYWNpdHkgYWRkZWQiLCBzdWJ0aXRsZSA9ICJDYW5hZGlhbiB3aW5kIHR1cmJpbmVzIikKYGBgCgojIyMjIFByb3BvcnRpb24gb2YgdGhlIHRvdGFsIHdpbmQgZW5lcmd5IGdlbmVyYXRpb24gY2FwYWNpdHkgY29tbWlzc2lvbmVkIGluIGEgZ2l2ZW4geWVhciAKKGluc3BpcmVkIGJ5IGFuYWx5dGljc191cmJhbikKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQp3aW5kX3R1cmJpbmUyJG1hbnVfcmFua2luZ1t3aW5kX3R1cmJpbmUyJG1hbnVfcmFua2luZyA+NF0gPC0gJ090aGVycycKZ2dwbG90KHdpbmRfdHVyYmluZTIsIGFlcyh4ID0gbW9zdF9yZWNlbnRfY29tX2RhdGUsIHk9IHByb3BfY2FwYWNpdHlfYWRkZWQsIGZpbGw9ZmFjdG9yKG1hbnVfcmFua2luZywgbGFiZWxzPWMoIjE6VmVzdGFzIiwgIjI6R0UiLCAiMzpTaWVtZW5zIiwgIjQ6RW5lcmNvbiIsICJPdGhlcnMiKSkpKSArIGdlb21fY29sKCkgICsgc2NhbGVfZmlsbF9qYW1hKCkgKyB0aGVtZV9saWdodCgpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShmcm9tPTE5OTMsIHRvPTIwMTksIGJ5PTUpKSArIGxhYnMoZmlsbD0iTWFudWZhY3R1cmVyIiwgdGl0bGU9ICJDYW5hZGlhbiB3aW5kIHR1cmJpbmVzOiBGb3VyIGxhcmdlc3QgbWFudWZhY3R1cmVycyIsIHN1YnRpdGxlPSJQcm9wb3J0aW9uIG9mIHRoZSB0b3RhbCB3aW5kIGVuZXJneSBnZW5lcmF0aW9uIGNhcGFjaXR5IGNvbW1pc3Npb25lZCBpbiBhIGdpdmVuIHllYXIiLCB4PSIiLCB5PSAiUHJvcG9ydGlvbiIpICsgdGhlbWUoCiAgICBwYW5lbC5ncmlkLm1ham9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMjIyBUb3RhbCBjYXBhY2l0eSBjb21taXNzaW9uZWQgYWNyb3NzIHRoZSB5ZWFycyAKKGluc3BpcmVkIGJ5IGFuYWx5dGljc191cmJhbikKYGBge3J9CmdncGxvdCh3aW5kX3R1cmJpbmUyLCBhZXMoeD1tb3N0X3JlY2VudF9jb21fZGF0ZSwgeT1hbm51YWxfY2FwYWNpdHlfYWRkZWQpKSArIGdlb21fbGluZShjb2xvcj0ic2xhdGVncmV5IikgKyBnZW9tX3BvaW50KGNvbG9yPSJ3aGl0ZSIsc2hhcGU9MjQsYWVzKGZpbGw9YW5udWFsX2NhcGFjaXR5X2FkZGVkLCBzaXplPTMpKSArIGxhYnMoeT0iVG90YWwgY2FwYWNpdHkgY29tbWlzc2lvbmVkIChtZWdhd2F0dHMpIiwgeD0iIiwgdGl0bGU9IkNhbmFkaWFuIHdpbmQgdHVyYmluZXMiLHN1YnRpdGxlPSJUb3RhbCBjYXBhY2l0eSBjb21taXNzaW9uZWQgYWNyb3NzIHRoZSB5ZWFycyIpICsgdGhlbWVfbGlnaHQoKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoZnJvbT0xOTkzLCB0bz0yMDE5LCBieT01KSkgKyB0aGVtZSgKICAgIHBhbmVsLmdyaWQubWFqb3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsgc2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbj0iY2l2aWRpcyIpCmBgYAoKCiMjIyBTZWN0aW9uIDI6IE1vZGVsaW5nCgojIyMjIENsZWFuIGRhdGEKYGBge3J9CnR1cmJpbmVzX2RmIDwtIHR1cmJpbmVzICU+JQogIHRyYW5zbXV0ZSgKICAgIHR1cmJpbmVfY2FwYWNpdHkgPSB0dXJiaW5lX3JhdGVkX2NhcGFjaXR5X2tfdywKICAgIHJvdG9yX2RpYW1ldGVyX20sCiAgICBodWJfaGVpZ2h0X20sCiAgICBjb21taXNzaW9uaW5nX2RhdGUgPSBwYXJzZV9udW1iZXIoY29tbWlzc2lvbmluZ19kYXRlKSwKICAgIHByb3ZpbmNlX3RlcnJpdG9yeSA9IGZjdF9sdW1wX24ocHJvdmluY2VfdGVycml0b3J5LCAxMCksCiAgICBtb2RlbCA9IGZjdF9sdW1wX24obW9kZWwsIDEwKQogICkgJT4lCiAgZmlsdGVyKCFpcy5uYSh0dXJiaW5lX2NhcGFjaXR5KSkgJT4lCiAgbXV0YXRlX2lmKGlzLmNoYXJhY3RlciwgZmFjdG9yKQpgYGAKCiMjIyMgVmlzdWFsaXplIGNhcGFjaXR5IHdpdGggeWVhciwgaHViIGhlaWdodCBhbmQgcm90b3IgZGlhbWV0ZXIKYGBge3IsIGZpZy53aWR0aD01LGZpZy5oZWlnaHQ9MS44fQp0dXJiaW5lc19kZiAlPiUKICBzZWxlY3QodHVyYmluZV9jYXBhY2l0eTpjb21taXNzaW9uaW5nX2RhdGUpICU+JQogIHBpdm90X2xvbmdlcihyb3Rvcl9kaWFtZXRlcl9tOmNvbW1pc3Npb25pbmdfZGF0ZSkgJT4lCiAgZ2dwbG90KGFlcyh0dXJiaW5lX2NhcGFjaXR5LCB2YWx1ZSkpICsKICBnZW9tX2hleChiaW5zID0gMTUsIGFscGhhID0gMC44KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIixjb2xvcj0ib3JhbmdlIikgKwogIGZhY2V0X3dyYXAofm5hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgbGFicyh5ID0gTlVMTCkgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnQoaGlnaCA9ICJvbGl2ZWRyYWIzIikgKyB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyMjIFBhcnRpdGlvbiBkYXRhCmBgYHtyfQpzZXQuc2VlZCgxMjMpCndpbmRfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdCh0dXJiaW5lc19kZiwgc3RyYXRhID0gdHVyYmluZV9jYXBhY2l0eSkKd2luZF90cmFpbiA8LSB0cmFpbmluZyh3aW5kX3NwbGl0KQp3aW5kX3Rlc3QgPC0gdGVzdGluZyh3aW5kX3NwbGl0KQoKc2V0LnNlZWQoMjM0KQp3aW5kX2ZvbGRzIDwtIHZmb2xkX2N2KHdpbmRfdHJhaW4sIHN0cmF0YSA9IHR1cmJpbmVfY2FwYWNpdHkpCndpbmRfZm9sZHMKYGBgCgojIyMjIERlY2lzaW9uIHRyZWUgYmFzZSBtb2RlbApgYGB7cn0KdHJlZV9zcGVjIDwtIGRlY2lzaW9uX3RyZWUoCiAgY29zdF9jb21wbGV4aXR5ID0gdHVuZSgpLAogIHRyZWVfZGVwdGggPSB0dW5lKCksCiAgbWluX24gPSB0dW5lKCkKKSAlPiUKICBzZXRfZW5naW5lKCJycGFydCIpICU+JQogIHNldF9tb2RlKCJyZWdyZXNzaW9uIikKCnRyZWVfc3BlYwpgYGAKCiMjIyMgR3JpZApgYGB7cn0KdHJlZV9ncmlkIDwtIGdyaWRfcmVndWxhcihjb3N0X2NvbXBsZXhpdHkoKSwgdHJlZV9kZXB0aCgpLCBtaW5fbigpLCBsZXZlbHMgPSA0KQp0cmVlX2dyaWQKYGBgCgojIyMjIFR1bmUgbW9kZWwgc3BlY3MKYGBge3J9CmRvUGFyYWxsZWw6OnJlZ2lzdGVyRG9QYXJhbGxlbCgpCgpzZXQuc2VlZCgzNDUpCnRyZWVfcnMgPC0gdHVuZV9ncmlkKAogIHRyZWVfc3BlYywKICB0dXJiaW5lX2NhcGFjaXR5IH4gLiwKICByZXNhbXBsZXMgPSB3aW5kX2ZvbGRzLAogIGdyaWQgPSB0cmVlX2dyaWQsCiAgbWV0cmljcyA9IG1ldHJpY19zZXQocm1zZSwgcnNxLCBtYWUsIG1hcGUpCikKCnRyZWVfcnMKYGBgCgojIyMjIE1vZGVsIGV2YWx1YXRpb24KYGBge3J9CmNvbGxlY3RfbWV0cmljcyh0cmVlX3JzKQphdXRvcGxvdCh0cmVlX3JzKSArIHRoZW1lX21pbmltYWwoKSArIHNjYWxlX2NvbG9yX2pjbygpCmBgYApgYGB7cn0Kc2hvd19iZXN0KHRyZWVfcnMsICJtYXBlIikKc2VsZWN0X2Jlc3QodHJlZV9ycywgInJtc2UiKQpgYGAKCiMjIyMgRmluYWxpemUgdHJlZQpgYGB7cn0KZmluYWxfdHJlZSA8LSBmaW5hbGl6ZV9tb2RlbCh0cmVlX3NwZWMsIHNlbGVjdF9iZXN0KHRyZWVfcnMsICJybXNlIikpCmZpbmFsX3RyZWUKYGBgCgojIyMjIEZpdCBhbmQgcHJlZGljdCAKYGBge3J9CmZpbmFsX2ZpdCA8LSBmaXQoZmluYWxfdHJlZSwgdHVyYmluZV9jYXBhY2l0eSB+IC4sIHdpbmRfdHJhaW4pCmZpbmFsX3JzIDwtIGxhc3RfZml0KGZpbmFsX3RyZWUsIHR1cmJpbmVfY2FwYWNpdHkgfiAuLCB3aW5kX3NwbGl0KQoKcHJlZGljdChmaW5hbF9maXQsIHdpbmRfdHJhaW5bMTQ0LCBdKQpwcmVkaWN0KGZpbmFsX3JzJC53b3JrZmxvd1tbMV1dLCB3aW5kX3RyYWluWzE0NCwgXSkKYGBgCgojIyMjIFZhcmlhYmxlIGltcG9ydGFuY2UKYGBge3J9CmZpbmFsX2ZpdCAlPiUKICB2aXAoZ2VvbSA9ICJjb2wiLCBhZXN0aGV0aWNzID0gbGlzdChmaWxsID0gIiMyNjQ2NTMiLCBhbHBoYSA9IDAuOCkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLCAwKSkgKyB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyMjIFZpc3VhbGl6ZSB0cmVlIHJlc3VsdHMKYGBge3J9CmV4X2ZpdCA8LSBmaXQoCiAgZmluYWxfdHJlZSwKICB0dXJiaW5lX2NhcGFjaXR5IH4gcm90b3JfZGlhbWV0ZXJfbSArIGNvbW1pc3Npb25pbmdfZGF0ZSwKICB3aW5kX3RyYWluCikKCnBhbCA8LSB3ZXNfcGFsZXR0ZSgiWmlzc291MSIsIDEwMCwgdHlwZSA9ICJjb250aW51b3VzIikKCndpbmRfdHJhaW4gJT4lCiAgZ2dwbG90KGFlcyhyb3Rvcl9kaWFtZXRlcl9tLCBjb21taXNzaW9uaW5nX2RhdGUpKSArCiAgZ2VvbV9wYXJ0dHJlZShkYXRhID0gZXhfZml0LCBhZXMoZmlsbCA9IHR1cmJpbmVfY2FwYWNpdHkpLCBhbHBoYSA9IDAuMykgKwogIGdlb21faml0dGVyKGFscGhhID0gMC43LCB3aWR0aCA9IDEsIGhlaWdodCA9IDAuNSwgYWVzKGNvbG9yID0gdHVyYmluZV9jYXBhY2l0eSkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50bihhZXN0aGV0aWNzID0gYygiY29sb3IiLCAiZmlsbCIpLGNvbG9ycz1wYWwpCmBgYAoKYGBge3J9CmNvbGxlY3RfbWV0cmljcyhmaW5hbF9ycykKCmZpbmFsX3JzICU+JQogIGNvbGxlY3RfcHJlZGljdGlvbnMoKSAlPiUKICBnZ3Bsb3QoYWVzKHR1cmJpbmVfY2FwYWNpdHksIC5wcmVkKSkgKwogIGdlb21fYWJsaW5lKHNsb3BlID0gMSwgbHR5ID0gMiwgY29sb3IgPSAiZ3JheTUwIiwgYWxwaGEgPSAwLjUpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC42LCBjb2xvciA9ICIjMDA2ZDc3IikgKwogIGNvb3JkX2ZpeGVkKCkgKyB0aGVtZV9taW5pbWFsKCkKCmBgYAoK