1. Herkunft der Daten

Die zugrunde liegenden Daten wurden in einer Online-Umfrage im November 2021 erhoben. Das Ziel dieser Umfrage ist es, das Ausmaß von Fahrraddiebstahl in Nordamerika, Europa und dem Vereinigten Königreich einschätzen zu können.

Die Umfrage umfasst 12 Fragen, welche hier unverändert in der aufgelistet sind. Bei der Auswahl der Antwort-Möglichkeiten “Other” beziehungsweise “Others” wird zusätzlich gewünscht, diese Antwort als Freitext zu spezifizieren. Eine Verifizierung der Eingabe bietet das Formular nicht. So ist es beispielsweise möglich anzugeben, das Fahrrad wäre gleichzeitig abgesichert und nicht abgesichert gewesen.

Frage Antwort-Möglichkeiten Mehrfachauswahl Verpflichtend
How many times has your bike been stolen since you started cycling? Never
1
2
3
4
5
6 times or more
Nein Ja
Where was the last bike stolen from? Street
Front garden / yard
Back garden / yard
Shed
Garage
Inside home
Work or Public Authority bike
parking
Nein Nein
What was the bike secured with? It wasn’t locked
Cable lock
U-lock / D-lock
Chain lock
Folding lock
Ja Nein
What security rating did the primary lock have? Don’t know
None
Sold Secure Bronze
Sold Secure Silver
Sold Secure Gold
Sold Secure Diamond
Other
Nein Nein
How much did you spend on your locks? Less than 20
20 - 40
41 - 60
61 - 100
More than 100
Nein Nein
What was the approximate value of the bike? Less than 100
100 - 400
401 - 700
701 - 1000
Over 1000
Nein Nein
Did you have a record of the frame number of the bike? Yes
No
Nein Nein
Did you report the stolen bike to the police? Yes
No
Nein Nein
Did you get the stolen bike back? Yes
No
Nein Nein
How many times have components (wheels, seats, brakes etc) been stolen from your bike? Never
1
2
3
4
5
6 times or more
Nein Ja
Which components have been stolen? Wheels
Seats
Handlebars
Headset
Forks
Brakes
Gears
Lights
Others
Ja Nein
What was the last stolen component attached / secured with? Quick release clamp only
Quick release clamp plus cable
Hex bolt or Standard nut only
Hex bolt or Standard nut plus cable
Security skewers / nuts (Pinhead, Pitlock, Hexlox etc)
Any other type of secondary bike lock
Nein Nein

Als Belohnung wurde den ersten 5000 Teilnehmern eine Gewinnchance auf ein Fahrrad im Wert von bis zu 600 $ in Aussicht gestellt. Eine Mehrfachteilnahme wird nicht verhindert, weshalb es nahe liegt, dass einzelne Teilnehmer mehrfach die Umfrage beantworten, um ihre Gewinnchancen zu steigern.

Neben den Antworten auf die Fragen enthält der Datensatz den Standort der Teilnehmer. Da diese nicht explizit vom Teilnehmer angegeben wird, handelt es sich wahrscheinlich um eine grobe Bestimmung anhand der IP-Adresse statt einer exakten Methode.

2. Einlesen der Daten

Die Daten liegen in einer Excel-Datei vor, welche als Spaltennamen die jeweiligen Fragen verwendet. Für eine bessere Handhabung werden die Spaltennamen durch Umbenennen vereinfacht.

library(rmarkdown)

knitr::opts_chunk$set(echo = TRUE, warning = FALSE, message = FALSE)

library(readxl)
library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
data_source <- './Data_With_Geolocations_stud.xlsx'
sheet_name <- 'Geolocation expanded'

data <- read_excel(data_source, sheet = sheet_name) %>%
  rename(
    bike_stolen_frequency = 'How many times has your bike been stolen since you started cycling?',
    place_of_last_bike_theft = 'Where was the last bike stolen from?',
    bike_securing_method = 'What was the bike secured with?',
    bike_lock_rating = 'What security rating did the primary lock have?',
    other_lock_rating = 'Please specify the other security rating...',
    bike_lock_cost = 'How much did you spend on your locks?',
    bike_cost = 'What was the approximate value of the bike?',
    frame_number_record = 'Did you have a record of the frame number of the bike?',
    police_report = 'Did you report the stolen bike to the police?',
    bike_retrieved = 'Did you get the stolen bike back?',
    components_stolen_frequency = 'How many times have components (wheels, seats, brakes etc) been stolen from your bike?',
    components_stolen = 'Which components have been stolen?',
    other_components_stolen_description = 'Please specify the other components...',
    component_securing_method = 'What was the last stolen component attached / secured with?',
    location_description = 'Geolocation Details',
    location_latitude = 'Latitude',
    location_longitude = 'Longitude',
    location_country = 'Country',
    location_city = 'City'
  )

paged_table(data)

3. Aufbereitung

Alle gewählten Antworten bei Fragen mit Mehrfachauswahl befinden sich in der gleichen Spalte. Um die Qualität der Daten zu verbessern, wird für jede Option einer Mehrfachauswahl eine weitere Spalte abgeleitet.

library(stringr)
library(purrr)

check_multi_selection <- function(method, column) {
  if_else(
    is.na(column),
    NA,
    if_else(
      map_lgl(str_split(column, '\r\n'), ~method %in% .x),
      'Yes',
      'No'
    )
  )
}

data <- data %>%
  mutate(
    bike_not_locked = check_multi_selection('It wasn’t locked', bike_securing_method),
    bike_cable_lock = check_multi_selection('Cable lock', bike_securing_method),
    bike_u_d_lock = check_multi_selection('U-lock / D-lock', bike_securing_method),
    bike_chain_lock = check_multi_selection('Chain lock', bike_securing_method),
    bike_folding_lock = check_multi_selection('Folding lock', bike_securing_method),
    wheels_stolen = check_multi_selection('Wheels', components_stolen),
    seats_stolen = check_multi_selection('Seats', components_stolen),
    handlebars_stolen = check_multi_selection('Handlebars', components_stolen),
    headset_stolen = check_multi_selection('Headset', components_stolen),
    forks_stolen = check_multi_selection('Forks', components_stolen),
    brakes_stolen = check_multi_selection('Brakes', components_stolen),
    gears_stolen = check_multi_selection('Gears', components_stolen),
    lights_stolen = check_multi_selection('Lights', components_stolen),
    other_components_stolen = check_multi_selection('Others', components_stolen),
  )

paged_table(data)

4. Quantitative Betrachtung der Teilnahmen

Da die Umfragedaten keine Rückschlüsse auf die Teilnehmer zulassen, lassen sich lediglich die Teilnahmen betrachten. Eine mehrfache Teilnahme lässt sich nicht eindeutig identifizieren.

Wie viele Antworten auf die Umfrage wurden abgegeben?

library(ggplot2)

donut_diagram <- function(data, values, fill, annotation) {
  donut_diameter <- 2.5

  ggplot(data, aes(x = donut_diameter, y = values, fill = fill)) +
    geom_col() +
    geom_text(aes(label = values), position = position_stack(vjust = 0.5), color = 'white') +
    coord_polar(theta = "y") +
    xlim(c(0.2, donut_diameter + 0.5)) +
    theme(
      panel.background = element_rect(fill = 'white', color = 'white'),
      axis.title = element_blank(),
      axis.text = element_blank(),
      axis.ticks = element_blank()
    ) +
    annotate(
      'text',
      label = annotation,
      fontface = 'bold',
      color = 'black',
      size = 16,
      x = 0.3,
      y = 0
    )
}

total_count <- nrow(data)
unique_count <- data %>% unique() %>% nrow()

submission_counts <- data.frame(
  count = c(unique_count, total_count - unique_count),
  group = c("Yes", "No")
)

submission_counts %>%
  donut_diagram(values = submission_counts$count, fill = submission_counts$group, annotation = total_count) +
  labs(
    title = 'Anzahl der Teilnahmen',
    fill = 'Einzigartig'
  )

Die Doppelnennungen können zufällig entstehen, da ein Teilnehmer die gleiche Erfahrung gemacht haben kann wie ein anderer. Der Anteil dieser Antworten liegt bei unter 2 %, weshalb hier keine genauere Betrachtung benötigt wird und im restlichen Notebook der volle Datensatz verwendet wird.

5. Geographische Auswertung der Teilnahmen

Aus wie vielen Ländern kommen die Teilnahmen?

country_count <- data %>%
  select(location_country) %>%
  unique() %>%
  filter(!is.na(location_country)) %>%
  nrow()

ggplot() +
  annotate('text', x = 0, y = 0, size = 16, label = paste('≥', country_count)) +
  theme_void()

Das Ergebnis ist als Mindestanzahl zu verstehen, da Teilnahmen nicht immer einem Land zugeordnet werden können und gegebenenfalls aus weiteren Ländern getätigt worden sind.

Aus welchen Regionen kommen die Teilnahmen?

library(maps)

world_map <- map_data('world')

ggplot() +
  geom_polygon(data = world_map, aes(x = long, y = lat, group = group)) +
  theme(
    panel.background = element_rect(fill = 'white', color = 'black'),
    axis.line = element_line(color = 'black'),
    axis.title = element_blank(),
    axis.text = element_blank(),
    axis.ticks = element_blank()
  ) +
  geom_point(
    data = data,
    aes(x = as.numeric(location_longitude), y = as.numeric(location_latitude)),
    color = 'Deep Pink',
    fill = 'Pink',
    pch = 21,
    size = 2,
    alpha = I(0.5)
  ) +
  labs(
    title = 'Orte der Teilnahmen',
    caption = 'Ein Marker entspricht eine Teilnahme.',
  )

Welche Regionen sind am meisten vertreten?

library(treemapify)

submissions_per_region <- data %>%
  group_by(location_country, location_city) %>%
  summarize(region_count = n())

top_countries <- submissions_per_region %>%
  filter(!is.na(location_country)) %>%
  group_by(location_country) %>%
  summarize(country_count = sum(region_count)) %>%
  arrange(desc(country_count)) %>%
  top_n(5) %>%
  select(location_country)

country_city_distrubition <- function(region_counts, country) {
  country_regions <- region_counts %>%
    filter(location_country == country, !is.na(location_city))

  top_region_counts <- country_regions %>%
    arrange(desc(region_count)) %>%
    top_n(5)

  other_region_combined_count <- country_regions %>%
    filter(!(location_city %in% top_region_counts$location_city)) %>%
    summarize(region_count = sum(region_count), location_city = 'Other')

  bind_rows(top_region_counts, other_region_combined_count)
}

top_regions_per_top_country <- bind_rows(
  top_countries$location_country %>% map_df(\(x) country_city_distrubition(submissions_per_region, x)),
  data %>%
    filter(!(location_country %in% top_countries$location_country)) %>%
    summarize(region_count = n(), location_city = 'Other', location_country = 'Other')
)

ggplot(
  top_regions_per_top_country,
  aes(area = region_count, fill = location_country, subgroup = location_country, label = location_city)
) +
  geom_treemap() +
  geom_treemap_text(colour = 'white', place = 'centre', grow = FALSE) +
  labs(
    title = 'Regionen mit den meisten Teilnahmen',
    caption = 'Die Flächen der Teile entspricht der Anzahl der Teilnahmen.',
    fill = 'Land'
  )

6. Unstimmigkeiten in den Daten

Da wie genannt das Formular der Umfrage keine hinreichende Validierung durchführt, treten Widersprüche in den Daten auf. Um widerspruchslose Aussagen treffen zu können, werden die problematischen Daten bei der Auswertung ausgeschlossen.

Zu welchem Anteil sind Fahrräder als gestohlen angenommen worden, ohne gestohlen zu sein?

stolen_never_stolen <- function(data, stated_condition, frequency) {
  frequency <- deparse(substitute(frequency))

  data %>%
    mutate(frequency = map_chr(data[[frequency]], \(x) if_else(x == 'Never', 'No', 'Yes'))) %>%
    filter(stated_condition) %>%
    group_by(frequency) %>%
    summarize(submission_count = n())
}

bike_stolen_never_stolen <- function(data, stated_condition, case) {
  data %>%
    stolen_never_stolen(
      stated_condition = stated_condition,
      frequency = bike_stolen_frequency
    ) %>%
    summarize(frequency, submission_count, case = case)
}

bike_theft_counts <- bind_rows(
  data %>% bike_stolen_never_stolen(data$bike_retrieved == 'Yes', 'Bike retrieved'),
  data %>% bike_stolen_never_stolen(data$bike_retrieved == 'No', 'Bike not retrieved'),
  data %>% bike_stolen_never_stolen(data$police_report == 'Yes', 'Bike theft reported to police')
)

