R Codes creating Plot

# Some nice projects: 1. https://github.com/cnicault/tidytuesday
#                     2. https://github.com/rfordatascience/tidytuesday
#                     3. https://github.com/zhiiiyang/tidytuesday
#                     4. https://github.com/jack-davison/TidyTuesday

# Load some libraries: 

library(tidyverse)
library(glue)
library(Cairo)
library(scales)
library(ggtext)

# Clear workspace: 
rm(list = ls())

# Load data:
transit_cost <- read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2021/2021-01-05/transit_cost.csv')

#-------------------------------------
#  Stage 1: Prepare data for ploting
#-------------------------------------

transit_cost <- transit_cost %>% 
  filter(!is.na(e))

# Parse numeric variables interpreted as character: 

transit_cost %>% 
  mutate(tunnel_per = replace_na(parse_number(tunnel_per), 0), 
         real_cost = parse_number(real_cost), 
         across(start_year:end_year, as.numeric)) %>% 
  filter(length <= 80) -> transit_cost

# Fit linear regression cost ~ length: 

reg <- lm(real_cost ~ length, data = transit_cost)

# Add predicted and error
  
transit_cost %>% 
  mutate(fit = predict(reg, transit_cost), error = real_cost - fit) -> transit_cost

#  Some highlighted projects based on error: 

transit_cost %>% 
  filter(error < quantile(error, 0.005) | error > quantile(error, 0.995)) %>%
  mutate(years_to_complete = replace_na(end_year - start_year, "Unspecified")) %>% 
  select(e, country, city, line, start_year, years_to_complete, tunnel, real_cost, length, fit) -> outliers_error

# Addition of formatted text for tags: 

outliers_error <- outliers_error %>% 
  mutate(text = glue('Project: {line}\nCity: {city} ({country})\nTotal cost: {comma(real_cost, prefix = "$", suffix =  "M")}\nStart: {start_year}\nYears to complete: {years_to_complete}\nDeviation from predicted: {comma(real_cost - fit, prefix = "$", suffix =  "M")}'), 
         hjust = c("right", "right", "right", "right", "left", "left")) 

# Dataframe for segements accompaining text for highlighted projects: 

# tibble(x = c(30, 20, 50, 72, 67, 60), 
#        x_end = c(30, 20, 50, 72, 67, 60), 
#        y_shift = c(-4000, 0, 0, 4000, -2600, -1000), 
#        e = outliers_error$e) -> segments_df


tribble(
  ~x, ~xend, ~y_shift,
  30, 30, -4000,
  20, 20, 0,
  50, 50, 0,
  72, 72, 4000,
  67, 67, -2600,
  60, 60, -1000
) %>% 
  mutate(e = outliers_error$e) -> segments_df


# Aesthetics needed for geom_path: 

outliers_error <- outliers_error %>% 
  left_join(segments_df, by = "e") %>% 
  mutate(y = real_cost - 2300 + y_shift, 
         yend = real_cost + 2300 + y_shift, 
         y2 = real_cost - 300 + y_shift, 
         yend2 = real_cost + 300 + y_shift, 
         x2 = ifelse(hjust == "right", x + 0.3, x - 0.3), 
         x_beggin = ifelse(hjust == "right", length - 0.7, length + 0.7))

# Data frame in format suitable for geom_path: 

paths_df <- outliers_error %>% 
  mutate(x_middle1 = (length + x) / 2, 
         x_middle2 = (length + x) / 2, 
         y_middle1 = real_cost, 
         y_middle2 = (y + yend) / 2,
         y_end = (y + yend) / 2
  ) %>% 
  select(e, x_beggin, x_middle1, x_middle2, x2, real_cost, y_middle1, y_middle2, y_end) %>% 
  rename(x_end = x2, y_beggin = real_cost) %>% 
  pivot_longer(names_to = c(".value", "point"), names_sep = "_", cols = 2:ncol(.))


#----------------------------------
#   Stage 2: Data Visualization
#----------------------------------

library(extrafont)

transit_cost %>% 
  ggplot(aes(length, real_cost)) + 
  geom_point(color = "grey65", alpha = 0.6) + 
  
  # segments connecting fitted line to highlited points: 
  geom_segment(data = outliers_error, aes(x = length, xend = length, y= fit, yend = real_cost - 350), color = "grey25") +
  
  # highglighted points: 
  geom_point(data = outliers_error, color = "#059fff", size = 5, shape = 1, stroke = 1) +
  geom_point(data = outliers_error, color = "white", size = 2.2) +
  
  
  # Add text and dashed segments indicating the increasing in cost ofr every 10 Km: 
  
  annotate(geom = "segment", x = 60, xend = 60,
           y = predict(reg, data.frame(length = 60)),
           yend = predict(reg, data.frame(length = 70)), 
           color = "#bbd1f0", lty = "dashed") +
  
  
  annotate(geom = "segment", x = 60, xend = 70,
           y = predict(reg, data.frame(length = 70)),
           yend = predict(reg, data.frame(length = 70)), 
           color = "#bbd1f0", lty = "dashed") +
  
  annotate(geom = "text", x = 65, y = predict(reg, data.frame(length = 70)) + 900,
           label = glue("Every 10 Km of road increases \n the cost in {comma(reg$coefficients[2], , prefix = '$', suffix =  'M')} on average"),
           color = "#bbd1f0", 
           size = 2.9) +
  
  # Add fitted line: 
  geom_smooth(method = "lm", se = F, color = "#385ee8") +
  
  # Add blue vertical segments next to tag text: 
  geom_segment(data = outliers_error, aes(x = x , xend = xend, y = y, yend = yend), color = "#059fff") +
  geom_segment(data = outliers_error, aes(x = x2, xend = x2, y = y2, yend = yend2), color = "#059fff") +
  
  # Add lines connecting highlighted points: 
  geom_path(data = paths_df, aes(x, y, group = e), color = "white", linejoin = "bevel", linemitre = 1) +
  
  # Text: 
  geom_text(data = outliers_error, aes(ifelse(hjust == "right",x - 0.5, x + 0.5), (y + yend) / 2, label = text, hjust = hjust), color = "white", size = 3) +
  
  labs(x = "Length of the line (Km)", 
       y = "Real cost of the project (millions of $)", 
       title = "THE MOST AND LEAST COSTLY TRANSIT-INFRASTRUCTURE PROJECTS AROUND THE WORLD", 
       subtitle = "Each dot represents a project. Highligthed projects are below the 0.5 percentile or above the 99.5 percentile of the  <span style='color:#385ee8'>predicted cost</span>", 
       caption = "Data comes from the Transit Costs Project. Visualization adapted from Martín Pons") +
  
  scale_x_continuous(breaks = seq(0, 80, by = 10)) +
  scale_y_continuous(labels = comma) +
  
  theme(
    text = element_text(family = "Candara", color = "#9dc6e0"),
    plot.background = element_rect(fill = "grey15"),
    panel.background = element_rect(fill = "grey15"), 
    panel.grid = element_blank(), 
    axis.text = element_text(size = 13, color = "#9dc6e0"), 
    axis.title = element_text(size = 13, color = "#9dc6e0"),
    plot.title = element_text(color = "#ced8f2", size = 21),
    plot.subtitle = element_markdown(color = "#9bb0c9", size = 13),
    plot.caption = element_text(color = "#9bb0c9", size = 10)
  ) + 
  theme(plot.margin = unit(rep(0.7, 4), "cm")) 