total_bike_theft_counts <- bike_theft_counts %>%
  group_by(case) %>%
  summarize(submission_count = sum(submission_count))

bike_theft_counts %>% ggplot(aes(x = case, y = submission_count, fill = frequency)) +
  geom_bar(stat = 'identity') +
  geom_text(aes(label = submission_count), position = position_stack(vjust = 0.5), color = 'white') +
  geom_text(data = total_bike_theft_counts, aes(label = submission_count, fill = NULL), hjust = -0.5, color = 'black') +
  ylim(c(0, 3750)) +
  theme(
    axis.ticks = element_blank(),
    axis.title = element_blank(),
    axis.text.x = element_blank(),
    panel.background = element_rect(fill = 'white', color = 'white')
  ) +
  coord_flip() +
  labs(
    title = 'Anzahl der als gestohlen angegebenen Fahrräder je Sachverhalten',
    fill = 'Fahrrad gestohlen',
  )

In wie vielen Fällen wurden Komponenten als gestohlen angegeben, ohne gestohlen worden zu sein?

stolen_component_counts <- data %>%
  stolen_never_stolen(
    stated_condition = !is.na(data$components_stolen),
    frequency = components_stolen_frequency
  )

stolen_component_counts %>%
  donut_diagram(
    values = stolen_component_counts$submission_count,
    fill = stolen_component_counts$frequency,
    annotation = stolen_component_counts %>% summarize(submission_count = sum(submission_count))
  ) +
  labs(
    title = 'Anzahl der Teilnahmen mit als geklaut angegebene Komponenten',
    fill = 'Komponente gestohlen',
    caption = 'Anzahl der Teilnahmen welche als mindestens eine Komponente als gestohlen angeben.'
  )

7. Ausmaß der Diebstähle

Wie häufig treten Diebstähle auf?

heat_map_design <- function(plot) {
  plot +
    geom_tile(color = 'white', lwd = 1.5, linetype = 1) +
    coord_fixed() +
    scale_fill_gradient(low = "grey", high = "Deep Pink") +
    theme(
      plot.title = element_text(hjust = 0.5),
      plot.caption = element_text(hjust = 0.5),
      panel.background = element_rect(fill = 'white', color = 'white'),
      axis.line = element_line(color = 'white'),
      axis.ticks = element_blank(),
      axis.title.y = element_text(margin = margin(r = 10)),
      axis.title.x = element_text(margin = margin(t = 10)),
      axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1),
      legend.position = 'none'
    )
}

data %>%
  group_by(bike_stolen_frequency, components_stolen_frequency) %>%
  summarize(
    submission_count = n(),
    bike_frequency_order = if_else(bike_stolen_frequency == 'Never', '0', bike_stolen_frequency),
    components_frequency_order = if_else(
      components_stolen_frequency == 'Never',
      '0',
      components_stolen_frequency
    )
  ) %>%
  ggplot(aes(x = bike_frequency_order, y = components_frequency_order, fill = submission_count)) %>%
  heat_map_design() +
  geom_text(aes(label = submission_count), color = 'white', size = 4) +
  labs(
    title = 'Anzahl der Teilnahmen pro Anzahl der erlebte Diebstähle',
    y = 'Anzahl der erlebten Komponentendiebstähle',
    x = 'Anzahl der erlebten Fahrraddiebstähle'
  )

Da gerade weniger als ein Fünftel der Teilnahmen keine Diebstähle angeben, lässt sich darüber hinaus ein Selection-Bias vermuten. Bei einer öffentlichen Onlineumfrage handelt es sich nicht immer um eine randomisierte Stichprobe. Es ist denkbar, dass Teilnehmer, die Diebstahl erlebt haben, ein größeres Interesse aufweisen und dadurch überproportional vertreten sind.

In welchen Ländern wird am wahrscheinlichsten mindestens ein Fahrrad gestohlen?

Die Frage wird mittels der bedingten Diebstahlwahrscheinlichkeit beantwortet. Da häufig Länder zu wenige Teilnahmen haben, um eine treffende Aussage machen zu können, sind diese unter “Other” zusammengefasst.

theft_per_country <- data %>%
  filter(!is.na(location_country)) %>%
  mutate(
    bike_stolen = if_else(bike_stolen_frequency == 'Never', 'No', 'Yes'),
    location_country = if_else(location_country %in% top_countries$location_country, location_country, 'Other')
  ) %>%
  group_by(location_country, bike_stolen) %>%
  summarize(submission_count = n())

theft_totals_per_country <- theft_per_country %>%
  group_by(location_country) %>%
  summarize(total_submission_per_country = sum(submission_count))

merge(theft_per_country, theft_totals_per_country) %>%
  filter(bike_stolen == "Yes") %>%
  mutate(theft_likelihood = submission_count / total_submission_per_country) %>%
  arrange(desc(theft_likelihood)) %>%
  ggplot(aes(x = location_country, y = theft_likelihood, fill = location_country)) +
  coord_cartesian(ylim = c(0.65, 0.85)) +
  geom_bar(stat = 'identity') +
  geom_text(aes(label = scales::percent(theft_likelihood)), vjust = -1, color = 'black') +
  theme(
    legend.position = 'none',
    axis.title.x = element_blank(),
    axis.ticks.x = element_blank(),
    axis.title.y = element_text(margin(r = 20))
  ) +
  labs(
    title = 'Fahrraddiebstahlswahrscheinlichkeit für die Länder mit den meisten Teilnahmen',
    y = 'Diebstahlwahrscheinlichkeit',
    caption = 'Wahrscheinlichkeiten, dass mindestens ein Fahrrad gestohlen wird.'
  )

Auch hier lässt sich wie oben erklärt ein Selection-Bias vermuten. Es fällt jedoch auf, dass die Diebstahlwahrscheinlichkeit in Kanada und den Vereinigten Staaten mit über 80 % am höchsten liegt. Eine mögliche Begründung könnte die hohe Abhängigkeit vom Auto in den Ländern sein. Ein Fahrrad ist für die Mehrheit der Bevölkerung in den Ländern ein Sportgerät, nicht ein alltägliches Transportmittel. Dies könnte zu einem Diebstahl durch wertvollere Fahrräder attraktiver machen und zum anderen soziale Hemmungen reduzieren, da ein Diebstahl nicht einen Mobilitätsverlust verursacht.

Welche Komponenten werden am meisten gestohlen?

stolen_components <- data %>%
  filter(components_stolen_frequency != 'Never')

count_stolen <- function(data, name, column) {
  column <- deparse(substitute(column))

  data %>%
    filter(data[[column]] == "Yes") %>%
    summarize(submission_count = n(), component = name) %>%
    summarize(component, theft_frequency = submission_count / nrow(data))
}

bind_rows(
  stolen_components %>% count_stolen('Wheels', wheels_stolen),
  stolen_components %>% count_stolen('Seats', seats_stolen),
  stolen_components %>% count_stolen('Handlebars', handlebars_stolen),
  stolen_components %>% count_stolen('Headsets', headset_stolen),
  stolen_components %>% count_stolen('Forks', forks_stolen),
  stolen_components %>% count_stolen('Brakes', brakes_stolen),
  stolen_components %>% count_stolen('Gears', gears_stolen),
  stolen_components %>% count_stolen('Lights', lights_stolen),
  stolen_components %>% count_stolen('Other', other_components_stolen),
) %>%
  ggplot(aes(x = component, y = theft_frequency, fill = '')) +
  geom_bar(stat = 'identity') +
  geom_text(aes(label = scales::percent(theft_frequency)), vjust = -1, color = 'black') +
  theme(
    legend.position = 'none',
    axis.ticks = element_blank(),
    axis.title.y = element_blank(),
    axis.title.x = element_blank(),
  ) +
  ylim(c(0, 0.5)) +
  labs(
    title = 'Häufigkeit der Diebstähle je Komponente unter den Komponentendiebstählen',
  )

Es fällt auf, das Komponenten, die ohne Werkzeuge entfernt werden können, auch deutlich häufiger gestohlen werden als die, die Werkzeug benötigen.

8. Effektivität der Maßnahmen zur Fahrraddiebstahlaufklärung

Eine Aussage zur Effektivität einer Maßnahme lässt sich durch das Vergleichen der bedingten Wahrscheinlichkeit treffen.

Werden Fälle mit bekannter Rahmennummer öfters Äufgeklärt?

library(kableExtra)
library(knitr)
library(tibble)
library(tidyr)

stolen_bikes <- data %>%
  filter(bike_stolen_frequency != 'Never')

two_conditions <- function(data, condition_x, condition_y) {
  condition_x <- deparse(substitute(condition_x))
  condition_y <- deparse(substitute(condition_y))

  data %>%
    filter(!is.na(data[condition_x]), !is.na(data[[condition_y]])) %>%
    group_by(.data[[condition_x]], .data[[condition_y]]) %>%
    summarize(submission_count = n())
}

yes_no_label <- function(value, yes, no) {
  case_when(
    value == "Yes" ~ yes,
    value == "No" ~ no,
    .default = value
  )
}

contingency_table <- function(data, condition_x, condition_y, x_lables, y_lables) {
  condition_x <- deparse(substitute(condition_x))
  condition_y <- deparse(substitute(condition_y))

  sum_label <- 'Summe'
  sum_color <- '#f3f3f3'

  frame <- data %>%
    mutate(
      '{condition_x}' := x_lables(.data[[condition_x]]),
      '{condition_y}' := y_lables(.data[[condition_y]]),
    ) %>%
    pivot_wider(names_from = condition_x, values_from = submission_count) %>%
    column_to_rownames(var = condition_y) %>%
    as.matrix() %>%
    addmargins() %>%
    as.data.frame()

  colnames(frame)[3] <- sum_label
  row.names(frame)[3] <- sum_label

  frame %>%
    kable(format = 'html') %>%
    kable_styling() %>%
    row_spec(0, extra_css = 'border-bottom: 1px solid;') %>%
    row_spec(2, extra_css = 'border-bottom: 1px solid;') %>%
    column_spec(1, bold = TRUE, border_right = TRUE, include_thead = TRUE) %>%
    column_spec(4, background = sum_color, include_thead = TRUE, border_left = TRUE) %>%
    row_spec(3, background = sum_color) %>%
    print()

  data
}

two_conditions_plot <- function(data, condition_x, condition_y) {
  condition_x <- deparse(substitute(condition_x))
  condition_y <- deparse(substitute(condition_y))

  data %>%
    mutate(likelihood = submission_count / nrow(data)) %>%
    ggplot(aes(x = .data[[condition_x]], y = .data[[condition_y]], fill = likelihood)) %>%
    heat_map_design() +
    geom_text(aes(label = scales::percent(likelihood)), color = 'white', size = 4)
}

retrieve_to_frame_number <- stolen_bikes %>% two_conditions(bike_retrieved, frame_number_record)

retrieve_to_frame_number %>%
  contingency_table(
    bike_retrieved,
    frame_number_record,
    \(x) yes_no_label(x, "Fahrrad zurückerhalten", "Fahrrad nicht zurückerhalten"),
    \(y) yes_no_label(y, "Rahmennummer bekannt", "Rahmennummer nicht bekannt")
  ) %>%
  two_conditions_plot(bike_retrieved, frame_number_record) +
  labs(
    title = 'Aufklärungsquotenverteilung der Fahrraddiebstähle in Abhängikeit zur Rahmennummer',
    x = 'Fahrrad zurückerhalten',
    y = 'Rahmennummer bekannt',
    caption = 'Die Randhäufigkeiten entsprechen der Summe einer einzeler Zeile beziehungsweiße Spalte.'
  )
Fahrrad nicht zurückerhalten Fahrrad zurückerhalten Summe
Rahmennummer nicht bekannt 2393 357 2750
Rahmennummer bekannt 719 356 1075
Summe 3112 713 3825

improvement_plot <- function(data, fixed_condition, changing_condition) {
  fixed_condition <- deparse(substitute(fixed_condition))
  changing_condition <- deparse(substitute(changing_condition))

  totals <- data %>%
    group_by(.data[[changing_condition]]) %>%
    summarize(total = sum(submission_count))

  likelihoods <- merge(data, totals, by = changing_condition) %>%
    filter(.data[[fixed_condition]] == "Yes") %>%
    mutate(likelihood = submission_count / total)

  likelihood <- function(data, condition) {
    (data %>% filter(.data[[changing_condition]] == condition))$likelihood[[1]]
  }

  improvement <- ((likelihoods %>% likelihood("Yes")) / (likelihoods %>% likelihood("No"))) - 1

  ggplot() +
    annotate('text', x = 0, y = 0, size = 14, label = paste(scales::percent(improvement), 'more likely')) +
    theme_void()
}

retrieve_to_frame_number %>%
  improvement_plot(bike_retrieved, frame_number_record)