LS0tDQp0aXRsZTogJ1RpZHkgVHVlc2RheSBQcm9qZWN0IGZvciBEYXRhIFNjaWVuY2UnDQphdXRob3I6ICdBdXRob3I6IE5ndXllbiBDaGkgRHVuZycNCnN1YnRpdGxlOiAiRGFpbHkgR3JhcGggU2VyaWVzIg0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgIyBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4NCiAgICAjIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6ICJmbGF0bHkiDQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQotLS0NCg0KYGBge3Igc2V0dXAsaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGNhY2hlID0gVFJVRSwgZXZhbCA9IEZBTFNFKQ0KDQpgYGANCg0KDQohW10oQzpcXFVzZXJzXFxBZG1pblxcRG9jdW1lbnRzXFxjb3N0LmpwZykNCg0KIyBSIENvZGVzIGNyZWF0aW5nIFBsb3QNCg0KYGBge3J9DQojIFNvbWUgbmljZSBwcm9qZWN0czogMS4gaHR0cHM6Ly9naXRodWIuY29tL2NuaWNhdWx0L3RpZHl0dWVzZGF5DQojICAgICAgICAgICAgICAgICAgICAgMi4gaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheQ0KIyAgICAgICAgICAgICAgICAgICAgIDMuIGh0dHBzOi8vZ2l0aHViLmNvbS96aGlpaXlhbmcvdGlkeXR1ZXNkYXkNCiMgICAgICAgICAgICAgICAgICAgICA0LiBodHRwczovL2dpdGh1Yi5jb20vamFjay1kYXZpc29uL1RpZHlUdWVzZGF5DQoNCiMgTG9hZCBzb21lIGxpYnJhcmllczogDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShnbHVlKQ0KbGlicmFyeShDYWlybykNCmxpYnJhcnkoc2NhbGVzKQ0KbGlicmFyeShnZ3RleHQpDQoNCiMgQ2xlYXIgd29ya3NwYWNlOiANCnJtKGxpc3QgPSBscygpKQ0KDQojIExvYWQgZGF0YToNCnRyYW5zaXRfY29zdCA8LSByZWFkX2NzdignaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9tYXN0ZXIvZGF0YS8yMDIxLzIwMjEtMDEtMDUvdHJhbnNpdF9jb3N0LmNzdicpDQoNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQojICBTdGFnZSAxOiBQcmVwYXJlIGRhdGEgZm9yIHBsb3RpbmcNCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCnRyYW5zaXRfY29zdCA8LSB0cmFuc2l0X2Nvc3QgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGUpKQ0KDQojIFBhcnNlIG51bWVyaWMgdmFyaWFibGVzIGludGVycHJldGVkIGFzIGNoYXJhY3RlcjogDQoNCnRyYW5zaXRfY29zdCAlPiUgDQogIG11dGF0ZSh0dW5uZWxfcGVyID0gcmVwbGFjZV9uYShwYXJzZV9udW1iZXIodHVubmVsX3BlciksIDApLCANCiAgICAgICAgIHJlYWxfY29zdCA9IHBhcnNlX251bWJlcihyZWFsX2Nvc3QpLCANCiAgICAgICAgIGFjcm9zcyhzdGFydF95ZWFyOmVuZF95ZWFyLCBhcy5udW1lcmljKSkgJT4lIA0KICBmaWx0ZXIobGVuZ3RoIDw9IDgwKSAtPiB0cmFuc2l0X2Nvc3QNCg0KIyBGaXQgbGluZWFyIHJlZ3Jlc3Npb24gY29zdCB+IGxlbmd0aDogDQoNCnJlZyA8LSBsbShyZWFsX2Nvc3QgfiBsZW5ndGgsIGRhdGEgPSB0cmFuc2l0X2Nvc3QpDQoNCiMgQWRkIHByZWRpY3RlZCBhbmQgZXJyb3INCiAgDQp0cmFuc2l0X2Nvc3QgJT4lIA0KICBtdXRhdGUoZml0ID0gcHJlZGljdChyZWcsIHRyYW5zaXRfY29zdCksIGVycm9yID0gcmVhbF9jb3N0IC0gZml0KSAtPiB0cmFuc2l0X2Nvc3QNCg0KIyAgU29tZSBoaWdobGlnaHRlZCBwcm9qZWN0cyBiYXNlZCBvbiBlcnJvcjogDQoNCnRyYW5zaXRfY29zdCAlPiUgDQogIGZpbHRlcihlcnJvciA8IHF1YW50aWxlKGVycm9yLCAwLjAwNSkgfCBlcnJvciA+IHF1YW50aWxlKGVycm9yLCAwLjk5NSkpICU+JQ0KICBtdXRhdGUoeWVhcnNfdG9fY29tcGxldGUgPSByZXBsYWNlX25hKGVuZF95ZWFyIC0gc3RhcnRfeWVhciwgIlVuc3BlY2lmaWVkIikpICU+JSANCiAgc2VsZWN0KGUsIGNvdW50cnksIGNpdHksIGxpbmUsIHN0YXJ0X3llYXIsIHllYXJzX3RvX2NvbXBsZXRlLCB0dW5uZWwsIHJlYWxfY29zdCwgbGVuZ3RoLCBmaXQpIC0+IG91dGxpZXJzX2Vycm9yDQoNCiMgQWRkaXRpb24gb2YgZm9ybWF0dGVkIHRleHQgZm9yIHRhZ3M6IA0KDQpvdXRsaWVyc19lcnJvciA8LSBvdXRsaWVyc19lcnJvciAlPiUgDQogIG11dGF0ZSh0ZXh0ID0gZ2x1ZSgnUHJvamVjdDoge2xpbmV9XG5DaXR5OiB7Y2l0eX0gKHtjb3VudHJ5fSlcblRvdGFsIGNvc3Q6IHtjb21tYShyZWFsX2Nvc3QsIHByZWZpeCA9ICIkIiwgc3VmZml4ID0gICJNIil9XG5TdGFydDoge3N0YXJ0X3llYXJ9XG5ZZWFycyB0byBjb21wbGV0ZToge3llYXJzX3RvX2NvbXBsZXRlfVxuRGV2aWF0aW9uIGZyb20gcHJlZGljdGVkOiB7Y29tbWEocmVhbF9jb3N0IC0gZml0LCBwcmVmaXggPSAiJCIsIHN1ZmZpeCA9ICAiTSIpfScpLCANCiAgICAgICAgIGhqdXN0ID0gYygicmlnaHQiLCAicmlnaHQiLCAicmlnaHQiLCAicmlnaHQiLCAibGVmdCIsICJsZWZ0IikpIA0KDQojIERhdGFmcmFtZSBmb3Igc2VnZW1lbnRzIGFjY29tcGFpbmluZyB0ZXh0IGZvciBoaWdobGlnaHRlZCBwcm9qZWN0czogDQoNCiMgdGliYmxlKHggPSBjKDMwLCAyMCwgNTAsIDcyLCA2NywgNjApLCANCiMgICAgICAgIHhfZW5kID0gYygzMCwgMjAsIDUwLCA3MiwgNjcsIDYwKSwgDQojICAgICAgICB5X3NoaWZ0ID0gYygtNDAwMCwgMCwgMCwgNDAwMCwgLTI2MDAsIC0xMDAwKSwgDQojICAgICAgICBlID0gb3V0bGllcnNfZXJyb3IkZSkgLT4gc2VnbWVudHNfZGYNCg0KDQp0cmliYmxlKA0KICB+eCwgfnhlbmQsIH55X3NoaWZ0LA0KICAzMCwgMzAsIC00MDAwLA0KICAyMCwgMjAsIDAsDQogIDUwLCA1MCwgMCwNCiAgNzIsIDcyLCA0MDAwLA0KICA2NywgNjcsIC0yNjAwLA0KICA2MCwgNjAsIC0xMDAwDQopICU+JSANCiAgbXV0YXRlKGUgPSBvdXRsaWVyc19lcnJvciRlKSAtPiBzZWdtZW50c19kZg0KDQoNCiMgQWVzdGhldGljcyBuZWVkZWQgZm9yIGdlb21fcGF0aDogDQoNCm91dGxpZXJzX2Vycm9yIDwtIG91dGxpZXJzX2Vycm9yICU+JSANCiAgbGVmdF9qb2luKHNlZ21lbnRzX2RmLCBieSA9ICJlIikgJT4lIA0KICBtdXRhdGUoeSA9IHJlYWxfY29zdCAtIDIzMDAgKyB5X3NoaWZ0LCANCiAgICAgICAgIHllbmQgPSByZWFsX2Nvc3QgKyAyMzAwICsgeV9zaGlmdCwgDQogICAgICAgICB5MiA9IHJlYWxfY29zdCAtIDMwMCArIHlfc2hpZnQsIA0KICAgICAgICAgeWVuZDIgPSByZWFsX2Nvc3QgKyAzMDAgKyB5X3NoaWZ0LCANCiAgICAgICAgIHgyID0gaWZlbHNlKGhqdXN0ID09ICJyaWdodCIsIHggKyAwLjMsIHggLSAwLjMpLCANCiAgICAgICAgIHhfYmVnZ2luID0gaWZlbHNlKGhqdXN0ID09ICJyaWdodCIsIGxlbmd0aCAtIDAuNywgbGVuZ3RoICsgMC43KSkNCg0KIyBEYXRhIGZyYW1lIGluIGZvcm1hdCBzdWl0YWJsZSBmb3IgZ2VvbV9wYXRoOiANCg0KcGF0aHNfZGYgPC0gb3V0bGllcnNfZXJyb3IgJT4lIA0KICBtdXRhdGUoeF9taWRkbGUxID0gKGxlbmd0aCArIHgpIC8gMiwgDQogICAgICAgICB4X21pZGRsZTIgPSAobGVuZ3RoICsgeCkgLyAyLCANCiAgICAgICAgIHlfbWlkZGxlMSA9IHJlYWxfY29zdCwgDQogICAgICAgICB5X21pZGRsZTIgPSAoeSArIHllbmQpIC8gMiwNCiAgICAgICAgIHlfZW5kID0gKHkgKyB5ZW5kKSAvIDINCiAgKSAlPiUgDQogIHNlbGVjdChlLCB4X2JlZ2dpbiwgeF9taWRkbGUxLCB4X21pZGRsZTIsIHgyLCByZWFsX2Nvc3QsIHlfbWlkZGxlMSwgeV9taWRkbGUyLCB5X2VuZCkgJT4lIA0KICByZW5hbWUoeF9lbmQgPSB4MiwgeV9iZWdnaW4gPSByZWFsX2Nvc3QpICU+JSANCiAgcGl2b3RfbG9uZ2VyKG5hbWVzX3RvID0gYygiLnZhbHVlIiwgInBvaW50IiksIG5hbWVzX3NlcCA9ICJfIiwgY29scyA9IDI6bmNvbCguKSkNCg0KDQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KIyAgIFN0YWdlIDI6IERhdGEgVmlzdWFsaXphdGlvbg0KIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KbGlicmFyeShleHRyYWZvbnQpDQoNCnRyYW5zaXRfY29zdCAlPiUgDQogIGdncGxvdChhZXMobGVuZ3RoLCByZWFsX2Nvc3QpKSArIA0KICBnZW9tX3BvaW50KGNvbG9yID0gImdyZXk2NSIsIGFscGhhID0gMC42KSArIA0KICANCiAgIyBzZWdtZW50cyBjb25uZWN0aW5nIGZpdHRlZCBsaW5lIHRvIGhpZ2hsaXRlZCBwb2ludHM6IA0KICBnZW9tX3NlZ21lbnQoZGF0YSA9IG91dGxpZXJzX2Vycm9yLCBhZXMoeCA9IGxlbmd0aCwgeGVuZCA9IGxlbmd0aCwgeT0gZml0LCB5ZW5kID0gcmVhbF9jb3N0IC0gMzUwKSwgY29sb3IgPSAiZ3JleTI1IikgKw0KICANCiAgIyBoaWdoZ2xpZ2h0ZWQgcG9pbnRzOiANCiAgZ2VvbV9wb2ludChkYXRhID0gb3V0bGllcnNfZXJyb3IsIGNvbG9yID0gIiMwNTlmZmYiLCBzaXplID0gNSwgc2hhcGUgPSAxLCBzdHJva2UgPSAxKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IG91dGxpZXJzX2Vycm9yLCBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAyLjIpICsNCiAgDQogIA0KICAjIEFkZCB0ZXh0IGFuZCBkYXNoZWQgc2VnbWVudHMgaW5kaWNhdGluZyB0aGUgaW5jcmVhc2luZyBpbiBjb3N0IG9mciBldmVyeSAxMCBLbTogDQogIA0KICBhbm5vdGF0ZShnZW9tID0gInNlZ21lbnQiLCB4ID0gNjAsIHhlbmQgPSA2MCwNCiAgICAgICAgICAgeSA9IHByZWRpY3QocmVnLCBkYXRhLmZyYW1lKGxlbmd0aCA9IDYwKSksDQogICAgICAgICAgIHllbmQgPSBwcmVkaWN0KHJlZywgZGF0YS5mcmFtZShsZW5ndGggPSA3MCkpLCANCiAgICAgICAgICAgY29sb3IgPSAiI2JiZDFmMCIsIGx0eSA9ICJkYXNoZWQiKSArDQogIA0KICANCiAgYW5ub3RhdGUoZ2VvbSA9ICJzZWdtZW50IiwgeCA9IDYwLCB4ZW5kID0gNzAsDQogICAgICAgICAgIHkgPSBwcmVkaWN0KHJlZywgZGF0YS5mcmFtZShsZW5ndGggPSA3MCkpLA0KICAgICAgICAgICB5ZW5kID0gcHJlZGljdChyZWcsIGRhdGEuZnJhbWUobGVuZ3RoID0gNzApKSwgDQogICAgICAgICAgIGNvbG9yID0gIiNiYmQxZjAiLCBsdHkgPSAiZGFzaGVkIikgKw0KICANCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDY1LCB5ID0gcHJlZGljdChyZWcsIGRhdGEuZnJhbWUobGVuZ3RoID0gNzApKSArIDkwMCwNCiAgICAgICAgICAgbGFiZWwgPSBnbHVlKCJFdmVyeSAxMCBLbSBvZiByb2FkIGluY3JlYXNlcyBcbiB0aGUgY29zdCBpbiB7Y29tbWEocmVnJGNvZWZmaWNpZW50c1syXSwgLCBwcmVmaXggPSAnJCcsIHN1ZmZpeCA9ICAnTScpfSBvbiBhdmVyYWdlIiksDQogICAgICAgICAgIGNvbG9yID0gIiNiYmQxZjAiLCANCiAgICAgICAgICAgc2l6ZSA9IDIuOSkgKw0KICANCiAgIyBBZGQgZml0dGVkIGxpbmU6IA0KICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEYsIGNvbG9yID0gIiMzODVlZTgiKSArDQogIA0KICAjIEFkZCBibHVlIHZlcnRpY2FsIHNlZ21lbnRzIG5leHQgdG8gdGFnIHRleHQ6IA0KICBnZW9tX3NlZ21lbnQoZGF0YSA9IG91dGxpZXJzX2Vycm9yLCBhZXMoeCA9IHggLCB4ZW5kID0geGVuZCwgeSA9IHksIHllbmQgPSB5ZW5kKSwgY29sb3IgPSAiIzA1OWZmZiIpICsNCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBvdXRsaWVyc19lcnJvciwgYWVzKHggPSB4MiwgeGVuZCA9IHgyLCB5ID0geTIsIHllbmQgPSB5ZW5kMiksIGNvbG9yID0gIiMwNTlmZmYiKSArDQogIA0KICAjIEFkZCBsaW5lcyBjb25uZWN0aW5nIGhpZ2hsaWdodGVkIHBvaW50czogDQogIGdlb21fcGF0aChkYXRhID0gcGF0aHNfZGYsIGFlcyh4LCB5LCBncm91cCA9IGUpLCBjb2xvciA9ICJ3aGl0ZSIsIGxpbmVqb2luID0gImJldmVsIiwgbGluZW1pdHJlID0gMSkgKw0KICANCiAgIyBUZXh0OiANCiAgZ2VvbV90ZXh0KGRhdGEgPSBvdXRsaWVyc19lcnJvciwgYWVzKGlmZWxzZShoanVzdCA9PSAicmlnaHQiLHggLSAwLjUsIHggKyAwLjUpLCAoeSArIHllbmQpIC8gMiwgbGFiZWwgPSB0ZXh0LCBoanVzdCA9IGhqdXN0KSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMykgKw0KICANCiAgbGFicyh4ID0gIkxlbmd0aCBvZiB0aGUgbGluZSAoS20pIiwgDQogICAgICAgeSA9ICJSZWFsIGNvc3Qgb2YgdGhlIHByb2plY3QgKG1pbGxpb25zIG9mICQpIiwgDQogICAgICAgdGl0bGUgPSAiVEhFIE1PU1QgQU5EIExFQVNUIENPU1RMWSBUUkFOU0lULUlORlJBU1RSVUNUVVJFIFBST0pFQ1RTIEFST1VORCBUSEUgV09STEQiLCANCiAgICAgICBzdWJ0aXRsZSA9ICJFYWNoIGRvdCByZXByZXNlbnRzIGEgcHJvamVjdC4gSGlnaGxpZ3RoZWQgcHJvamVjdHMgYXJlIGJlbG93IHRoZSAwLjUgcGVyY2VudGlsZSBvciBhYm92ZSB0aGUgOTkuNSBwZXJjZW50aWxlIG9mIHRoZSAgPHNwYW4gc3R5bGU9J2NvbG9yOiMzODVlZTgnPnByZWRpY3RlZCBjb3N0PC9zcGFuPiIsIA0KICAgICAgIGNhcHRpb24gPSAiRGF0YSBjb21lcyBmcm9tIHRoZSBUcmFuc2l0IENvc3RzIFByb2plY3QuIFZpc3VhbGl6YXRpb24gYWRhcHRlZCBmcm9tIE1hcnTDrW4gUG9ucyIpICsNCiAgDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgODAsIGJ5ID0gMTApKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKw0KICANCiAgdGhlbWUoDQogICAgdGV4dCA9IGVsZW1lbnRfdGV4dChmYW1pbHkgPSAiQ2FuZGFyYSIsIGNvbG9yID0gIiM5ZGM2ZTAiKSwNCiAgICBwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJncmV5MTUiKSwNCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiZ3JleTE1IiksIA0KICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGNvbG9yID0gIiM5ZGM2ZTAiKSwgDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGNvbG9yID0gIiM5ZGM2ZTAiKSwNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGNvbG9yID0gIiNjZWQ4ZjIiLCBzaXplID0gMjEpLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X21hcmtkb3duKGNvbG9yID0gIiM5YmIwYzkiLCBzaXplID0gMTMpLA0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICIjOWJiMGM5Iiwgc2l6ZSA9IDEwKQ0KICApICsgDQogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChyZXAoMC43LCA0KSwgImNtIikpIA0KDQoNCmBgYA0KDQo=