Werden Fälle mit polizeilicher Meldung öfters Äufgeklärt?

retrieve_to_report <- stolen_bikes %>%
  two_conditions(bike_retrieved, police_report)

retrieve_to_report %>%
  contingency_table(
    bike_retrieved,
    police_report,
    \(x) yes_no_label(x, "Fahrrad zurückerhalten", "Fahrrad nicht zurückerhalten"),
    \(x) yes_no_label(x, "Polizeilich gemeldet", "Polizeilich nicht gemeldet")
  ) %>%
  two_conditions_plot(bike_retrieved, police_report) +
  labs(
    title = 'Aufklärungsquotenverteilung der Fahrraddiebstähle in Abhängikeit zur polizeilicher Behörde',
    x = 'Fahrrad zurückerhalten',
    y = 'Polizeilich gemeldet',
    caption = 'Die Randhäufigkeiten entsprechen der Summe einer einzeler Zeile beziehungsweiße Spalte.'
  )
Fahrrad nicht zurückerhalten Fahrrad zurückerhalten Summe
Polizeilich nicht gemeldet 1675 362 2037
Polizeilich gemeldet 1435 348 1783
Summe 3110 710 3820

retrieve_to_report %>% improvement_plot(bike_retrieved, police_report)

Werden Fälle mit Rahmennummer öfters polizeilich gemeldet?

report_to_frame_number <- stolen_bikes %>%
  two_conditions(police_report, frame_number_record)

report_to_frame_number %>%
  contingency_table(
    police_report,
    frame_number_record,
    \(x) yes_no_label(x, "Polizeilich gemeldet", "Polizeilich nicht gemeldet"),
    \(y) yes_no_label(y, "Rahmennummer bekannt", "Rahmennummer nicht bekannt")
  ) %>%
  two_conditions_plot(police_report, frame_number_record) +
  labs(
    title = 'Polizeiliche Fahrraddiebstahlmeldequotenverteilung in Abhängikeit zur Rahmennummer',
    x = 'Polizeilich gemeldet',
    y = 'Rahmennummer bekannt',
    caption = 'Die Randhäufigkeiten entsprechen der Summe einer einzeler Zeile beziehungsweiße Spalte.'
  )
Polizeilich nicht gemeldet Polizeilich gemeldet Summe
Rahmennummer nicht bekannt 1717 1036 2753
Rahmennummer bekannt 324 749 1073
Summe 2041 1785 3826

report_to_frame_number %>% improvement_plot(police_report, frame_number_record)

LS0tCnRpdGxlOiAiRXhwbG9yYXRpdmUgRGF0ZW5hbmFseXNlIHZvbiBGYWhycmFkZGllYnN0w6RobGUiCi0tLQoKCiMgMS4gSGVya3VuZnQgZGVyIERhdGVuCgpEaWUgenVncnVuZGUgbGllZ2VuZGVuIERhdGVuIHd1cmRlbiBpbiBlaW5lciBbT25saW5lLVVtZnJhZ2VdKGh0dHBzOi8vdGhlYmVzdGJpa2Vsb2NrLmNvbS93aW4tZnJlZS1iaWtlLWNvbXBldGl0aW9uLykKaW0gTm92ZW1iZXIgMjAyMSBlcmhvYmVuLgpEYXMgWmllbCBkaWVzZXIgVW1mcmFnZSBpc3QgZXMsIGRhcyBBdXNtYcOfIHZvbiBGYWhycmFkZGllYnN0YWhsIGluIE5vcmRhbWVyaWthLCBFdXJvcGEgdW5kIGRlbSBWZXJlaW5pZ3RlbiBLw7ZuaWdyZWljaAplaW5zY2jDpHR6ZW4genUga8O2bm5lbi4KCkRpZSBVbWZyYWdlIHVtZmFzc3QgMTIgRnJhZ2VuLCB3ZWxjaGUgaGllciB1bnZlcsOkbmRlcnQgaW4gZGVyIGF1ZmdlbGlzdGV0IHNpbmQuCkJlaSBkZXIgQXVzd2FobCBkZXIgQW50d29ydC1Nw7ZnbGljaGtlaXRlbiAiT3RoZXIiIGJlemllaHVuZ3N3ZWlzZSAiT3RoZXJzIiB3aXJkIHp1c8OkdHpsaWNoIGdld8O8bnNjaHQsIGRpZXNlIEFudHdvcnQgYWxzCkZyZWl0ZXh0IHp1IHNwZXppZml6aWVyZW4uCkVpbmUgVmVyaWZpemllcnVuZyBkZXIgRWluZ2FiZSBiaWV0ZXQgZGFzIEZvcm11bGFyIG5pY2h0LgpTbyBpc3QgZXMgYmVpc3BpZWxzd2Vpc2UgbcO2Z2xpY2ggYW56dWdlYmVuLCBkYXMgRmFocnJhZCB3w6RyZSBnbGVpY2h6ZWl0aWcgYWJnZXNpY2hlcnQgdW5kIG5pY2h0IGFiZ2VzaWNoZXJ0IGdld2VzZW4uCgo8IS0tIEBmb3JtYXR0ZXI6b2ZmIC0tPgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCkZyYWdlICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbnR3b3J0LU3DtmdsaWNoa2VpdGVuICAgICAgICAgICAgICAgICAgTWVocmZhY2hhdXN3YWhsIFZlcnBmbGljaHRlbmQKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIC0tLS0tLS0tLS0tLS0tLSAtLS0tLS0tLS0tLS0tCkhvdyBtYW55IHRpbWVzIGhhcyB5b3VyIGJpa2UgYmVlbiBzdG9sZW4gc2luY2UgeW91ICBOZXZlcjxicj4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOZWluICAgICAgICAgICAgSmEKc3RhcnRlZCBjeWNsaW5nPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDE8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMzxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQ8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1PGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNiB0aW1lcyBvciBtb3JlCgpXaGVyZSB3YXMgdGhlIGxhc3QgYmlrZSBzdG9sZW4gZnJvbT8gICAgICAgICAgICAgICAgU3RyZWV0PGJyPiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTmVpbiAgICAgICAgICAgIE5laW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZyb250IGdhcmRlbiAvIHlhcmQ8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCYWNrIGdhcmRlbiAvIHlhcmQ8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTaGVkPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2FyYWdlPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5zaWRlIGhvbWU8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXb3JrIG9yIFB1YmxpYyBBdXRob3JpdHkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJpa2U8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJraW5nCgpXaGF0IHdhcyB0aGUgYmlrZSBzZWN1cmVkIHdpdGg/ICAgICAgICAgICAgICAgICAgICAgSXQgd2FzbuKAmXQgbG9ja2VkPGJyPiAgICAgICAgICAgICAgICAgICBKYSAgICAgICAgICAgICAgTmVpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2FibGUgbG9jazxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFUtbG9jayAvIEQtbG9jazxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENoYWluIGxvY2s8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGb2xkaW5nIGxvY2sKCldoYXQgc2VjdXJpdHkgcmF0aW5nIGRpZCB0aGUgcHJpbWFyeSBsb2NrIGhhdmU/ICAgICBEb24ndCBrbm93PGJyPiAgICAgICAgICAgICAgICAgICAgICAgICBOZWluICAgICAgICAgICAgTmVpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTm9uZTxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNvbGQgU2VjdXJlIEJyb256ZTxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNvbGQgU2VjdXJlIFNpbHZlcjxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNvbGQgU2VjdXJlIEdvbGQ8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTb2xkIFNlY3VyZSBEaWFtb25kPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3RoZXIKCkhvdyBtdWNoIGRpZCB5b3Ugc3BlbmQgb24geW91ciBsb2Nrcz8gICAgICAgICAgICAgICBMZXNzIHRoYW4gMjA8YnI+ICAgICAgICAgICAgICAgICAgICAgICBOZWluICAgICAgICAgICAgTmVpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMjAgLSA0MDxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDQxIC0gNjA8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA2MSAtIDEwMDxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE1vcmUgdGhhbiAxMDAKCldoYXQgd2FzIHRoZSBhcHByb3hpbWF0ZSB2YWx1ZSBvZiB0aGUgYmlrZT8gICAgICAgICBMZXNzIHRoYW4gMTAwPGJyPiAgICAgICAgICAgICAgICAgICAgICBOZWluICAgICAgICAgICAgTmVpbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMTAwIC0gNDAwPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNDAxIC0gNzAwPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNzAxIC0gMTAwMDxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE92ZXIgMTAwMAoKRGlkIHlvdSBoYXZlIGEgcmVjb3JkIG9mIHRoZSBmcmFtZSBudW1iZXIgb2YgdGhlICAgIFllczxicj4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5laW4gICAgICAgICAgICBOZWluCmJpa2U/ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBObwoKRGlkIHlvdSByZXBvcnQgdGhlIHN0b2xlbiBiaWtlIHRvIHRoZSBwb2xpY2U/ICAgICAgIFllczxicj4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5laW4gICAgICAgICAgICBOZWluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBObwoKRGlkIHlvdSBnZXQgdGhlIHN0b2xlbiBiaWtlIGJhY2s/ICAgICAgICAgICAgICAgICAgIFllczxicj4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5laW4gICAgICAgICAgICBOZWluCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBObwoKSG93IG1hbnkgdGltZXMgaGF2ZSBjb21wb25lbnRzICh3aGVlbHMsIHNlYXRzLCAgICAgIE5ldmVyPGJyPiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5laW4gICAgICAgICAgICBKYQpicmFrZXMgZXRjKSBiZWVuIHN0b2xlbiBmcm9tIHlvdXIgYmlrZT8gICAgICAgICAgICAgMTxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDI8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNDxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDU8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA2IHRpbWVzIG9yIG1vcmU8YnI+CgpXaGljaCBjb21wb25lbnRzIGhhdmUgYmVlbiBzdG9sZW4/ICAgICAgICAgICAgICAgICAgV2hlZWxzPGJyPiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSmEgICAgICAgICAgICAgIE5laW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNlYXRzPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSGFuZGxlYmFyczxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWRzZXQ8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGb3Jrczxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJyYWtlczxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlYXJzPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTGlnaHRzPGJyPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT3RoZXJzCgpXaGF0IHdhcyB0aGUgbGFzdCBzdG9sZW4gY29tcG9uZW50IGF0dGFjaGVkIC8gICAgICAgUXVpY2sgcmVsZWFzZSBjbGFtcCBvbmx5PGJyPiAgICAgICAgICAgTmVpbiAgICAgICAgICAgIE5laW4Kc2VjdXJlZCB3aXRoPyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFF1aWNrIHJlbGVhc2UgY2xhbXAgcGx1cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FibGU8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIZXggYm9sdCBvciBTdGFuZGFyZCBudXQgb25seTxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhleCBib2x0IG9yIFN0YW5kYXJkIG51dCBwbHVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWJsZTxicj4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNlY3VyaXR5IHNrZXdlcnMgLyBudXRzIChQaW5oZWFkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGl0bG9jaywgSGV4bG94IGV0Yyk8YnI+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbnkgb3RoZXIgdHlwZSBvZiBzZWNvbmRhcnkgYmlrZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9jawotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCjwhLS0gQGZvcm1hdHRlcjpvbiAtLT4KCkFscyBCZWxvaG51bmcgd3VyZGUgZGVuIGVyc3RlbiA1MDAwIFRlaWxuZWhtZXJuIGVpbmUgR2V3aW5uY2hhbmNlIGF1ZiBlaW4gRmFocnJhZCBpbSBXZXJ0IHZvbiBiaXMgenUgNjAwwqAkIGluCkF1c3NpY2h0IGdlc3RlbGx0LgpFaW5lIE1laHJmYWNodGVpbG5haG1lIHdpcmQgbmljaHQgdmVyaGluZGVydCwgd2VzaGFsYiBlcyBuYWhlIGxpZWd0LCBkYXNzIGVpbnplbG5lIFRlaWxuZWhtZXIgbWVocmZhY2ggZGllIFVtZnJhZ2UKYmVhbnR3b3J0ZW4sIHVtIGlocmUgR2V3aW5uY2hhbmNlbiB6dSBzdGVpZ2Vybi4KCk5lYmVuIGRlbiBBbnR3b3J0ZW4gYXVmIGRpZSBGcmFnZW4gZW50aMOkbHQgZGVyIERhdGVuc2F0eiBkZW4gU3RhbmRvcnQgZGVyIFRlaWxuZWhtZXIuCkRhIGRpZXNlIG5pY2h0IGV4cGxpeml0IHZvbSBUZWlsbmVobWVyIGFuZ2VnZWJlbiB3aXJkLCBoYW5kZWx0IGVzIHNpY2ggd2FocnNjaGVpbmxpY2ggdW0gZWluZSBncm9iZSBCZXN0aW1tdW5nIGFuaGFuZApkZXIgSVAtQWRyZXNzZSBzdGF0dCBlaW5lciBleGFrdGVuIE1ldGhvZGUuCgojIDIuIEVpbmxlc2VuIGRlciBEYXRlbgoKRGllIERhdGVuIGxpZWdlbiBpbiBlaW5lciBFeGNlbC1EYXRlaSB2b3IsIHdlbGNoZSBhbHMgU3BhbHRlbm5hbWVuIGRpZSBqZXdlaWxpZ2VuIEZyYWdlbiB2ZXJ3ZW5kZXQuCkbDvHIgZWluZSBiZXNzZXJlIEhhbmRoYWJ1bmcgd2VyZGVuIGRpZSBTcGFsdGVubmFtZW4gZHVyY2ggVW1iZW5lbm5lbiB2ZXJlaW5mYWNodC4KCmBgYHtyfQpsaWJyYXJ5KHJtYXJrZG93bikKCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UpCgpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShkcGx5cikKCmRhdGFfc291cmNlIDwtICcuL0RhdGFfV2l0aF9HZW9sb2NhdGlvbnNfc3R1ZC54bHN4JwpzaGVldF9uYW1lIDwtICdHZW9sb2NhdGlvbiBleHBhbmRlZCcKCmRhdGEgPC0gcmVhZF9leGNlbChkYXRhX3NvdXJjZSwgc2hlZXQgPSBzaGVldF9uYW1lKSAlPiUKICByZW5hbWUoCiAgICBiaWtlX3N0b2xlbl9mcmVxdWVuY3kgPSAnSG93IG1hbnkgdGltZXMgaGFzIHlvdXIgYmlrZSBiZWVuIHN0b2xlbiBzaW5jZSB5b3Ugc3RhcnRlZCBjeWNsaW5nPycsCiAgICBwbGFjZV9vZl9sYXN0X2Jpa2VfdGhlZnQgPSAnV2hlcmUgd2FzIHRoZSBsYXN0IGJpa2Ugc3RvbGVuIGZyb20/JywKICAgIGJpa2Vfc2VjdXJpbmdfbWV0aG9kID0gJ1doYXQgd2FzIHRoZSBiaWtlIHNlY3VyZWQgd2l0aD8nLAogICAgYmlrZV9sb2NrX3JhdGluZyA9ICdXaGF0IHNlY3VyaXR5IHJhdGluZyBkaWQgdGhlIHByaW1hcnkgbG9jayBoYXZlPycsCiAgICBvdGhlcl9sb2NrX3JhdGluZyA9ICdQbGVhc2Ugc3BlY2lmeSB0aGUgb3RoZXIgc2VjdXJpdHkgcmF0aW5nLi4uJywKICAgIGJpa2VfbG9ja19jb3N0ID0gJ0hvdyBtdWNoIGRpZCB5b3Ugc3BlbmQgb24geW91ciBsb2Nrcz8nLAogICAgYmlrZV9jb3N0ID0gJ1doYXQgd2FzIHRoZSBhcHByb3hpbWF0ZSB2YWx1ZSBvZiB0aGUgYmlrZT8nLAogICAgZnJhbWVfbnVtYmVyX3JlY29yZCA9ICdEaWQgeW91IGhhdmUgYSByZWNvcmQgb2YgdGhlIGZyYW1lIG51bWJlciBvZiB0aGUgYmlrZT8nLAogICAgcG9saWNlX3JlcG9ydCA9ICdEaWQgeW91IHJlcG9ydCB0aGUgc3RvbGVuIGJpa2UgdG8gdGhlIHBvbGljZT8nLAogICAgYmlrZV9yZXRyaWV2ZWQgPSAnRGlkIHlvdSBnZXQgdGhlIHN0b2xlbiBiaWtlIGJhY2s/JywKICAgIGNvbXBvbmVudHNfc3RvbGVuX2ZyZXF1ZW5jeSA9ICdIb3cgbWFueSB0aW1lcyBoYXZlIGNvbXBvbmVudHMgKHdoZWVscywgc2VhdHMsIGJyYWtlcyBldGMpIGJlZW4gc3RvbGVuIGZyb20geW91ciBiaWtlPycsCiAgICBjb21wb25lbnRzX3N0b2xlbiA9ICdXaGljaCBjb21wb25lbnRzIGhhdmUgYmVlbiBzdG9sZW4/JywKICAgIG90aGVyX2NvbXBvbmVudHNfc3RvbGVuX2Rlc2NyaXB0aW9uID0gJ1BsZWFzZSBzcGVjaWZ5IHRoZSBvdGhlciBjb21wb25lbnRzLi4uJywKICAgIGNvbXBvbmVudF9zZWN1cmluZ19tZXRob2QgPSAnV2hhdCB3YXMgdGhlIGxhc3Qgc3RvbGVuIGNvbXBvbmVudCBhdHRhY2hlZCAvIHNlY3VyZWQgd2l0aD8nLAogICAgbG9jYXRpb25fZGVzY3JpcHRpb24gPSAnR2VvbG9jYXRpb24gRGV0YWlscycsCiAgICBsb2NhdGlvbl9sYXRpdHVkZSA9ICdMYXRpdHVkZScsCiAgICBsb2NhdGlvbl9sb25naXR1ZGUgPSAnTG9uZ2l0dWRlJywKICAgIGxvY2F0aW9uX2NvdW50cnkgPSAnQ291bnRyeScsCiAgICBsb2NhdGlvbl9jaXR5ID0gJ0NpdHknCiAgKQoKcGFnZWRfdGFibGUoZGF0YSkKYGBgCgojIDMuIEF1ZmJlcmVpdHVuZwoKQWxsZSBnZXfDpGhsdGVuIEFudHdvcnRlbiBiZWkgRnJhZ2VuIG1pdCBNZWhyZmFjaGF1c3dhaGwgYmVmaW5kZW4gc2ljaCBpbiBkZXIgZ2xlaWNoZW4gU3BhbHRlLgpVbSBkaWUgUXVhbGl0w6R0IGRlciBEYXRlbiB6dSB2ZXJiZXNzZXJuLCB3aXJkIGbDvHIgamVkZSBPcHRpb24gZWluZXIgTWVocmZhY2hhdXN3YWhsIGVpbmUgd2VpdGVyZSBTcGFsdGUgYWJnZWxlaXRldC4KCmBgYHtyfQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkocHVycnIpCgpjaGVja19tdWx0aV9zZWxlY3Rpb24gPC0gZnVuY3Rpb24obWV0aG9kLCBjb2x1bW4pIHsKICBpZl9lbHNlKAogICAgaXMubmEoY29sdW1uKSwKICAgIE5BLAogICAgaWZfZWxzZSgKICAgICAgbWFwX2xnbChzdHJfc3BsaXQoY29sdW1uLCAnXHJcbicpLCB+bWV0aG9kICVpbiUgLngpLAogICAgICAnWWVzJywKICAgICAgJ05vJwogICAgKQogICkKfQoKZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZSgKICAgIGJpa2Vfbm90X2xvY2tlZCA9IGNoZWNrX211bHRpX3NlbGVjdGlvbignSXQgd2FzbuKAmXQgbG9ja2VkJywgYmlrZV9zZWN1cmluZ19tZXRob2QpLAogICAgYmlrZV9jYWJsZV9sb2NrID0gY2hlY2tfbXVsdGlfc2VsZWN0aW9uKCdDYWJsZSBsb2NrJywgYmlrZV9zZWN1cmluZ19tZXRob2QpLAogICAgYmlrZV91X2RfbG9jayA9IGNoZWNrX211bHRpX3NlbGVjdGlvbignVS1sb2NrIC8gRC1sb2NrJywgYmlrZV9zZWN1cmluZ19tZXRob2QpLAogICAgYmlrZV9jaGFpbl9sb2NrID0gY2hlY2tfbXVsdGlfc2VsZWN0aW9uKCdDaGFpbiBsb2NrJywgYmlrZV9zZWN1cmluZ19tZXRob2QpLAogICAgYmlrZV9mb2xkaW5nX2xvY2sgPSBjaGVja19tdWx0aV9zZWxlY3Rpb24oJ0ZvbGRpbmcgbG9jaycsIGJpa2Vfc2VjdXJpbmdfbWV0aG9kKSwKICAgIHdoZWVsc19zdG9sZW4gPSBjaGVja19tdWx0aV9zZWxlY3Rpb24oJ1doZWVscycsIGNvbXBvbmVudHNfc3RvbGVuKSwKICAgIHNlYXRzX3N0b2xlbiA9IGNoZWNrX211bHRpX3NlbGVjdGlvbignU2VhdHMnLCBjb21wb25lbnRzX3N0b2xlbiksCiAgICBoYW5kbGViYXJzX3N0b2xlbiA9IGNoZWNrX211bHRpX3NlbGVjdGlvbignSGFuZGxlYmFycycsIGNvbXBvbmVudHNfc3RvbGVuKSwKICAgIGhlYWRzZXRfc3RvbGVuID0gY2hlY2tfbXVsdGlfc2VsZWN0aW9uKCdIZWFkc2V0JywgY29tcG9uZW50c19zdG9sZW4pLAogICAgZm9ya3Nfc3RvbGVuID0gY2hlY2tfbXVsdGlfc2VsZWN0aW9uKCdGb3JrcycsIGNvbXBvbmVudHNfc3RvbGVuKSwKICAgIGJyYWtlc19zdG9sZW4gPSBjaGVja19tdWx0aV9zZWxlY3Rpb24oJ0JyYWtlcycsIGNvbXBvbmVudHNfc3RvbGVuKSwKICAgIGdlYXJzX3N0b2xlbiA9IGNoZWNrX211bHRpX3NlbGVjdGlvbignR2VhcnMnLCBjb21wb25lbnRzX3N0b2xlbiksCiAgICBsaWdodHNfc3RvbGVuID0gY2hlY2tfbXVsdGlfc2VsZWN0aW9uKCdMaWdodHMnLCBjb21wb25lbnRzX3N0b2xlbiksCiAgICBvdGhlcl9jb21wb25lbnRzX3N0b2xlbiA9IGNoZWNrX211bHRpX3NlbGVjdGlvbignT3RoZXJzJywgY29tcG9uZW50c19zdG9sZW4pLAogICkKCnBhZ2VkX3RhYmxlKGRhdGEpCmBgYAoKIyA0LiBRdWFudGl0YXRpdmUgQmV0cmFjaHR1bmcgZGVyIFRlaWxuYWhtZW4KCkRhIGRpZSBVbWZyYWdlZGF0ZW4ga2VpbmUgUsO8Y2tzY2hsw7xzc2UgYXVmIGRpZSBUZWlsbmVobWVyIHp1bGFzc2VuLCBsYXNzZW4gc2ljaCBsZWRpZ2xpY2ggZGllIFRlaWxuYWhtZW4gYmV0cmFjaHRlbi4KRWluZSBtZWhyZmFjaGUgVGVpbG5haG1lIGzDpHNzdCBzaWNoIG5pY2h0IGVpbmRldXRpZyBpZGVudGlmaXppZXJlbi4KCj4gV2llIHZpZWxlIEFudHdvcnRlbiBhdWYgZGllIFVtZnJhZ2Ugd3VyZGVuIGFiZ2VnZWJlbj8KCmBgYHtyLCBmaWcud2lkdGg9MTB9CmxpYnJhcnkoZ2dwbG90MikKCmRvbnV0X2RpYWdyYW0gPC0gZnVuY3Rpb24oZGF0YSwgdmFsdWVzLCBmaWxsLCBhbm5vdGF0aW9uKSB7CiAgZG9udXRfZGlhbWV0ZXIgPC0gMi41CgogIGdncGxvdChkYXRhLCBhZXMoeCA9IGRvbnV0X2RpYW1ldGVyLCB5ID0gdmFsdWVzLCBmaWxsID0gZmlsbCkpICsKICAgIGdlb21fY29sKCkgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHZhbHVlcyksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBjb2xvciA9ICd3aGl0ZScpICsKICAgIGNvb3JkX3BvbGFyKHRoZXRhID0gInkiKSArCiAgICB4bGltKGMoMC4yLCBkb251dF9kaWFtZXRlciArIDAuNSkpICsKICAgIHRoZW1lKAogICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnLCBjb2xvciA9ICd3aGl0ZScpLAogICAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgICBheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkKICAgICkgKwogICAgYW5ub3RhdGUoCiAgICAgICd0ZXh0JywKICAgICAgbGFiZWwgPSBhbm5vdGF0aW9uLAogICAgICBmb250ZmFjZSA9ICdib2xkJywKICAgICAgY29sb3IgPSAnYmxhY2snLAogICAgICBzaXplID0gMTYsCiAgICAgIHggPSAwLjMsCiAgICAgIHkgPSAwCiAgICApCn0KCnRvdGFsX2NvdW50IDwtIG5yb3coZGF0YSkKdW5pcXVlX2NvdW50IDwtIGRhdGEgJT4lIHVuaXF1ZSgpICU+JSBucm93KCkKCnN1Ym1pc3Npb25fY291bnRzIDwtIGRhdGEuZnJhbWUoCiAgY291bnQgPSBjKHVuaXF1ZV9jb3VudCwgdG90YWxfY291bnQgLSB1bmlxdWVfY291bnQpLAogIGdyb3VwID0gYygiWWVzIiwgIk5vIikKKQoKc3VibWlzc2lvbl9jb3VudHMgJT4lCiAgZG9udXRfZGlhZ3JhbSh2YWx1ZXMgPSBzdWJtaXNzaW9uX2NvdW50cyRjb3VudCwgZmlsbCA9IHN1Ym1pc3Npb25fY291bnRzJGdyb3VwLCBhbm5vdGF0aW9uID0gdG90YWxfY291bnQpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQW56YWhsIGRlciBUZWlsbmFobWVuJywKICAgIGZpbGwgPSAnRWluemlnYXJ0aWcnCiAgKQpgYGAKCkRpZSBEb3BwZWxuZW5udW5nZW4ga8O2bm5lbiB6dWbDpGxsaWcgZW50c3RlaGVuLCBkYSBlaW4gVGVpbG5laG1lciBkaWUgZ2xlaWNoZSBFcmZhaHJ1bmcgZ2VtYWNodCBoYWJlbiBrYW5uIHdpZSBlaW4KYW5kZXJlci4KRGVyIEFudGVpbCBkaWVzZXIgQW50d29ydGVuIGxpZWd0IGJlaSB1bnRlciAywqAlLCB3ZXNoYWxiIGhpZXIga2VpbmUgZ2VuYXVlcmUgQmV0cmFjaHR1bmcgYmVuw7Z0aWd0IHdpcmQgdW5kIGltCnJlc3RsaWNoZW4gTm90ZWJvb2sgZGVyIHZvbGxlIERhdGVuc2F0eiB2ZXJ3ZW5kZXQgd2lyZC4KCiMgNS4gR2VvZ3JhcGhpc2NoZSBBdXN3ZXJ0dW5nIGRlciBUZWlsbmFobWVuCgo+IEF1cyB3aWUgdmllbGVuIEzDpG5kZXJuIGtvbW1lbiBkaWUgVGVpbG5haG1lbj8KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDJ9CmNvdW50cnlfY291bnQgPC0gZGF0YSAlPiUKICBzZWxlY3QobG9jYXRpb25fY291bnRyeSkgJT4lCiAgdW5pcXVlKCkgJT4lCiAgZmlsdGVyKCFpcy5uYShsb2NhdGlvbl9jb3VudHJ5KSkgJT4lCiAgbnJvdygpCgpnZ3Bsb3QoKSArCiAgYW5ub3RhdGUoJ3RleHQnLCB4ID0gMCwgeSA9IDAsIHNpemUgPSAxNiwgbGFiZWwgPSBwYXN0ZSgn4omlJywgY291bnRyeV9jb3VudCkpICsKICB0aGVtZV92b2lkKCkKYGBgCgpEYXMgRXJnZWJuaXMgaXN0IGFscyBNaW5kZXN0YW56YWhsIHp1IHZlcnN0ZWhlbiwgZGEgVGVpbG5haG1lbiBuaWNodCBpbW1lciBlaW5lbSBMYW5kIHp1Z2VvcmRuZXQgd2VyZGVuIGvDtm5uZW4gdW5kCmdlZ2ViZW5lbmZhbGxzIGF1cyB3ZWl0ZXJlbiBMw6RuZGVybiBnZXTDpHRpZ3Qgd29yZGVuIHNpbmQuCgo+IEF1cyB3ZWxjaGVuIFJlZ2lvbmVuIGtvbW1lbiBkaWUgVGVpbG5haG1lbj8KCmBgYHtyLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD02LjI1fQpsaWJyYXJ5KG1hcHMpCgp3b3JsZF9tYXAgPC0gbWFwX2RhdGEoJ3dvcmxkJykKCmdncGxvdCgpICsKICBnZW9tX3BvbHlnb24oZGF0YSA9IHdvcmxkX21hcCwgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSkgKwogIHRoZW1lKAogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJywgY29sb3IgPSAnYmxhY2snKSwKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICdibGFjaycpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCkKICApICsKICBnZW9tX3BvaW50KAogICAgZGF0YSA9IGRhdGEsCiAgICBhZXMoeCA9IGFzLm51bWVyaWMobG9jYXRpb25fbG9uZ2l0dWRlKSwgeSA9IGFzLm51bWVyaWMobG9jYXRpb25fbGF0aXR1ZGUpKSwKICAgIGNvbG9yID0gJ0RlZXAgUGluaycsCiAgICBmaWxsID0gJ1BpbmsnLAogICAgcGNoID0gMjEsCiAgICBzaXplID0gMiwKICAgIGFscGhhID0gSSgwLjUpCiAgKSArCiAgbGFicygKICAgIHRpdGxlID0gJ09ydGUgZGVyIFRlaWxuYWhtZW4nLAogICAgY2FwdGlvbiA9ICdFaW4gTWFya2VyIGVudHNwcmljaHQgZWluZSBUZWlsbmFobWUuJywKICApCmBgYAoKPiBXZWxjaGUgUmVnaW9uZW4gc2luZCBhbSBtZWlzdGVuIHZlcnRyZXRlbj8KCmBgYHtyLCBmaWcud2lkdGg9MTAsZmlnLmhlaWdodD01fQpsaWJyYXJ5KHRyZWVtYXBpZnkpCgpzdWJtaXNzaW9uc19wZXJfcmVnaW9uIDwtIGRhdGEgJT4lCiAgZ3JvdXBfYnkobG9jYXRpb25fY291bnRyeSwgbG9jYXRpb25fY2l0eSkgJT4lCiAgc3VtbWFyaXplKHJlZ2lvbl9jb3VudCA9IG4oKSkKCnRvcF9jb3VudHJpZXMgPC0gc3VibWlzc2lvbnNfcGVyX3JlZ2lvbiAlPiUKICBmaWx0ZXIoIWlzLm5hKGxvY2F0aW9uX2NvdW50cnkpKSAlPiUKICBncm91cF9ieShsb2NhdGlvbl9jb3VudHJ5KSAlPiUKICBzdW1tYXJpemUoY291bnRyeV9jb3VudCA9IHN1bShyZWdpb25fY291bnQpKSAlPiUKICBhcnJhbmdlKGRlc2MoY291bnRyeV9jb3VudCkpICU+JQogIHRvcF9uKDUpICU+JQogIHNlbGVjdChsb2NhdGlvbl9jb3VudHJ5KQoKY291bnRyeV9jaXR5X2Rpc3RydWJpdGlvbiA8LSBmdW5jdGlvbihyZWdpb25fY291bnRzLCBjb3VudHJ5KSB7CiAgY291bnRyeV9yZWdpb25zIDwtIHJlZ2lvbl9jb3VudHMgJT4lCiAgICBmaWx0ZXIobG9jYXRpb25fY291bnRyeSA9PSBjb3VudHJ5LCAhaXMubmEobG9jYXRpb25fY2l0eSkpCgogIHRvcF9yZWdpb25fY291bnRzIDwtIGNvdW50cnlfcmVnaW9ucyAlPiUKICAgIGFycmFuZ2UoZGVzYyhyZWdpb25fY291bnQpKSAlPiUKICAgIHRvcF9uKDUpCgogIG90aGVyX3JlZ2lvbl9jb21iaW5lZF9jb3VudCA8LSBjb3VudHJ5X3JlZ2lvbnMgJT4lCiAgICBmaWx0ZXIoIShsb2NhdGlvbl9jaXR5ICVpbiUgdG9wX3JlZ2lvbl9jb3VudHMkbG9jYXRpb25fY2l0eSkpICU+JQogICAgc3VtbWFyaXplKHJlZ2lvbl9jb3VudCA9IHN1bShyZWdpb25fY291bnQpLCBsb2NhdGlvbl9jaXR5ID0gJ090aGVyJykKCiAgYmluZF9yb3dzKHRvcF9yZWdpb25fY291bnRzLCBvdGhlcl9yZWdpb25fY29tYmluZWRfY291bnQpCn0KCnRvcF9yZWdpb25zX3Blcl90b3BfY291bnRyeSA8LSBiaW5kX3Jvd3MoCiAgdG9wX2NvdW50cmllcyRsb2NhdGlvbl9jb3VudHJ5ICU+JSBtYXBfZGYoXCh4KSBjb3VudHJ5X2NpdHlfZGlzdHJ1Yml0aW9uKHN1Ym1pc3Npb25zX3Blcl9yZWdpb24sIHgpKSwKICBkYXRhICU+JQogICAgZmlsdGVyKCEobG9jYXRpb25fY291bnRyeSAlaW4lIHRvcF9jb3VudHJpZXMkbG9jYXRpb25fY291bnRyeSkpICU+JQogICAgc3VtbWFyaXplKHJlZ2lvbl9jb3VudCA9IG4oKSwgbG9jYXRpb25fY2l0eSA9ICdPdGhlcicsIGxvY2F0aW9uX2NvdW50cnkgPSAnT3RoZXInKQopCgpnZ3Bsb3QoCiAgdG9wX3JlZ2lvbnNfcGVyX3RvcF9jb3VudHJ5LAogIGFlcyhhcmVhID0gcmVnaW9uX2NvdW50LCBmaWxsID0gbG9jYXRpb25fY291bnRyeSwgc3ViZ3JvdXAgPSBsb2NhdGlvbl9jb3VudHJ5LCBsYWJlbCA9IGxvY2F0aW9uX2NpdHkpCikgKwogIGdlb21fdHJlZW1hcCgpICsKICBnZW9tX3RyZWVtYXBfdGV4dChjb2xvdXIgPSAnd2hpdGUnLCBwbGFjZSA9ICdjZW50cmUnLCBncm93ID0gRkFMU0UpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUmVnaW9uZW4gbWl0IGRlbiBtZWlzdGVuIFRlaWxuYWhtZW4nLAogICAgY2FwdGlvbiA9ICdEaWUgRmzDpGNoZW4gZGVyIFRlaWxlIGVudHNwcmljaHQgZGVyIEFuemFobCBkZXIgVGVpbG5haG1lbi4nLAogICAgZmlsbCA9ICdMYW5kJwogICkKYGBgCgoKIyA2LiBVbnN0aW1taWdrZWl0ZW4gaW4gZGVuIERhdGVuCgpEYSB3aWUgZ2VuYW5udCBkYXMgRm9ybXVsYXIgZGVyIFVtZnJhZ2Uga2VpbmUgaGlucmVpY2hlbmRlIFZhbGlkaWVydW5nIGR1cmNoZsO8aHJ0LCB0cmV0ZW4gV2lkZXJzcHLDvGNoZSBpbiBkZW4gRGF0ZW4KYXVmLgpVbSB3aWRlcnNwcnVjaHNsb3NlIEF1c3NhZ2VuIHRyZWZmZW4genUga8O2bm5lbiwgd2VyZGVuIGRpZSBwcm9ibGVtYXRpc2NoZW4gRGF0ZW4gYmVpIGRlciBBdXN3ZXJ0dW5nIGF1c2dlc2NobG9zc2VuLgoKPiBadSB3ZWxjaGVtIEFudGVpbCBzaW5kIEZhaHJyw6RkZXIgYWxzIGdlc3RvaGxlbiBhbmdlbm9tbWVuIHdvcmRlbiwgb2huZSBnZXN0b2hsZW4genUgc2Vpbj8KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQgPSAzfQpzdG9sZW5fbmV2ZXJfc3RvbGVuIDwtIGZ1bmN0aW9uKGRhdGEsIHN0YXRlZF9jb25kaXRpb24sIGZyZXF1ZW5jeSkgewogIGZyZXF1ZW5jeSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoZnJlcXVlbmN5KSkKCiAgZGF0YSAlPiUKICAgIG11dGF0ZShmcmVxdWVuY3kgPSBtYXBfY2hyKGRhdGFbW2ZyZXF1ZW5jeV1dLCBcKHgpIGlmX2Vsc2UoeCA9PSAnTmV2ZXInLCAnTm8nLCAnWWVzJykpKSAlPiUKICAgIGZpbHRlcihzdGF0ZWRfY29uZGl0aW9uKSAlPiUKICAgIGdyb3VwX2J5KGZyZXF1ZW5jeSkgJT4lCiAgICBzdW1tYXJpemUoc3VibWlzc2lvbl9jb3VudCA9IG4oKSkKfQoKYmlrZV9zdG9sZW5fbmV2ZXJfc3RvbGVuIDwtIGZ1bmN0aW9uKGRhdGEsIHN0YXRlZF9jb25kaXRpb24sIGNhc2UpIHsKICBkYXRhICU+JQogICAgc3RvbGVuX25ldmVyX3N0b2xlbigKICAgICAgc3RhdGVkX2NvbmRpdGlvbiA9IHN0YXRlZF9jb25kaXRpb24sCiAgICAgIGZyZXF1ZW5jeSA9IGJpa2Vfc3RvbGVuX2ZyZXF1ZW5jeQogICAgKSAlPiUKICAgIHN1bW1hcml6ZShmcmVxdWVuY3ksIHN1Ym1pc3Npb25fY291bnQsIGNhc2UgPSBjYXNlKQp9CgpiaWtlX3RoZWZ0X2NvdW50cyA8LSBiaW5kX3Jvd3MoCiAgZGF0YSAlPiUgYmlrZV9zdG9sZW5fbmV2ZXJfc3RvbGVuKGRhdGEkYmlrZV9yZXRyaWV2ZWQgPT0gJ1llcycsICdCaWtlIHJldHJpZXZlZCcpLAogIGRhdGEgJT4lIGJpa2Vfc3RvbGVuX25ldmVyX3N0b2xlbihkYXRhJGJpa2VfcmV0cmlldmVkID09ICdObycsICdCaWtlIG5vdCByZXRyaWV2ZWQnKSwKICBkYXRhICU+JSBiaWtlX3N0b2xlbl9uZXZlcl9zdG9sZW4oZGF0YSRwb2xpY2VfcmVwb3J0ID09ICdZZXMnLCAnQmlrZSB0aGVmdCByZXBvcnRlZCB0byBwb2xpY2UnKQopCgp0b3RhbF9iaWtlX3RoZWZ0X2NvdW50cyA8LSBiaWtlX3RoZWZ0X2NvdW50cyAlPiUKICBncm91cF9ieShjYXNlKSAlPiUKICBzdW1tYXJpemUoc3VibWlzc2lvbl9jb3VudCA9IHN1bShzdWJtaXNzaW9uX2NvdW50KSkKCmJpa2VfdGhlZnRfY291bnRzICU+JSBnZ3Bsb3QoYWVzKHggPSBjYXNlLCB5ID0gc3VibWlzc2lvbl9jb3VudCwgZmlsbCA9IGZyZXF1ZW5jeSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzdWJtaXNzaW9uX2NvdW50KSwgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksIGNvbG9yID0gJ3doaXRlJykgKwogIGdlb21fdGV4dChkYXRhID0gdG90YWxfYmlrZV90aGVmdF9jb3VudHMsIGFlcyhsYWJlbCA9IHN1Ym1pc3Npb25fY291bnQsIGZpbGwgPSBOVUxMKSwgaGp1c3QgPSAtMC41LCBjb2xvciA9ICdibGFjaycpICsKICB5bGltKGMoMCwgMzc1MCkpICsKICB0aGVtZSgKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksCiAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnLCBjb2xvciA9ICd3aGl0ZScpCiAgKSArCiAgY29vcmRfZmxpcCgpICsKICBsYWJzKAogICAgdGl0bGUgPSAnQW56YWhsIGRlciBhbHMgZ2VzdG9obGVuIGFuZ2VnZWJlbmVuIEZhaHJyw6RkZXIgamUgU2FjaHZlcmhhbHRlbicsCiAgICBmaWxsID0gJ0ZhaHJyYWQgZ2VzdG9obGVuJywKICApCgpgYGAKCgo+IEluIHdpZSB2aWVsZW4gRsOkbGxlbiB3dXJkZW4gS29tcG9uZW50ZW4gYWxzIGdlc3RvaGxlbiBhbmdlZ2ViZW4sIG9obmUgZ2VzdG9obGVuIHdvcmRlbiB6dSBzZWluPwoKYGBge3IsIGZpZy53aWR0aCA9IDEwfQpzdG9sZW5fY29tcG9uZW50X2NvdW50cyA8LSBkYXRhICU+JQogIHN0b2xlbl9uZXZlcl9zdG9sZW4oCiAgICBzdGF0ZWRfY29uZGl0aW9uID0gIWlzLm5hKGRhdGEkY29tcG9uZW50c19zdG9sZW4pLAogICAgZnJlcXVlbmN5ID0gY29tcG9uZW50c19zdG9sZW5fZnJlcXVlbmN5CiAgKQoKc3RvbGVuX2NvbXBvbmVudF9jb3VudHMgJT4lCiAgZG9udXRfZGlhZ3JhbSgKICAgIHZhbHVlcyA9IHN0b2xlbl9jb21wb25lbnRfY291bnRzJHN1Ym1pc3Npb25fY291bnQsCiAgICBmaWxsID0gc3RvbGVuX2NvbXBvbmVudF9jb3VudHMkZnJlcXVlbmN5LAogICAgYW5ub3RhdGlvbiA9IHN0b2xlbl9jb21wb25lbnRfY291bnRzICU+JSBzdW1tYXJpemUoc3VibWlzc2lvbl9jb3VudCA9IHN1bShzdWJtaXNzaW9uX2NvdW50KSkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAnQW56YWhsIGRlciBUZWlsbmFobWVuIG1pdCBhbHMgZ2VrbGF1dCBhbmdlZ2ViZW5lIEtvbXBvbmVudGVuJywKICAgIGZpbGwgPSAnS29tcG9uZW50ZSBnZXN0b2hsZW4nLAogICAgY2FwdGlvbiA9ICdBbnphaGwgZGVyIFRlaWxuYWhtZW4gd2VsY2hlIGFscyBtaW5kZXN0ZW5zIGVpbmUgS29tcG9uZW50ZSBhbHMgZ2VzdG9obGVuIGFuZ2ViZW4uJwogICkKYGBgCgojIDcuIEF1c21hw58gZGVyIERpZWJzdMOkaGxlCgo+IFdpZSBow6R1ZmlnIHRyZXRlbiBEaWVic3TDpGhsZSBhdWY/CgpgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA4fQpoZWF0X21hcF9kZXNpZ24gPC0gZnVuY3Rpb24ocGxvdCkgewogIHBsb3QgKwogICAgZ2VvbV90aWxlKGNvbG9yID0gJ3doaXRlJywgbHdkID0gMS41LCBsaW5ldHlwZSA9IDEpICsKICAgIGNvb3JkX2ZpeGVkKCkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiZ3JleSIsIGhpZ2ggPSAiRGVlcCBQaW5rIikgKwogICAgdGhlbWUoCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnLCBjb2xvciA9ICd3aGl0ZScpLAogICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAnd2hpdGUnKSwKICAgICAgYXhpcy50aWNrcyA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApKSwKICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTApKSwKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMSksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJwogICAgKQp9CgpkYXRhICU+JQogIGdyb3VwX2J5KGJpa2Vfc3RvbGVuX2ZyZXF1ZW5jeSwgY29tcG9uZW50c19zdG9sZW5fZnJlcXVlbmN5KSAlPiUKICBzdW1tYXJpemUoCiAgICBzdWJtaXNzaW9uX2NvdW50ID0gbigpLAogICAgYmlrZV9mcmVxdWVuY3lfb3JkZXIgPSBpZl9lbHNlKGJpa2Vfc3RvbGVuX2ZyZXF1ZW5jeSA9PSAnTmV2ZXInLCAnMCcsIGJpa2Vfc3RvbGVuX2ZyZXF1ZW5jeSksCiAgICBjb21wb25lbnRzX2ZyZXF1ZW5jeV9vcmRlciA9IGlmX2Vsc2UoCiAgICAgIGNvbXBvbmVudHNfc3RvbGVuX2ZyZXF1ZW5jeSA9PSAnTmV2ZXInLAogICAgICAnMCcsCiAgICAgIGNvbXBvbmVudHNfc3RvbGVuX2ZyZXF1ZW5jeQogICAgKQogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYmlrZV9mcmVxdWVuY3lfb3JkZXIsIHkgPSBjb21wb25lbnRzX2ZyZXF1ZW5jeV9vcmRlciwgZmlsbCA9IHN1Ym1pc3Npb25fY291bnQpKSAlPiUKICBoZWF0X21hcF9kZXNpZ24oKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHN1Ym1pc3Npb25fY291bnQpLCBjb2xvciA9ICd3aGl0ZScsIHNpemUgPSA0KSArCiAgbGFicygKICAgIHRpdGxlID0gJ0FuemFobCBkZXIgVGVpbG5haG1lbiBwcm8gQW56YWhsIGRlciBlcmxlYnRlIERpZWJzdMOkaGxlJywKICAgIHkgPSAnQW56YWhsIGRlciBlcmxlYnRlbiBLb21wb25lbnRlbmRpZWJzdMOkaGxlJywKICAgIHggPSAnQW56YWhsIGRlciBlcmxlYnRlbiBGYWhycmFkZGllYnN0w6RobGUnCiAgKQpgYGAKRGEgZ2VyYWRlIHdlbmlnZXIgYWxzIGVpbiBGw7xuZnRlbCBkZXIgVGVpbG5haG1lbiBrZWluZSBEaWVic3TDpGhsZSBhbmdlYmVuLCBsw6Rzc3Qgc2ljaCBkYXLDvGJlciBoaW5hdXMgZWluIFNlbGVjdGlvbi1CaWFzCnZlcm11dGVuLgpCZWkgZWluZXIgw7ZmZmVudGxpY2hlbiBPbmxpbmV1bWZyYWdlIGhhbmRlbHQgZXMgc2ljaCBuaWNodCBpbW1lciB1bSBlaW5lIHJhbmRvbWlzaWVydGUgU3RpY2hwcm9iZS4KRXMgaXN0IGRlbmtiYXIsIGRhc3MgVGVpbG5laG1lciwgZGllIERpZWJzdGFobCBlcmxlYnQgaGFiZW4sIGVpbiBncsO2w59lcmVzIEludGVyZXNzZSBhdWZ3ZWlzZW4gdW5kIGRhZHVyY2gKw7xiZXJwcm9wb3J0aW9uYWwgdmVydHJldGVuIHNpbmQuCgo+IEluIHdlbGNoZW4gTMOkbmRlcm4gd2lyZCBhbSB3YWhyc2NoZWlubGljaHN0ZW4gbWluZGVzdGVucyBlaW4gRmFocnJhZCBnZXN0b2hsZW4/CgpEaWUgRnJhZ2Ugd2lyZCBtaXR0ZWxzIGRlciBiZWRpbmd0ZW4gRGllYnN0YWhsd2FocnNjaGVpbmxpY2hrZWl0IGJlYW50d29ydGV0LgpEYSBow6R1ZmlnIEzDpG5kZXIgenUgd2VuaWdlIFRlaWxuYWhtZW4gaGFiZW4sIHVtIGVpbmUgdHJlZmZlbmRlIEF1c3NhZ2UgbWFjaGVuIHp1IGvDtm5uZW4sIHNpbmQgZGllc2UgdW50ZXIgIk90aGVyIgp6dXNhbW1lbmdlZmFzc3QuCgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnRoZWZ0X3Blcl9jb3VudHJ5IDwtIGRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShsb2NhdGlvbl9jb3VudHJ5KSkgJT4lCiAgbXV0YXRlKAogICAgYmlrZV9zdG9sZW4gPSBpZl9lbHNlKGJpa2Vfc3RvbGVuX2ZyZXF1ZW5jeSA9PSAnTmV2ZXInLCAnTm8nLCAnWWVzJyksCiAgICBsb2NhdGlvbl9jb3VudHJ5ID0gaWZfZWxzZShsb2NhdGlvbl9jb3VudHJ5ICVpbiUgdG9wX2NvdW50cmllcyRsb2NhdGlvbl9jb3VudHJ5LCBsb2NhdGlvbl9jb3VudHJ5LCAnT3RoZXInKQogICkgJT4lCiAgZ3JvdXBfYnkobG9jYXRpb25fY291bnRyeSwgYmlrZV9zdG9sZW4pICU+JQogIHN1bW1hcml6ZShzdWJtaXNzaW9uX2NvdW50ID0gbigpKQoKdGhlZnRfdG90YWxzX3Blcl9jb3VudHJ5IDwtIHRoZWZ0X3Blcl9jb3VudHJ5ICU+JQogIGdyb3VwX2J5KGxvY2F0aW9uX2NvdW50cnkpICU+JQogIHN1bW1hcml6ZSh0b3RhbF9zdWJtaXNzaW9uX3Blcl9jb3VudHJ5ID0gc3VtKHN1Ym1pc3Npb25fY291bnQpKQoKbWVyZ2UodGhlZnRfcGVyX2NvdW50cnksIHRoZWZ0X3RvdGFsc19wZXJfY291bnRyeSkgJT4lCiAgZmlsdGVyKGJpa2Vfc3RvbGVuID09ICJZZXMiKSAlPiUKICBtdXRhdGUodGhlZnRfbGlrZWxpaG9vZCA9IHN1Ym1pc3Npb25fY291bnQgLyB0b3RhbF9zdWJtaXNzaW9uX3Blcl9jb3VudHJ5KSAlPiUKICBhcnJhbmdlKGRlc2ModGhlZnRfbGlrZWxpaG9vZCkpICU+JQogIGdncGxvdChhZXMoeCA9IGxvY2F0aW9uX2NvdW50cnksIHkgPSB0aGVmdF9saWtlbGlob29kLCBmaWxsID0gbG9jYXRpb25fY291bnRyeSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoMC42NSwgMC44NSkpICsKICBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzY2FsZXM6OnBlcmNlbnQodGhlZnRfbGlrZWxpaG9vZCkpLCB2anVzdCA9IC0xLCBjb2xvciA9ICdibGFjaycpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJywKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4ociA9IDIwKSkKICApICsKICBsYWJzKAogICAgdGl0bGUgPSAnRmFocnJhZGRpZWJzdGFobHN3YWhyc2NoZWlubGljaGtlaXQgZsO8ciBkaWUgTMOkbmRlciBtaXQgZGVuIG1laXN0ZW4gVGVpbG5haG1lbicsCiAgICB5ID0gJ0RpZWJzdGFobHdhaHJzY2hlaW5saWNoa2VpdCcsCiAgICBjYXB0aW9uID0gJ1dhaHJzY2hlaW5saWNoa2VpdGVuLCBkYXNzIG1pbmRlc3RlbnMgZWluIEZhaHJyYWQgZ2VzdG9obGVuIHdpcmQuJwogICkKYGBgCgpBdWNoIGhpZXIgbMOkc3N0IHNpY2ggd2llIG9iZW4gZXJrbMOkcnQgZWluIFNlbGVjdGlvbi1CaWFzIHZlcm11dGVuLgpFcyBmw6RsbHQgamVkb2NoIGF1ZiwgZGFzcyBkaWUgRGllYnN0YWhsd2FocnNjaGVpbmxpY2hrZWl0IGluIEthbmFkYSB1bmQgZGVuIFZlcmVpbmlndGVuIFN0YWF0ZW4gbWl0IMO8YmVyIDgwwqAlIGFtCmjDtmNoc3RlbiBsaWVndC4KRWluZSBtw7ZnbGljaGUgQmVncsO8bmR1bmcga8O2bm50ZSBkaWUgaG9oZSBBYmjDpG5naWdrZWl0IHZvbSBBdXRvIGluIGRlbiBMw6RuZGVybiBzZWluLgpFaW4gRmFocnJhZCBpc3QgZsO8ciBkaWUgTWVocmhlaXQgZGVyIEJldsO2bGtlcnVuZyBpbiBkZW4gTMOkbmRlcm4gZWluIFNwb3J0Z2Vyw6R0LCBuaWNodCBlaW4gYWxsdMOkZ2xpY2hlcyBUcmFuc3BvcnRtaXR0ZWwuCkRpZXMga8O2bm50ZSB6dSBlaW5lbSBEaWVic3RhaGwgZHVyY2ggd2VydHZvbGxlcmUgRmFocnLDpGRlciBhdHRyYWt0aXZlciBtYWNoZW4gdW5kIHp1bSBhbmRlcmVuIHNvemlhbGUgSGVtbXVuZ2VuCnJlZHV6aWVyZW4sIGRhIGVpbiBEaWVic3RhaGwgbmljaHQgZWluZW4gTW9iaWxpdMOkdHN2ZXJsdXN0IHZlcnVyc2FjaHQuCgo+IFdlbGNoZSBLb21wb25lbnRlbiB3ZXJkZW4gYW0gbWVpc3RlbiBnZXN0b2hsZW4/CgpgYGB7ciwgZmlnLndpZHRoID0gMTB9CnN0b2xlbl9jb21wb25lbnRzIDwtIGRhdGEgJT4lCiAgZmlsdGVyKGNvbXBvbmVudHNfc3RvbGVuX2ZyZXF1ZW5jeSAhPSAnTmV2ZXInKQoKY291bnRfc3RvbGVuIDwtIGZ1bmN0aW9uKGRhdGEsIG5hbWUsIGNvbHVtbikgewogIGNvbHVtbiA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY29sdW1uKSkKCiAgZGF0YSAlPiUKICAgIGZpbHRlcihkYXRhW1tjb2x1bW5dXSA9PSAiWWVzIikgJT4lCiAgICBzdW1tYXJpemUoc3VibWlzc2lvbl9jb3VudCA9IG4oKSwgY29tcG9uZW50ID0gbmFtZSkgJT4lCiAgICBzdW1tYXJpemUoY29tcG9uZW50LCB0aGVmdF9mcmVxdWVuY3kgPSBzdWJtaXNzaW9uX2NvdW50IC8gbnJvdyhkYXRhKSkKfQoKYmluZF9yb3dzKAogIHN0b2xlbl9jb21wb25lbnRzICU+JSBjb3VudF9zdG9sZW4oJ1doZWVscycsIHdoZWVsc19zdG9sZW4pLAogIHN0b2xlbl9jb21wb25lbnRzICU+JSBjb3VudF9zdG9sZW4oJ1NlYXRzJywgc2VhdHNfc3RvbGVuKSwKICBzdG9sZW5fY29tcG9uZW50cyAlPiUgY291bnRfc3RvbGVuKCdIYW5kbGViYXJzJywgaGFuZGxlYmFyc19zdG9sZW4pLAogIHN0b2xlbl9jb21wb25lbnRzICU+JSBjb3VudF9zdG9sZW4oJ0hlYWRzZXRzJywgaGVhZHNldF9zdG9sZW4pLAogIHN0b2xlbl9jb21wb25lbnRzICU+JSBjb3VudF9zdG9sZW4oJ0ZvcmtzJywgZm9ya3Nfc3RvbGVuKSwKICBzdG9sZW5fY29tcG9uZW50cyAlPiUgY291bnRfc3RvbGVuKCdCcmFrZXMnLCBicmFrZXNfc3RvbGVuKSwKICBzdG9sZW5fY29tcG9uZW50cyAlPiUgY291bnRfc3RvbGVuKCdHZWFycycsIGdlYXJzX3N0b2xlbiksCiAgc3RvbGVuX2NvbXBvbmVudHMgJT4lIGNvdW50X3N0b2xlbignTGlnaHRzJywgbGlnaHRzX3N0b2xlbiksCiAgc3RvbGVuX2NvbXBvbmVudHMgJT4lIGNvdW50X3N0b2xlbignT3RoZXInLCBvdGhlcl9jb21wb25lbnRzX3N0b2xlbiksCikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gY29tcG9uZW50LCB5ID0gdGhlZnRfZnJlcXVlbmN5LCBmaWxsID0gJycpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KHRoZWZ0X2ZyZXF1ZW5jeSkpLCB2anVzdCA9IC0xLCBjb2xvciA9ICdibGFjaycpICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJywKICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgKSArCiAgeWxpbShjKDAsIDAuNSkpICsKICBsYWJzKAogICAgdGl0bGUgPSAnSMOkdWZpZ2tlaXQgZGVyIERpZWJzdMOkaGxlIGplIEtvbXBvbmVudGUgdW50ZXIgZGVuIEtvbXBvbmVudGVuZGllYnN0w6RobGVuJywKICApCmBgYApFcyBmw6RsbHQgYXVmLCBkYXMgS29tcG9uZW50ZW4sIGRpZSBvaG5lIFdlcmt6ZXVnZSBlbnRmZXJudCB3ZXJkZW4ga8O2bm5lbiwgYXVjaCBkZXV0bGljaCBow6R1ZmlnZXIgZ2VzdG9obGVuIHdlcmRlbiBhbHMKZGllLCBkaWUgV2Vya3pldWcgYmVuw7Z0aWdlbi4KCiMgOC4gRWZmZWt0aXZpdMOkdCBkZXIgTWHDn25haG1lbiB6dXIgRmFocnJhZGRpZWJzdGFobGF1Zmtsw6RydW5nCgpFaW5lIEF1c3NhZ2UgenVyIEVmZmVrdGl2aXTDpHQgZWluZXIgTWHDn25haG1lIGzDpHNzdCBzaWNoIGR1cmNoIGRhcyBWZXJnbGVpY2hlbiBkZXIgYmVkaW5ndGVuIFdhaHJzY2hlaW5saWNoa2VpdCB0cmVmZmVuLgoKPiBXZXJkZW4gRsOkbGxlIG1pdCBiZWthbm50ZXIgUmFobWVubnVtbWVyIMO2ZnRlcnMgw4R1Zmdla2zDpHJ0PwoKYGBge3IsIGZpZy53aWR0aCA9IDExLCByZXN1bHRzPSdhc2lzJ30KbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpYmJsZSkKbGlicmFyeSh0aWR5cikKCnN0b2xlbl9iaWtlcyA8LSBkYXRhICU+JQogIGZpbHRlcihiaWtlX3N0b2xlbl9mcmVxdWVuY3kgIT0gJ05ldmVyJykKCnR3b19jb25kaXRpb25zIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbmRpdGlvbl94LCBjb25kaXRpb25feSkgewogIGNvbmRpdGlvbl94IDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShjb25kaXRpb25feCkpCiAgY29uZGl0aW9uX3kgPC0gZGVwYXJzZShzdWJzdGl0dXRlKGNvbmRpdGlvbl95KSkKCiAgZGF0YSAlPiUKICAgIGZpbHRlcighaXMubmEoZGF0YVtjb25kaXRpb25feF0pLCAhaXMubmEoZGF0YVtbY29uZGl0aW9uX3ldXSkpICU+JQogICAgZ3JvdXBfYnkoLmRhdGFbW2NvbmRpdGlvbl94XV0sIC5kYXRhW1tjb25kaXRpb25feV1dKSAlPiUKICAgIHN1bW1hcml6ZShzdWJtaXNzaW9uX2NvdW50ID0gbigpKQp9Cgp5ZXNfbm9fbGFiZWwgPC0gZnVuY3Rpb24odmFsdWUsIHllcywgbm8pIHsKICBjYXNlX3doZW4oCiAgICB2YWx1ZSA9PSAiWWVzIiB+IHllcywKICAgIHZhbHVlID09ICJObyIgfiBubywKICAgIC5kZWZhdWx0ID0gdmFsdWUKICApCn0KCmNvbnRpbmdlbmN5X3RhYmxlIDwtIGZ1bmN0aW9uKGRhdGEsIGNvbmRpdGlvbl94LCBjb25kaXRpb25feSwgeF9sYWJsZXMsIHlfbGFibGVzKSB7CiAgY29uZGl0aW9uX3ggPC0gZGVwYXJzZShzdWJzdGl0dXRlKGNvbmRpdGlvbl94KSkKICBjb25kaXRpb25feSA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY29uZGl0aW9uX3kpKQoKICBzdW1fbGFiZWwgPC0gJ1N1bW1lJwogIHN1bV9jb2xvciA8LSAnI2YzZjNmMycKCiAgZnJhbWUgPC0gZGF0YSAlPiUKICAgIG11dGF0ZSgKICAgICAgJ3tjb25kaXRpb25feH0nIDo9IHhfbGFibGVzKC5kYXRhW1tjb25kaXRpb25feF1dKSwKICAgICAgJ3tjb25kaXRpb25feX0nIDo9IHlfbGFibGVzKC5kYXRhW1tjb25kaXRpb25feV1dKSwKICAgICkgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gY29uZGl0aW9uX3gsIHZhbHVlc19mcm9tID0gc3VibWlzc2lvbl9jb3VudCkgJT4lCiAgICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gY29uZGl0aW9uX3kpICU+JQogICAgYXMubWF0cml4KCkgJT4lCiAgICBhZGRtYXJnaW5zKCkgJT4lCiAgICBhcy5kYXRhLmZyYW1lKCkKCiAgY29sbmFtZXMoZnJhbWUpWzNdIDwtIHN1bV9sYWJlbAogIHJvdy5uYW1lcyhmcmFtZSlbM10gPC0gc3VtX2xhYmVsCgogIGZyYW1lICU+JQogICAga2FibGUoZm9ybWF0ID0gJ2h0bWwnKSAlPiUKICAgIGthYmxlX3N0eWxpbmcoKSAlPiUKICAgIHJvd19zcGVjKDAsIGV4dHJhX2NzcyA9ICdib3JkZXItYm90dG9tOiAxcHggc29saWQ7JykgJT4lCiAgICByb3dfc3BlYygyLCBleHRyYV9jc3MgPSAnYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkOycpICU+JQogICAgY29sdW1uX3NwZWMoMSwgYm9sZCA9IFRSVUUsIGJvcmRlcl9yaWdodCA9IFRSVUUsIGluY2x1ZGVfdGhlYWQgPSBUUlVFKSAlPiUKICAgIGNvbHVtbl9zcGVjKDQsIGJhY2tncm91bmQgPSBzdW1fY29sb3IsIGluY2x1ZGVfdGhlYWQgPSBUUlVFLCBib3JkZXJfbGVmdCA9IFRSVUUpICU+JQogICAgcm93X3NwZWMoMywgYmFja2dyb3VuZCA9IHN1bV9jb2xvcikgJT4lCiAgICBwcmludCgpCgogIGRhdGEKfQoKdHdvX2NvbmRpdGlvbnNfcGxvdCA8LSBmdW5jdGlvbihkYXRhLCBjb25kaXRpb25feCwgY29uZGl0aW9uX3kpIHsKICBjb25kaXRpb25feCA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY29uZGl0aW9uX3gpKQogIGNvbmRpdGlvbl95IDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShjb25kaXRpb25feSkpCgogIGRhdGEgJT4lCiAgICBtdXRhdGUobGlrZWxpaG9vZCA9IHN1Ym1pc3Npb25fY291bnQgLyBucm93KGRhdGEpKSAlPiUKICAgIGdncGxvdChhZXMoeCA9IC5kYXRhW1tjb25kaXRpb25feF1dLCB5ID0gLmRhdGFbW2NvbmRpdGlvbl95XV0sIGZpbGwgPSBsaWtlbGlob29kKSkgJT4lCiAgICBoZWF0X21hcF9kZXNpZ24oKSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KGxpa2VsaWhvb2QpKSwgY29sb3IgPSAnd2hpdGUnLCBzaXplID0gNCkKfQoKcmV0cmlldmVfdG9fZnJhbWVfbnVtYmVyIDwtIHN0b2xlbl9iaWtlcyAlPiUgdHdvX2NvbmRpdGlvbnMoYmlrZV9yZXRyaWV2ZWQsIGZyYW1lX251bWJlcl9yZWNvcmQpCgpyZXRyaWV2ZV90b19mcmFtZV9udW1iZXIgJT4lCiAgY29udGluZ2VuY3lfdGFibGUoCiAgICBiaWtlX3JldHJpZXZlZCwKICAgIGZyYW1lX251bWJlcl9yZWNvcmQsCiAgICBcKHgpIHllc19ub19sYWJlbCh4LCAiRmFocnJhZCB6dXLDvGNrZXJoYWx0ZW4iLCAiRmFocnJhZCBuaWNodCB6dXLDvGNrZXJoYWx0ZW4iKSwKICAgIFwoeSkgeWVzX25vX2xhYmVsKHksICJSYWhtZW5udW1tZXIgYmVrYW5udCIsICJSYWhtZW5udW1tZXIgbmljaHQgYmVrYW5udCIpCiAgKSAlPiUKICB0d29fY29uZGl0aW9uc19wbG90KGJpa2VfcmV0cmlldmVkLCBmcmFtZV9udW1iZXJfcmVjb3JkKSArCiAgbGFicygKICAgIHRpdGxlID0gJ0F1Zmtsw6RydW5nc3F1b3RlbnZlcnRlaWx1bmcgZGVyIEZhaHJyYWRkaWVic3TDpGhsZSBpbiBBYmjDpG5naWtlaXQgenVyIFJhaG1lbm51bW1lcicsCiAgICB4ID0gJ0ZhaHJyYWQgenVyw7xja2VyaGFsdGVuJywKICAgIHkgPSAnUmFobWVubnVtbWVyIGJla2FubnQnLAogICAgY2FwdGlvbiA9ICdEaWUgUmFuZGjDpHVmaWdrZWl0ZW4gZW50c3ByZWNoZW4gZGVyIFN1bW1lIGVpbmVyIGVpbnplbGVyIFplaWxlIGJlemllaHVuZ3N3ZWnDn2UgU3BhbHRlLicKICApCmBgYApgYGB7ciwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAyfQppbXByb3ZlbWVudF9wbG90IDwtIGZ1bmN0aW9uKGRhdGEsIGZpeGVkX2NvbmRpdGlvbiwgY2hhbmdpbmdfY29uZGl0aW9uKSB7CiAgZml4ZWRfY29uZGl0aW9uIDwtIGRlcGFyc2Uoc3Vic3RpdHV0ZShmaXhlZF9jb25kaXRpb24pKQogIGNoYW5naW5nX2NvbmRpdGlvbiA8LSBkZXBhcnNlKHN1YnN0aXR1dGUoY2hhbmdpbmdfY29uZGl0aW9uKSkKCiAgdG90YWxzIDwtIGRhdGEgJT4lCiAgICBncm91cF9ieSguZGF0YVtbY2hhbmdpbmdfY29uZGl0aW9uXV0pICU+JQogICAgc3VtbWFyaXplKHRvdGFsID0gc3VtKHN1Ym1pc3Npb25fY291bnQpKQoKICBsaWtlbGlob29kcyA8LSBtZXJnZShkYXRhLCB0b3RhbHMsIGJ5ID0gY2hhbmdpbmdfY29uZGl0aW9uKSAlPiUKICAgIGZpbHRlciguZGF0YVtbZml4ZWRfY29uZGl0aW9uXV0gPT0gIlllcyIpICU+JQogICAgbXV0YXRlKGxpa2VsaWhvb2QgPSBzdWJtaXNzaW9uX2NvdW50IC8gdG90YWwpCgogIGxpa2VsaWhvb2QgPC0gZnVuY3Rpb24oZGF0YSwgY29uZGl0aW9uKSB7CiAgICAoZGF0YSAlPiUgZmlsdGVyKC5kYXRhW1tjaGFuZ2luZ19jb25kaXRpb25dXSA9PSBjb25kaXRpb24pKSRsaWtlbGlob29kW1sxXV0KICB9CgogIGltcHJvdmVtZW50IDwtICgobGlrZWxpaG9vZHMgJT4lIGxpa2VsaWhvb2QoIlllcyIpKSAvIChsaWtlbGlob29kcyAlPiUgbGlrZWxpaG9vZCgiTm8iKSkpIC0gMQoKICBnZ3Bsb3QoKSArCiAgICBhbm5vdGF0ZSgndGV4dCcsIHggPSAwLCB5ID0gMCwgc2l6ZSA9IDE0LCBsYWJlbCA9IHBhc3RlKHNjYWxlczo6cGVyY2VudChpbXByb3ZlbWVudCksICdtb3JlIGxpa2VseScpKSArCiAgICB0aGVtZV92b2lkKCkKfQoKcmV0cmlldmVfdG9fZnJhbWVfbnVtYmVyICU+JQogIGltcHJvdmVtZW50X3Bsb3QoYmlrZV9yZXRyaWV2ZWQsIGZyYW1lX251bWJlcl9yZWNvcmQpCmBgYAoKPiBXZXJkZW4gRsOkbGxlIG1pdCBwb2xpemVpbGljaGVyIE1lbGR1bmcgw7ZmdGVycyDDhHVmZ2VrbMOkcnQ/CgpgYGB7ciwgZmlnLndpZHRoID0gMTEsIHJlc3VsdHM9J2FzaXMnfQpyZXRyaWV2ZV90b19yZXBvcnQgPC0gc3RvbGVuX2Jpa2VzICU+JQogIHR3b19jb25kaXRpb25zKGJpa2VfcmV0cmlldmVkLCBwb2xpY2VfcmVwb3J0KQoKcmV0cmlldmVfdG9fcmVwb3J0ICU+JQogIGNvbnRpbmdlbmN5X3RhYmxlKAogICAgYmlrZV9yZXRyaWV2ZWQsCiAgICBwb2xpY2VfcmVwb3J0LAogICAgXCh4KSB5ZXNfbm9fbGFiZWwoeCwgIkZhaHJyYWQgenVyw7xja2VyaGFsdGVuIiwgIkZhaHJyYWQgbmljaHQgenVyw7xja2VyaGFsdGVuIiksCiAgICBcKHgpIHllc19ub19sYWJlbCh4LCAiUG9saXplaWxpY2ggZ2VtZWxkZXQiLCAiUG9saXplaWxpY2ggbmljaHQgZ2VtZWxkZXQiKQogICkgJT4lCiAgdHdvX2NvbmRpdGlvbnNfcGxvdChiaWtlX3JldHJpZXZlZCwgcG9saWNlX3JlcG9ydCkgKwogIGxhYnMoCiAgICB0aXRsZSA9ICdBdWZrbMOkcnVuZ3NxdW90ZW52ZXJ0ZWlsdW5nIGRlciBGYWhycmFkZGllYnN0w6RobGUgaW4gQWJow6RuZ2lrZWl0IHp1ciBwb2xpemVpbGljaGVyIEJlaMO2cmRlJywKICAgIHggPSAnRmFocnJhZCB6dXLDvGNrZXJoYWx0ZW4nLAogICAgeSA9ICdQb2xpemVpbGljaCBnZW1lbGRldCcsCiAgICBjYXB0aW9uID0gJ0RpZSBSYW5kaMOkdWZpZ2tlaXRlbiBlbnRzcHJlY2hlbiBkZXIgU3VtbWUgZWluZXIgZWluemVsZXIgWmVpbGUgYmV6aWVodW5nc3dlacOfZSBTcGFsdGUuJwogICkKYGBgCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDJ9CnJldHJpZXZlX3RvX3JlcG9ydCAlPiUgaW1wcm92ZW1lbnRfcGxvdChiaWtlX3JldHJpZXZlZCwgcG9saWNlX3JlcG9ydCkKYGBgCgoKPiBXZXJkZW4gRsOkbGxlIG1pdCBSYWhtZW5udW1tZXIgw7ZmdGVycyBwb2xpemVpbGljaCBnZW1lbGRldD8KCmBgYHtyLCBmaWcud2lkdGggPSAxMSwgcmVzdWx0cz0nYXNpcyd9CnJlcG9ydF90b19mcmFtZV9udW1iZXIgPC0gc3RvbGVuX2Jpa2VzICU+JQogIHR3b19jb25kaXRpb25zKHBvbGljZV9yZXBvcnQsIGZyYW1lX251bWJlcl9yZWNvcmQpCgpyZXBvcnRfdG9fZnJhbWVfbnVtYmVyICU+JQogIGNvbnRpbmdlbmN5X3RhYmxlKAogICAgcG9saWNlX3JlcG9ydCwKICAgIGZyYW1lX251bWJlcl9yZWNvcmQsCiAgICBcKHgpIHllc19ub19sYWJlbCh4LCAiUG9saXplaWxpY2ggZ2VtZWxkZXQiLCAiUG9saXplaWxpY2ggbmljaHQgZ2VtZWxkZXQiKSwKICAgIFwoeSkgeWVzX25vX2xhYmVsKHksICJSYWhtZW5udW1tZXIgYmVrYW5udCIsICJSYWhtZW5udW1tZXIgbmljaHQgYmVrYW5udCIpCiAgKSAlPiUKICB0d29fY29uZGl0aW9uc19wbG90KHBvbGljZV9yZXBvcnQsIGZyYW1lX251bWJlcl9yZWNvcmQpICsKICBsYWJzKAogICAgdGl0bGUgPSAnUG9saXplaWxpY2hlIEZhaHJyYWRkaWVic3RhaGxtZWxkZXF1b3RlbnZlcnRlaWx1bmcgaW4gQWJow6RuZ2lrZWl0IHp1ciBSYWhtZW5udW1tZXInLAogICAgeCA9ICdQb2xpemVpbGljaCBnZW1lbGRldCcsCiAgICB5ID0gJ1JhaG1lbm51bW1lciBiZWthbm50JywKICAgIGNhcHRpb24gPSAnRGllIFJhbmRow6R1Zmlna2VpdGVuIGVudHNwcmVjaGVuIGRlciBTdW1tZSBlaW5lciBlaW56ZWxlciBaZWlsZSBiZXppZWh1bmdzd2Vpw59lIFNwYWx0ZS4nCiAgKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDJ9CnJlcG9ydF90b19mcmFtZV9udW1iZXIgJT4lIGltcHJvdmVtZW50X3Bsb3QocG9saWNlX3JlcG9ydCwgZnJhbWVfbnVtYmVyX3JlY29yZCkKYGBg