Project Context

This analysis explores EDDMapS (Early Detection & Distribution Mapping System) data as part of developing an automated invasive species risk assessment system using knowledge graphs.

Research Goal: Extract spatial-temporal patterns, species traits, and invasion dynamics from occurrence data to feed into a knowledge graph that predicts species at risk of invasion.

Key Questions: - What species are most frequently reported? - Where are invasion hotspots? - How do invasions spread over time? - What patterns emerge that could inform risk assessment?

Load Packages

library(readr)
library(dplyr)
library(stringr)
library(ggplot2)
library(tidyr)
library(lubridate)
library(scales)
library(viridis)
library(knitr)
library(sf)
library(maps)

# Set theme
theme_set(theme_minimal(base_size = 12) +
            theme(plot.title = element_text(face = "bold", size = 14),
                  plot.subtitle = element_text(size = 11, color = "gray30"),
                  legend.position = "bottom"))

1. Load and Inspect Data

# Load EDDMapS data
# UPDATE THIS PATH to match your file location
eddmaps <- read_csv("C:/Users/annasokol/Downloads/EDDMapSPoints_view_6818701269405066160.csv",
                    show_col_types = FALSE)

cat("✓ Data loaded!\n")
✓ Data loaded!
cat("Dimensions:", nrow(eddmaps), "rows ×", ncol(eddmaps), "columns\n")
Dimensions: 5630454 rows × 18 columns

Data Structure

# Show column names and types
glimpse(eddmaps)
Rows: 5,630,454
Columns: 18
$ TableID           <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, …
$ Reporter_First    <chr> "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "Chris", "…
$ Reporter_Last     <chr> "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "Evans", "…
$ State_Name        <chr> "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georgia", "Georg…
$ County_Name       <chr> "Tift County", "Tift County", "Montgomery County", "Berrien County", "Berrien County", "Tift County", "Tift County", "Berrien C…
$ ObservationDate   <chr> "4/17/2006 12:00:00 AM", "4/17/2006 12:00:00 AM", "3/17/2006 12:00:00 AM", "4/17/2006 12:00:00 AM", "4/17/2006 12:00:00 AM", "4…
$ Date_Updated      <chr> "4/17/2006 12:00:00 AM", "4/17/2006 12:00:00 AM", "3/17/2006 12:00:00 AM", "4/17/2006 12:00:00 AM", "4/17/2006 12:00:00 AM", "4…
$ SUB_ID            <dbl> 2425, 3035, 3045, 2779, 2779, 3035, 3039, 3035, 3009, 3039, 3039, 2425, 3049, 3035, 3083, 2425, 3049, 3035, 3039, 3039, 3035, 3…
$ SUB_name          <chr> "kudzu", "Chinese privet", "Japanese climbing fern", "alligatorweed", "alligatorweed", "Chinese privet", "Japanese honeysuckle"…
$ SUB_genus         <chr> "Pueraria", "Ligustrum", "Lygodium", "Alternanthera", "Alternanthera", "Ligustrum", "Lonicera", "Ligustrum", "Arundo", "Lonicer…
$ SUB_species       <chr> "montana", "sinense", "japonicum", "philoxeroides", "philoxeroides", "sinense", "japonica", "sinense", "donax", "japonica", "ja…
$ SUB_variety       <chr> "var. lobata", NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, "var. lobata", NA, NA, NA, "var. lobata", NA, NA, NA, NA, NA, NA, NA, NA…
$ Scientific_Name   <chr> "Pueraria montana var. lobata", "Ligustrum sinense", "Lygodium japonicum", "Alternanthera philoxeroides", "Alternanthera philox…
$ year              <dbl> 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2006, 2…
$ StateFIPS         <dbl> 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,…
$ InfestedAreaAcres <dbl> 3.000000000, 0.000344352, 3.000000000, 0.009182720, 0.004591360, NA, 0.000344352, 0.000229568, NA, 0.000688704, 0.045913600, 1.…
$ x                 <dbl> 3155857, 3155857, 3224207, 3172772, 3173432, 3153715, 3153715, 3172772, 3152477, 3152477, 3157085, 3157411, 3155444, 3149938, 3…
$ y                 <dbl> 3838672, 3838672, 3931809, 3828005, 3826923, 3838166, 3838166, 3828005, 3836609, 3836609, 3832718, 3832783, 3838497, 3834044, 3…

First Look at the Data

head(eddmaps, 20)

Column Descriptions

# Understand what each column contains
data.frame(
  Column = names(eddmaps),
  Type = sapply(eddmaps, class),
  Sample_Value = sapply(eddmaps, function(x) as.character(x[1])),
  Non_Missing = sapply(eddmaps, function(x) sum(!is.na(x))),
  Percent_Complete = round(sapply(eddmaps, function(x) sum(!is.na(x))/length(x)*100), 1)
) %>%
  arrange(desc(Percent_Complete))

2. Data Cleaning & Preparation

# Clean and prepare data
eddmaps_clean <- eddmaps %>%
  # Parse dates properly
  mutate(
    ObservationDate = as.Date(ObservationDate, format = "%m/%d/%Y"),
    Date_Updated = as.Date(Date_Updated, format = "%m/%d/%Y"),
    year = year(ObservationDate),
    month = month(ObservationDate, label = TRUE),
    # Handle infested area
    InfestedAreaAcres = as.numeric(InfestedAreaAcres),
    # Clean species names
    Scientific_Name = str_trim(Scientific_Name),
    SUB_name = str_trim(SUB_name)
  ) %>%
  # Remove records without dates or locations
  filter(!is.na(ObservationDate), !is.na(x), !is.na(y))

cat("✓ Data cleaned!\n")
cat("Records after cleaning:", nrow(eddmaps_clean), "\n")
cat("Date range:", min(eddmaps_clean$ObservationDate, na.rm = TRUE), "to", 
    max(eddmaps_clean$ObservationDate, na.rm = TRUE), "\n")

3. Species Overview

How Many Species?

n_species <- length(unique(eddmaps_clean$Scientific_Name))
n_genera <- length(unique(eddmaps_clean$SUB_genus))
n_families <- length(unique(eddmaps_clean$SUB_name))

cat("Total unique species:", n_species, "\n")
cat("Total genera:", n_genera, "\n")
cat("Total common names:", n_families, "\n")

Top 20 Most Reported Species

top_species <- eddmaps_clean %>%
  count(Scientific_Name, SUB_name, sort = TRUE) %>%
  head(20)

top_species
eddmaps_clean %>%
  count(Scientific_Name, SUB_name) %>%
  arrange(desc(n)) %>%
  head(20) %>%
  mutate(
    label = paste0(SUB_name, "\n(", Scientific_Name, ")"),
    label = str_wrap(label, width = 30)
  ) %>%
  ggplot(aes(x = reorder(label, n), y = n, fill = n)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = comma(n)), hjust = -0.2, size = 3.5) +
  coord_flip() +
  scale_fill_viridis_c(option = "plasma", direction = -1) +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.15))) +
  labs(
    title = "Top 20 Most Reported Invasive Species in EDDMapS",
    subtitle = "Number of occurrence records",
    x = NULL,
    y = "Number of Reports"
  ) +
  theme(panel.grid.major.y = element_blank())

4. Geographic Distribution

Reports by State

state_summary <- eddmaps_clean %>%
  count(State_Name, name = "reports") %>%
  arrange(desc(reports))

state_summary %>%
  head(20) 
state_summary %>%
  head(20) %>%
  ggplot(aes(x = reorder(State_Name, reports), y = reports, fill = reports)) +
  geom_col(show.legend = FALSE) +
  geom_text(aes(label = comma(reports)), hjust = -0.2, size = 3.5) +
  coord_flip() +
  scale_fill_gradient(low = "#3B9AB2", high = "#E1AF00") +
  scale_y_continuous(labels = comma, expand = expansion(mult = c(0, 0.15))) +
  labs(
    title = "Invasive Species Reports by State",
    subtitle = "Top 20 states with most EDDMapS reports",
    x = NULL,
    y = "Number of Reports"
  ) +
  theme(panel.grid.major.y = element_blank())

Geographic Hotspots Map

# Get US states map
us_states <- map_data("state")

# Create hexbin summary
eddmaps_map <- eddmaps_clean %>%
  filter(!is.na(x), !is.na(y))

# Plot all occurrence points
ggplot() +
  geom_polygon(data = us_states, 
               aes(x = long, y = lat, group = group),
               fill = "white", color = "gray60", size = 0.3) +
  geom_point(data = eddmaps_map, 
             aes(x = x, y = y), 
             color = "#D55E00", alpha = 0.3, size = 0.8) +
  coord_map() +
  labs(
    title = "Geographic Distribution of Invasive Species Reports",
    subtitle = paste("All", nrow(eddmaps_map), "occurrence records from EDDMapS"),
    x = "Longitude",
    y = "Latitude"
  ) +
  theme_minimal() +
  theme(
    plot.title = element_text(face = "bold", size = 14),
    plot.subtitle = element_text(size = 11)
  )

5. Temporal Patterns

Reports Over Time

temporal_summary <- eddmaps_clean %>%
  count(year) %>%
  arrange(year)

temporal_summary
eddmaps_clean %>%
  count(year) %>%
  ggplot(aes(x = year, y = n)) +
  geom_line(color = "#0072B2", size = 1.2) +
  geom_point(color = "#D55E00", size = 3) +
  scale_y_continuous(labels = comma) +
  scale_x_continuous(breaks = seq(min(eddmaps_clean$year, na.rm = TRUE),
                                  max(eddmaps_clean$year, na.rm = TRUE), 
                                  by = 2)) +
  labs(
    title = "Invasive Species Reports Over Time",
    subtitle = "Annual trend in EDDMapS submissions",
    x = "Year",
    y = "Number of Reports"
  )

Seasonal Patterns

eddmaps_clean %>%
  count(month) %>%
  ggplot(aes(x = month, y = n, fill = n)) +
  geom_col(show.legend = FALSE) +
  scale_fill_viridis_c(option = "magma", direction = -1) +
  scale_y_continuous(labels = comma) +
  labs(
    title = "Seasonal Pattern of Invasive Species Reports",
    subtitle = "When are most observations submitted?",
    x = "Month",
    y = "Number of Reports"
  )

6. Invasion Extent Analysis

Infested Area Distribution

area_data <- eddmaps_clean %>%
  filter(!is.na(InfestedAreaAcres), InfestedAreaAcres > 0)

cat("Records with area data:", nrow(area_data), 
    "out of", nrow(eddmaps_clean), 
    "(", round(100*nrow(area_data)/nrow(eddmaps_clean), 1), "%)\n")

if(nrow(area_data) > 0) {
  area_summary <- area_data %>%
    summarize(
      min_acres = min(InfestedAreaAcres, na.rm = TRUE),
      median_acres = median(InfestedAreaAcres, na.rm = TRUE),
      mean_acres = mean(InfestedAreaAcres, na.rm = TRUE),
      max_acres = max(InfestedAreaAcres, na.rm = TRUE),
      total_acres = sum(InfestedAreaAcres, na.rm = TRUE)
    )
  
  area_summary 
if(nrow(area_data) > 0) {
  area_data %>%
    filter(InfestedAreaAcres < quantile(InfestedAreaAcres, 0.95, na.rm = TRUE)) %>%
    ggplot(aes(x = InfestedAreaAcres)) +
    geom_histogram(bins = 50, fill = "#E69F00", color = "white") +
    scale_x_continuous(labels = comma) +
    scale_y_continuous(labels = comma) +
    labs(
      title = "Distribution of Infested Area Sizes",
      subtitle = "Most reports (95th percentile shown to reduce outlier effect)",
      x = "Infested Area (Acres)",
      y = "Number of Reports"
    )
}

Species with Largest Infestations

if(nrow(area_data) > 0) {
  area_data %>%
    group_by(Scientific_Name, SUB_name) %>%
    summarize(
      total_acres = sum(InfestedAreaAcres, na.rm = TRUE),
      max_single = max(InfestedAreaAcres, na.rm = TRUE),
      n_reports = n(),
      .groups = "drop"
    ) %>%
    arrange(desc(total_acres)) %>%
    head(15)
}

7. Species-Specific Deep Dives

Pick a Species to Analyze in Detail

Let’s analyze the most reported species in detail:

# Get most reported species
focus_species_name <- top_species$Scientific_Name[1]
focus_common_name <- top_species$SUB_name[1]

cat("Analyzing:", focus_species_name, "(", focus_common_name, ")\n")

species_data <- eddmaps_clean %>%
  filter(Scientific_Name == focus_species_name)

cat("Total reports:", nrow(species_data), "\n")

Geographic Distribution of corn earworm, tomato fruitworm

ggplot() +
  geom_polygon(data = us_states, 
               aes(x = long, y = lat, group = group),
               fill = "white", color = "gray60", size = 0.3) +
  geom_point(data = species_data, 
             aes(x = x, y = y, color = year), 
             alpha = 0.6, size = 2) +
  scale_color_viridis_c(option = "plasma") +
  coord_map() +
  labs(
    title = paste("Distribution of", focus_common_name),
    subtitle = paste(focus_species_name, "- All EDDMapS reports"),
    x = "Longitude",
    y = "Latitude",
    color = "Year"
  ) +
  theme_minimal()

8. Multi-Species Comparison

Compare Top 5 Species

top5_species <- top_species$Scientific_Name[1:5]

comparison_data <- eddmaps_clean %>%
  filter(Scientific_Name %in% top5_species) %>%
  count(year, Scientific_Name, SUB_name)

Geographic Spread

eddmaps_clean %>%
  filter(Scientific_Name %in% top5_species) %>%
  count(State_Name, SUB_name) %>%
  group_by(SUB_name) %>%
  top_n(10, n) %>%
  ungroup() %>%
  ggplot(aes(x = reorder_within(State_Name, n, SUB_name), y = n, fill = SUB_name)) +
  geom_col(show.legend = FALSE) +
  coord_flip() +
  scale_x_reordered() +
  scale_fill_viridis_d(option = "turbo") +
  facet_wrap(~SUB_name, scales = "free_y", ncol = 2) +
  labs(
    title = "Top States for Each of the 5 Most Reported Species",
    x = NULL,
    y = "Number of Reports"
  ) +
  theme(strip.text = element_text(face = "bold"))

9. Data Quality Assessment

Completeness Analysis

quality_metrics <- data.frame(
  Field = c("Scientific Name", "Common Name", "Date", "Location (x,y)", 
            "State", "County", "Infested Area", "Genus", "Species"),
  Records_With_Data = c(
    sum(!is.na(eddmaps$Scientific_Name) & eddmaps$Scientific_Name != ""),
    sum(!is.na(eddmaps$SUB_name) & eddmaps$SUB_name != ""),
    sum(!is.na(eddmaps$ObservationDate)),
    sum(!is.na(eddmaps$x) & !is.na(eddmaps$y)),
    sum(!is.na(eddmaps$State_Name)),
    sum(!is.na(eddmaps$County_Name)),
    sum(!is.na(eddmaps$InfestedAreaAcres) & eddmaps$InfestedAreaAcres > 0),
    sum(!is.na(eddmaps$SUB_genus)),
    sum(!is.na(eddmaps$SUB_species))
  )
) %>%
  mutate(
    Percent_Complete = round(100 * Records_With_Data / nrow(eddmaps), 1),
    Quality_Rating = case_when(
      Percent_Complete >= 95 ~ "Excellent",
      Percent_Complete >= 80 ~ "Good",
      Percent_Complete >= 50 ~ "Fair",
      TRUE ~ "Poor"
    )
  )

quality_metrics %>%
  arrange(desc(Percent_Complete)) 

Temporal Data Quality

eddmaps_clean %>%
  mutate(
    has_area = !is.na(InfestedAreaAcres) & InfestedAreaAcres > 0,
    has_county = !is.na(County_Name)
  ) %>%
  group_by(year) %>%
  summarize(
    total = n(),
    with_area = sum(has_area),
    with_county = sum(has_county),
    .groups = "drop"
  ) %>%
  pivot_longer(cols = c(with_area, with_county),
               names_to = "metric", values_to = "count") %>%
  mutate(
    percent = 100 * count / total,
    metric = recode(metric,
                   "with_area" = "Has Area Data",
                   "with_county" = "Has County Info")
  ) %>%
  ggplot(aes(x = year, y = percent, color = metric, group = metric)) +
  geom_line(size = 1.2) +
  geom_point(size = 2.5) +
  scale_color_brewer(palette = "Set1") +
  labs(
    title = "Data Quality Trends Over Time",
    subtitle = "Percentage of records with additional information",
    x = "Year",
    y = "Percent of Records (%)",
    color = "Data Type"
  )

10. Knowledge Graph Insights

Patterns for Risk Assessment

Based on this exploration, here are key patterns that could feed into your knowledge graph:

Species Traits to Extract:

  • Spread rate: Year-over-year expansion in number of states
  • Geographic breadth: Number of unique states/counties
  • Infestation intensity: Acres infested per report
  • Detection timing: When first detected vs. when spreading
  • Seasonal activity: Peak reporting months (biological timing)

Spatial-Temporal Metrics:

kg_metrics <- eddmaps_clean %>%
  group_by(Scientific_Name, SUB_name) %>%
  summarize(
    n_reports = n(),
    n_states = n_distinct(State_Name),
    n_counties = n_distinct(County_Name),
    years_active = max(year, na.rm = TRUE) - min(year, na.rm = TRUE) + 1,
    first_year = min(year, na.rm = TRUE),
    latest_year = max(year, na.rm = TRUE),
    total_acres = sum(InfestedAreaAcres, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  mutate(
    spread_rate = n_states / pmax(years_active, 1),
    geographic_breadth = n_states,
    reporting_intensity = n_reports / pmax(years_active, 1)
  ) %>%
  arrange(desc(spread_rate))

kg_metrics %>%
  head(20) %>%
  select(SUB_name, Scientific_Name, n_states, years_active, spread_rate, 
         reporting_intensity, total_acres)

High-Risk Species Identification

Species with rapid spread and broad geographic range:

kg_metrics %>%
  filter(n_reports >= 10, years_active >= 3) %>%
  ggplot(aes(x = spread_rate, y = n_reports, 
             size = total_acres, color = years_active)) +
  geom_point(alpha = 0.6) +
  scale_color_viridis_c(option = "plasma") +
  scale_size_continuous(labels = comma) +
  scale_x_continuous(labels = comma) +
  scale_y_continuous(labels = comma) +
  labs(
    title = "High-Risk Species Profile",
    subtitle = "Spread rate vs. detection frequency (bubble size = infested area)",
    x = "Spread Rate (states/year)",
    y = "Number of Reports",
    size = "Total Acres",
    color = "Years Active"
  ) +
  theme(legend.position = "right")

11. Connection to Research Framework

How This Data Informs Your Knowledge Graph

For Arrival Assessment:

  • First detection dates → When species entered regions
  • Geographic coordinates → Climate matching with native range
  • State/county patterns → Human-mediated pathways

For Establishment Assessment:

  • Years active → Persistence in new environments
  • Report frequency → Population stability indicator
  • Seasonal patterns → Life cycle completion

For Spread Assessment:

  • States invaded over time → Dispersal rate
  • Infested area growth → Local population expansion
  • County-level patterns → Within-state movement

For Impact Assessment:

  • Total infested acres → Ecological footprint
  • Report intensity → Management concern level
  • Multi-year presence → Eradication difficulty

12. Next Steps for Knowledge Graph

Data Integration Priorities:

  1. Match to RIIS database → Get establishment means, pathways
  2. Link to USGS NAS → Get narrative vectors, transport info
  3. Connect to trait databases → Add fecundity, dispersal ability
  4. Overlay climate data → Climate matching scores
  5. Link to literature → Mechanistic evidence

Key Variables to Extract:

variables_table <- data.frame(
  Category = c("Species Identity", "Spatial", "Temporal", "Extent", 
               "Traits (to link)", "Risk Factors"),
  Variables = c(
    "Scientific name, genus, family, common name",
    "x, y coordinates, state, county, region",
    "First record, latest record, year, season",
    "Infested acres, number of sites, geographic spread",
    "Fecundity, dispersal, tolerance, pathways (from other sources)",
    "Spread rate, persistence, area invaded, detection lag"
  )
)

variables_table

Summary Statistics

cat("\n=== EDDMAPS DATA SUMMARY ===\n\n")
cat("Total Records:", nrow(eddmaps_clean), "\n")
cat("Unique Species:", n_species, "\n")
cat("States Covered:", n_distinct(eddmaps_clean$State_Name), "\n")
cat("Counties Covered:", n_distinct(eddmaps_clean$County_Name), "\n")
cat("Date Range:", format(min(eddmaps_clean$ObservationDate), "%Y-%m-%d"), "to", 
    format(max(eddmaps_clean$ObservationDate), "%Y-%m-%d"), "\n")
cat("Years of Data:", max(eddmaps_clean$year) - min(eddmaps_clean$year) + 1, "\n\n")

cat("Data Completeness:\n")
cat("  - With area data:", 
    round(100*sum(!is.na(eddmaps_clean$InfestedAreaAcres))/nrow(eddmaps_clean), 1), "%\n")
cat("  - With county info:", 
    round(100*sum(!is.na(eddmaps_clean$County_Name))/nrow(eddmaps_clean), 1), "%\n")
cat("  - With coordinates:", 
    round(100*sum(!is.na(eddmaps_clean$x) & !is.na(eddmaps_clean$y))/nrow(eddmaps_clean), 1), "%\n")
LS0tDQp0aXRsZTogIkVERE1hcFMgSW52YXNpdmUgU3BlY2llcyBEYXRhIEV4cGxvcmF0aW9uIg0Kc3VidGl0bGU6ICJVbmRlcnN0YW5kaW5nIE9jY3VycmVuY2UgUGF0dGVybnMgZm9yIEtub3dsZWRnZSBHcmFwaCBEZXZlbG9wbWVudCINCmF1dGhvcjogIkludmFzaXZlIFNwZWNpZXMgUmVzZWFyY2giDQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIFByb2plY3QgQ29udGV4dA0KDQpUaGlzIGFuYWx5c2lzIGV4cGxvcmVzICoqRURETWFwUyoqIChFYXJseSBEZXRlY3Rpb24gJiBEaXN0cmlidXRpb24gTWFwcGluZyBTeXN0ZW0pIGRhdGEgYXMgcGFydCBvZiBkZXZlbG9waW5nIGFuIGF1dG9tYXRlZCBpbnZhc2l2ZSBzcGVjaWVzIHJpc2sgYXNzZXNzbWVudCBzeXN0ZW0gdXNpbmcga25vd2xlZGdlIGdyYXBocy4NCg0KKipSZXNlYXJjaCBHb2FsOioqIEV4dHJhY3Qgc3BhdGlhbC10ZW1wb3JhbCBwYXR0ZXJucywgc3BlY2llcyB0cmFpdHMsIGFuZCBpbnZhc2lvbiBkeW5hbWljcyBmcm9tIG9jY3VycmVuY2UgZGF0YSB0byBmZWVkIGludG8gYSBrbm93bGVkZ2UgZ3JhcGggdGhhdCBwcmVkaWN0cyBzcGVjaWVzIGF0IHJpc2sgb2YgaW52YXNpb24uDQoNCioqS2V5IFF1ZXN0aW9uczoqKiAtIFdoYXQgc3BlY2llcyBhcmUgbW9zdCBmcmVxdWVudGx5IHJlcG9ydGVkPyAtIFdoZXJlIGFyZSBpbnZhc2lvbiBob3RzcG90cz8gLSBIb3cgZG8gaW52YXNpb25zIHNwcmVhZCBvdmVyIHRpbWU/IC0gV2hhdCBwYXR0ZXJucyBlbWVyZ2UgdGhhdCBjb3VsZCBpbmZvcm0gcmlzayBhc3Nlc3NtZW50Pw0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgZWNobyA9IFRSVUUsDQogIG1lc3NhZ2UgPSBUUlVFLA0KICB3YXJuaW5nID0gRkFMU0UsDQogIGZpZy53aWR0aCA9IDEyLA0KICBmaWcuaGVpZ2h0ID0gNywNCiAgZHBpID0gMTUwDQopDQpgYGANCg0KIyBMb2FkIFBhY2thZ2VzDQoNCmBgYHtyIHBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHN0cmluZ3IpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHNjYWxlcykNCmxpYnJhcnkodmlyaWRpcykNCmxpYnJhcnkoa25pdHIpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShtYXBzKQ0KDQojIFNldCB0aGVtZQ0KdGhlbWVfc2V0KHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTIpICsNCiAgICAgICAgICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE0KSwNCiAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBjb2xvciA9ICJncmF5MzAiKSwNCiAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSkNCmBgYA0KDQojIDEuIExvYWQgYW5kIEluc3BlY3QgRGF0YQ0KDQpgYGB7ciBsb2FkLWRhdGF9DQojIExvYWQgRURETWFwUyBkYXRhDQojIFVQREFURSBUSElTIFBBVEggdG8gbWF0Y2ggeW91ciBmaWxlIGxvY2F0aW9uDQplZGRtYXBzIDwtIHJlYWRfY3N2KCJDOi9Vc2Vycy9hbm5hc29rb2wvRG93bmxvYWRzL0VERE1hcFNQb2ludHNfdmlld182ODE4NzAxMjY5NDA1MDY2MTYwLmNzdiIsDQogICAgICAgICAgICAgICAgICAgIHNob3dfY29sX3R5cGVzID0gRkFMU0UpDQoNCmNhdCgi4pyTIERhdGEgbG9hZGVkIVxuIikNCmNhdCgiRGltZW5zaW9uczoiLCBucm93KGVkZG1hcHMpLCAicm93cyDDlyIsIG5jb2woZWRkbWFwcyksICJjb2x1bW5zXG4iKQ0KYGBgDQoNCiMjIERhdGEgU3RydWN0dXJlDQoNCmBgYHtyIHN0cnVjdHVyZX0NCiMgU2hvdyBjb2x1bW4gbmFtZXMgYW5kIHR5cGVzDQpnbGltcHNlKGVkZG1hcHMpDQpgYGANCg0KIyMgRmlyc3QgTG9vayBhdCB0aGUgRGF0YQ0KDQpgYGB7ciBoZWFkfQ0KaGVhZChlZGRtYXBzLCAyMCkNCmBgYA0KDQojIyBDb2x1bW4gRGVzY3JpcHRpb25zDQoNCmBgYHtyIGNvbHVtbi1pbmZvfQ0KIyBVbmRlcnN0YW5kIHdoYXQgZWFjaCBjb2x1bW4gY29udGFpbnMNCmRhdGEuZnJhbWUoDQogIENvbHVtbiA9IG5hbWVzKGVkZG1hcHMpLA0KICBUeXBlID0gc2FwcGx5KGVkZG1hcHMsIGNsYXNzKSwNCiAgU2FtcGxlX1ZhbHVlID0gc2FwcGx5KGVkZG1hcHMsIGZ1bmN0aW9uKHgpIGFzLmNoYXJhY3Rlcih4WzFdKSksDQogIE5vbl9NaXNzaW5nID0gc2FwcGx5KGVkZG1hcHMsIGZ1bmN0aW9uKHgpIHN1bSghaXMubmEoeCkpKSwNCiAgUGVyY2VudF9Db21wbGV0ZSA9IHJvdW5kKHNhcHBseShlZGRtYXBzLCBmdW5jdGlvbih4KSBzdW0oIWlzLm5hKHgpKS9sZW5ndGgoeCkqMTAwKSwgMSkNCikgJT4lDQogIGFycmFuZ2UoZGVzYyhQZXJjZW50X0NvbXBsZXRlKSkNCg0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIuIERhdGEgQ2xlYW5pbmcgJiBQcmVwYXJhdGlvbg0KDQpgYGB7ciBjbGVhbi1kYXRhfQ0KIyBDbGVhbiBhbmQgcHJlcGFyZSBkYXRhDQplZGRtYXBzX2NsZWFuIDwtIGVkZG1hcHMgJT4lDQogICMgUGFyc2UgZGF0ZXMgcHJvcGVybHkNCiAgbXV0YXRlKA0KICAgIE9ic2VydmF0aW9uRGF0ZSA9IGFzLkRhdGUoT2JzZXJ2YXRpb25EYXRlLCBmb3JtYXQgPSAiJW0vJWQvJVkiKSwNCiAgICBEYXRlX1VwZGF0ZWQgPSBhcy5EYXRlKERhdGVfVXBkYXRlZCwgZm9ybWF0ID0gIiVtLyVkLyVZIiksDQogICAgeWVhciA9IHllYXIoT2JzZXJ2YXRpb25EYXRlKSwNCiAgICBtb250aCA9IG1vbnRoKE9ic2VydmF0aW9uRGF0ZSwgbGFiZWwgPSBUUlVFKSwNCiAgICAjIEhhbmRsZSBpbmZlc3RlZCBhcmVhDQogICAgSW5mZXN0ZWRBcmVhQWNyZXMgPSBhcy5udW1lcmljKEluZmVzdGVkQXJlYUFjcmVzKSwNCiAgICAjIENsZWFuIHNwZWNpZXMgbmFtZXMNCiAgICBTY2llbnRpZmljX05hbWUgPSBzdHJfdHJpbShTY2llbnRpZmljX05hbWUpLA0KICAgIFNVQl9uYW1lID0gc3RyX3RyaW0oU1VCX25hbWUpDQogICkgJT4lDQogICMgUmVtb3ZlIHJlY29yZHMgd2l0aG91dCBkYXRlcyBvciBsb2NhdGlvbnMNCiAgZmlsdGVyKCFpcy5uYShPYnNlcnZhdGlvbkRhdGUpLCAhaXMubmEoeCksICFpcy5uYSh5KSkNCg0KY2F0KCLinJMgRGF0YSBjbGVhbmVkIVxuIikNCmNhdCgiUmVjb3JkcyBhZnRlciBjbGVhbmluZzoiLCBucm93KGVkZG1hcHNfY2xlYW4pLCAiXG4iKQ0KY2F0KCJEYXRlIHJhbmdlOiIsIG1pbihlZGRtYXBzX2NsZWFuJE9ic2VydmF0aW9uRGF0ZSwgbmEucm0gPSBUUlVFKSwgInRvIiwgDQogICAgbWF4KGVkZG1hcHNfY2xlYW4kT2JzZXJ2YXRpb25EYXRlLCBuYS5ybSA9IFRSVUUpLCAiXG4iKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDMuIFNwZWNpZXMgT3ZlcnZpZXcNCg0KIyMgSG93IE1hbnkgU3BlY2llcz8NCg0KYGBge3Igc3BlY2llcy1jb3VudH0NCm5fc3BlY2llcyA8LSBsZW5ndGgodW5pcXVlKGVkZG1hcHNfY2xlYW4kU2NpZW50aWZpY19OYW1lKSkNCm5fZ2VuZXJhIDwtIGxlbmd0aCh1bmlxdWUoZWRkbWFwc19jbGVhbiRTVUJfZ2VudXMpKQ0Kbl9mYW1pbGllcyA8LSBsZW5ndGgodW5pcXVlKGVkZG1hcHNfY2xlYW4kU1VCX25hbWUpKQ0KDQpjYXQoIlRvdGFsIHVuaXF1ZSBzcGVjaWVzOiIsIG5fc3BlY2llcywgIlxuIikNCmNhdCgiVG90YWwgZ2VuZXJhOiIsIG5fZ2VuZXJhLCAiXG4iKQ0KY2F0KCJUb3RhbCBjb21tb24gbmFtZXM6Iiwgbl9mYW1pbGllcywgIlxuIikNCmBgYA0KDQojIyBUb3AgMjAgTW9zdCBSZXBvcnRlZCBTcGVjaWVzDQoNCmBgYHtyIHRvcC1zcGVjaWVzLXRhYmxlfQ0KdG9wX3NwZWNpZXMgPC0gZWRkbWFwc19jbGVhbiAlPiUNCiAgY291bnQoU2NpZW50aWZpY19OYW1lLCBTVUJfbmFtZSwgc29ydCA9IFRSVUUpICU+JQ0KICBoZWFkKDIwKQ0KDQp0b3Bfc3BlY2llcw0KYGBgDQoNCmBgYHtyIHRvcC1zcGVjaWVzLXBsb3QsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQ0KZWRkbWFwc19jbGVhbiAlPiUNCiAgY291bnQoU2NpZW50aWZpY19OYW1lLCBTVUJfbmFtZSkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkgJT4lDQogIGhlYWQoMjApICU+JQ0KICBtdXRhdGUoDQogICAgbGFiZWwgPSBwYXN0ZTAoU1VCX25hbWUsICJcbigiLCBTY2llbnRpZmljX05hbWUsICIpIiksDQogICAgbGFiZWwgPSBzdHJfd3JhcChsYWJlbCwgd2lkdGggPSAzMCkNCiAgKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihsYWJlbCwgbiksIHkgPSBuLCBmaWxsID0gbikpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY29tbWEobikpLCBoanVzdCA9IC0wLjIsIHNpemUgPSAzLjUpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIsIGRpcmVjdGlvbiA9IC0xKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSwgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMTUpKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRvcCAyMCBNb3N0IFJlcG9ydGVkIEludmFzaXZlIFNwZWNpZXMgaW4gRURETWFwUyIsDQogICAgc3VidGl0bGUgPSAiTnVtYmVyIG9mIG9jY3VycmVuY2UgcmVjb3JkcyIsDQogICAgeCA9IE5VTEwsDQogICAgeSA9ICJOdW1iZXIgb2YgUmVwb3J0cyINCiAgKSArDQogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA0LiBHZW9ncmFwaGljIERpc3RyaWJ1dGlvbg0KDQojIyBSZXBvcnRzIGJ5IFN0YXRlDQoNCmBgYHtyIHN0YXRlLXN1bW1hcnl9DQpzdGF0ZV9zdW1tYXJ5IDwtIGVkZG1hcHNfY2xlYW4gJT4lDQogIGNvdW50KFN0YXRlX05hbWUsIG5hbWUgPSAicmVwb3J0cyIpICU+JQ0KICBhcnJhbmdlKGRlc2MocmVwb3J0cykpDQoNCnN0YXRlX3N1bW1hcnkgJT4lDQogIGhlYWQoMjApIA0KYGBgDQoNCmBgYHtyIHN0YXRlLXBsb3QsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQ0Kc3RhdGVfc3VtbWFyeSAlPiUNCiAgaGVhZCgyMCkgJT4lDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIoU3RhdGVfTmFtZSwgcmVwb3J0cyksIHkgPSByZXBvcnRzLCBmaWxsID0gcmVwb3J0cykpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY29tbWEocmVwb3J0cykpLCBoanVzdCA9IC0wLjIsIHNpemUgPSAzLjUpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiIzNCOUFCMiIsIGhpZ2ggPSAiI0UxQUYwMCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hLCBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4xNSkpKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSW52YXNpdmUgU3BlY2llcyBSZXBvcnRzIGJ5IFN0YXRlIiwNCiAgICBzdWJ0aXRsZSA9ICJUb3AgMjAgc3RhdGVzIHdpdGggbW9zdCBFRERNYXBTIHJlcG9ydHMiLA0KICAgIHggPSBOVUxMLA0KICAgIHkgPSAiTnVtYmVyIG9mIFJlcG9ydHMiDQogICkgKw0KICB0aGVtZShwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCkpDQpgYGANCg0KIyMgR2VvZ3JhcGhpYyBIb3RzcG90cyBNYXANCg0KYGBge3IgbWFwLWFsbCwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTEwfQ0KIyBHZXQgVVMgc3RhdGVzIG1hcA0KdXNfc3RhdGVzIDwtIG1hcF9kYXRhKCJzdGF0ZSIpDQoNCiMgQ3JlYXRlIGhleGJpbiBzdW1tYXJ5DQplZGRtYXBzX21hcCA8LSBlZGRtYXBzX2NsZWFuICU+JQ0KICBmaWx0ZXIoIWlzLm5hKHgpLCAhaXMubmEoeSkpDQoNCiMgUGxvdCBhbGwgb2NjdXJyZW5jZSBwb2ludHMNCmdncGxvdCgpICsNCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSB1c19zdGF0ZXMsIA0KICAgICAgICAgICAgICAgYWVzKHggPSBsb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwNCiAgICAgICAgICAgICAgIGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJncmF5NjAiLCBzaXplID0gMC4zKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGVkZG1hcHNfbWFwLCANCiAgICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5KSwgDQogICAgICAgICAgICAgY29sb3IgPSAiI0Q1NUUwMCIsIGFscGhhID0gMC4zLCBzaXplID0gMC44KSArDQogIGNvb3JkX21hcCgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJHZW9ncmFwaGljIERpc3RyaWJ1dGlvbiBvZiBJbnZhc2l2ZSBTcGVjaWVzIFJlcG9ydHMiLA0KICAgIHN1YnRpdGxlID0gcGFzdGUoIkFsbCIsIG5yb3coZWRkbWFwc19tYXApLCAib2NjdXJyZW5jZSByZWNvcmRzIGZyb20gRURETWFwUyIpLA0KICAgIHggPSAiTG9uZ2l0dWRlIiwNCiAgICB5ID0gIkxhdGl0dWRlIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiLCBzaXplID0gMTQpLA0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExKQ0KICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNS4gVGVtcG9yYWwgUGF0dGVybnMNCg0KIyMgUmVwb3J0cyBPdmVyIFRpbWUNCg0KYGBge3IgdGVtcG9yYWwtb3ZlcnZpZXd9DQp0ZW1wb3JhbF9zdW1tYXJ5IDwtIGVkZG1hcHNfY2xlYW4gJT4lDQogIGNvdW50KHllYXIpICU+JQ0KICBhcnJhbmdlKHllYXIpDQoNCnRlbXBvcmFsX3N1bW1hcnkNCmBgYA0KDQpgYGB7ciB0ZW1wb3JhbC1wbG90LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCmVkZG1hcHNfY2xlYW4gJT4lDQogIGNvdW50KHllYXIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbikpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMwMDcyQjIiLCBzaXplID0gMS4yKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiI0Q1NUUwMCIsIHNpemUgPSAzKSArDQogIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKG1pbihlZGRtYXBzX2NsZWFuJHllYXIsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGVkZG1hcHNfY2xlYW4keWVhciwgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAyKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkludmFzaXZlIFNwZWNpZXMgUmVwb3J0cyBPdmVyIFRpbWUiLA0KICAgIHN1YnRpdGxlID0gIkFubnVhbCB0cmVuZCBpbiBFRERNYXBTIHN1Ym1pc3Npb25zIiwNCiAgICB4ID0gIlllYXIiLA0KICAgIHkgPSAiTnVtYmVyIG9mIFJlcG9ydHMiDQogICkNCmBgYA0KDQojIyBTZWFzb25hbCBQYXR0ZXJucw0KDQpgYGB7ciBzZWFzb25hbC1wbG90LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCmVkZG1hcHNfY2xlYW4gJT4lDQogIGNvdW50KG1vbnRoKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gbW9udGgsIHkgPSBuLCBmaWxsID0gbikpICsNCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb24gPSAibWFnbWEiLCBkaXJlY3Rpb24gPSAtMSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJTZWFzb25hbCBQYXR0ZXJuIG9mIEludmFzaXZlIFNwZWNpZXMgUmVwb3J0cyIsDQogICAgc3VidGl0bGUgPSAiV2hlbiBhcmUgbW9zdCBvYnNlcnZhdGlvbnMgc3VibWl0dGVkPyIsDQogICAgeCA9ICJNb250aCIsDQogICAgeSA9ICJOdW1iZXIgb2YgUmVwb3J0cyINCiAgKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDYuIEludmFzaW9uIEV4dGVudCBBbmFseXNpcw0KDQojIyBJbmZlc3RlZCBBcmVhIERpc3RyaWJ1dGlvbg0KDQpgYGB7ciBpbmZlc3RlZC1hcmVhfQ0KYXJlYV9kYXRhIDwtIGVkZG1hcHNfY2xlYW4gJT4lDQogIGZpbHRlcighaXMubmEoSW5mZXN0ZWRBcmVhQWNyZXMpLCBJbmZlc3RlZEFyZWFBY3JlcyA+IDApDQoNCmNhdCgiUmVjb3JkcyB3aXRoIGFyZWEgZGF0YToiLCBucm93KGFyZWFfZGF0YSksIA0KICAgICJvdXQgb2YiLCBucm93KGVkZG1hcHNfY2xlYW4pLCANCiAgICAiKCIsIHJvdW5kKDEwMCpucm93KGFyZWFfZGF0YSkvbnJvdyhlZGRtYXBzX2NsZWFuKSwgMSksICIlKVxuIikNCg0KaWYobnJvdyhhcmVhX2RhdGEpID4gMCkgew0KICBhcmVhX3N1bW1hcnkgPC0gYXJlYV9kYXRhICU+JQ0KICAgIHN1bW1hcml6ZSgNCiAgICAgIG1pbl9hY3JlcyA9IG1pbihJbmZlc3RlZEFyZWFBY3JlcywgbmEucm0gPSBUUlVFKSwNCiAgICAgIG1lZGlhbl9hY3JlcyA9IG1lZGlhbihJbmZlc3RlZEFyZWFBY3JlcywgbmEucm0gPSBUUlVFKSwNCiAgICAgIG1lYW5fYWNyZXMgPSBtZWFuKEluZmVzdGVkQXJlYUFjcmVzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWF4X2FjcmVzID0gbWF4KEluZmVzdGVkQXJlYUFjcmVzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgdG90YWxfYWNyZXMgPSBzdW0oSW5mZXN0ZWRBcmVhQWNyZXMsIG5hLnJtID0gVFJVRSkNCiAgICApDQogIA0KICBhcmVhX3N1bW1hcnkgDQpgYGANCg0KYGBge3IgaW5mZXN0ZWQtYXJlYS1wbG90LCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9Nn0NCmlmKG5yb3coYXJlYV9kYXRhKSA+IDApIHsNCiAgYXJlYV9kYXRhICU+JQ0KICAgIGZpbHRlcihJbmZlc3RlZEFyZWFBY3JlcyA8IHF1YW50aWxlKEluZmVzdGVkQXJlYUFjcmVzLCAwLjk1LCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgICBnZ3Bsb3QoYWVzKHggPSBJbmZlc3RlZEFyZWFBY3JlcykpICsNCiAgICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gNTAsIGZpbGwgPSAiI0U2OUYwMCIsIGNvbG9yID0gIndoaXRlIikgKw0KICAgIHNjYWxlX3hfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKw0KICAgIGxhYnMoDQogICAgICB0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgSW5mZXN0ZWQgQXJlYSBTaXplcyIsDQogICAgICBzdWJ0aXRsZSA9ICJNb3N0IHJlcG9ydHMgKDk1dGggcGVyY2VudGlsZSBzaG93biB0byByZWR1Y2Ugb3V0bGllciBlZmZlY3QpIiwNCiAgICAgIHggPSAiSW5mZXN0ZWQgQXJlYSAoQWNyZXMpIiwNCiAgICAgIHkgPSAiTnVtYmVyIG9mIFJlcG9ydHMiDQogICAgKQ0KfQ0KYGBgDQoNCiMjIFNwZWNpZXMgd2l0aCBMYXJnZXN0IEluZmVzdGF0aW9ucw0KDQpgYGB7ciBsYXJnZXN0LWluZmVzdGF0aW9uc30NCmlmKG5yb3coYXJlYV9kYXRhKSA+IDApIHsNCiAgYXJlYV9kYXRhICU+JQ0KICAgIGdyb3VwX2J5KFNjaWVudGlmaWNfTmFtZSwgU1VCX25hbWUpICU+JQ0KICAgIHN1bW1hcml6ZSgNCiAgICAgIHRvdGFsX2FjcmVzID0gc3VtKEluZmVzdGVkQXJlYUFjcmVzLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgbWF4X3NpbmdsZSA9IG1heChJbmZlc3RlZEFyZWFBY3JlcywgbmEucm0gPSBUUlVFKSwNCiAgICAgIG5fcmVwb3J0cyA9IG4oKSwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICApICU+JQ0KICAgIGFycmFuZ2UoZGVzYyh0b3RhbF9hY3JlcykpICU+JQ0KICAgIGhlYWQoMTUpDQp9DQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgNy4gU3BlY2llcy1TcGVjaWZpYyBEZWVwIERpdmVzDQoNCiMjIFBpY2sgYSBTcGVjaWVzIHRvIEFuYWx5emUgaW4gRGV0YWlsDQoNCkxldCdzIGFuYWx5emUgdGhlIG1vc3QgcmVwb3J0ZWQgc3BlY2llcyBpbiBkZXRhaWw6DQoNCmBgYHtyIGZvY3VzLXNwZWNpZXN9DQojIEdldCBtb3N0IHJlcG9ydGVkIHNwZWNpZXMNCmZvY3VzX3NwZWNpZXNfbmFtZSA8LSB0b3Bfc3BlY2llcyRTY2llbnRpZmljX05hbWVbMV0NCmZvY3VzX2NvbW1vbl9uYW1lIDwtIHRvcF9zcGVjaWVzJFNVQl9uYW1lWzFdDQoNCmNhdCgiQW5hbHl6aW5nOiIsIGZvY3VzX3NwZWNpZXNfbmFtZSwgIigiLCBmb2N1c19jb21tb25fbmFtZSwgIilcbiIpDQoNCnNwZWNpZXNfZGF0YSA8LSBlZGRtYXBzX2NsZWFuICU+JQ0KICBmaWx0ZXIoU2NpZW50aWZpY19OYW1lID09IGZvY3VzX3NwZWNpZXNfbmFtZSkNCg0KY2F0KCJUb3RhbCByZXBvcnRzOiIsIG5yb3coc3BlY2llc19kYXRhKSwgIlxuIikNCmBgYA0KDQojIyMgR2VvZ3JhcGhpYyBEaXN0cmlidXRpb24gb2YgYHIgZm9jdXNfY29tbW9uX25hbWVgDQoNCmBgYHtyIHNwZWNpZXMtbWFwLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9MTB9DQpnZ3Bsb3QoKSArDQogIGdlb21fcG9seWdvbihkYXRhID0gdXNfc3RhdGVzLCANCiAgICAgICAgICAgICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCksDQogICAgICAgICAgICAgICBmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiZ3JheTYwIiwgc2l6ZSA9IDAuMykgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBzcGVjaWVzX2RhdGEsIA0KICAgICAgICAgICAgIGFlcyh4ID0geCwgeSA9IHksIGNvbG9yID0geWVhciksIA0KICAgICAgICAgICAgIGFscGhhID0gMC42LCBzaXplID0gMikgKw0KICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gInBsYXNtYSIpICsNCiAgY29vcmRfbWFwKCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gcGFzdGUoIkRpc3RyaWJ1dGlvbiBvZiIsIGZvY3VzX2NvbW1vbl9uYW1lKSwNCiAgICBzdWJ0aXRsZSA9IHBhc3RlKGZvY3VzX3NwZWNpZXNfbmFtZSwgIi0gQWxsIEVERE1hcFMgcmVwb3J0cyIpLA0KICAgIHggPSAiTG9uZ2l0dWRlIiwNCiAgICB5ID0gIkxhdGl0dWRlIiwNCiAgICBjb2xvciA9ICJZZWFyIg0KICApICsNCiAgdGhlbWVfbWluaW1hbCgpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgOC4gTXVsdGktU3BlY2llcyBDb21wYXJpc29uDQoNCiMjIENvbXBhcmUgVG9wIDUgU3BlY2llcw0KDQpgYGB7ciB0b3A1LWNvbXBhcmlzb259DQp0b3A1X3NwZWNpZXMgPC0gdG9wX3NwZWNpZXMkU2NpZW50aWZpY19OYW1lWzE6NV0NCg0KY29tcGFyaXNvbl9kYXRhIDwtIGVkZG1hcHNfY2xlYW4gJT4lDQogIGZpbHRlcihTY2llbnRpZmljX05hbWUgJWluJSB0b3A1X3NwZWNpZXMpICU+JQ0KICBjb3VudCh5ZWFyLCBTY2llbnRpZmljX05hbWUsIFNVQl9uYW1lKQ0KYGBgDQoNCiMjIyBUZW1wb3JhbCBUcmVuZHMNCg0KYGBge3IgdG9wNS10ZW1wb3JhbCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTd9DQpjb21wYXJpc29uX2RhdGEgJT4lDQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBuLCBjb2xvciA9IFNVQl9uYW1lLCBncm91cCA9IFNVQl9uYW1lKSkgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEuMikgKw0KICBnZW9tX3BvaW50KHNpemUgPSAyLjUpICsNCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKG9wdGlvbiA9ICJ0dXJibyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVGVtcG9yYWwgVHJlbmRzOiBUb3AgNSBNb3N0IFJlcG9ydGVkIFNwZWNpZXMiLA0KICAgIHN1YnRpdGxlID0gIkhvdyBoYXZlIHJlcG9ydCBmcmVxdWVuY2llcyBjaGFuZ2VkIG92ZXIgdGltZT8iLA0KICAgIHggPSAiWWVhciIsDQogICAgeSA9ICJOdW1iZXIgb2YgUmVwb3J0cyIsDQogICAgY29sb3IgPSAiU3BlY2llcyINCiAgKQ0KYGBgDQoNCiMjIyBHZW9ncmFwaGljIFNwcmVhZA0KDQpgYGB7ciB0b3A1LXN0YXRlcywgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9DQplZGRtYXBzX2NsZWFuICU+JQ0KICBmaWx0ZXIoU2NpZW50aWZpY19OYW1lICVpbiUgdG9wNV9zcGVjaWVzKSAlPiUNCiAgY291bnQoU3RhdGVfTmFtZSwgU1VCX25hbWUpICU+JQ0KICBncm91cF9ieShTVUJfbmFtZSkgJT4lDQogIHRvcF9uKDEwLCBuKSAlPiUNCiAgdW5ncm91cCgpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSByZW9yZGVyX3dpdGhpbihTdGF0ZV9OYW1lLCBuLCBTVUJfbmFtZSksIHkgPSBuLCBmaWxsID0gU1VCX25hbWUpKSArDQogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgY29vcmRfZmxpcCgpICsNCiAgc2NhbGVfeF9yZW9yZGVyZWQoKSArDQogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKG9wdGlvbiA9ICJ0dXJibyIpICsNCiAgZmFjZXRfd3JhcCh+U1VCX25hbWUsIHNjYWxlcyA9ICJmcmVlX3kiLCBuY29sID0gMikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRvcCBTdGF0ZXMgZm9yIEVhY2ggb2YgdGhlIDUgTW9zdCBSZXBvcnRlZCBTcGVjaWVzIiwNCiAgICB4ID0gTlVMTCwNCiAgICB5ID0gIk51bWJlciBvZiBSZXBvcnRzIg0KICApICsNCiAgdGhlbWUoc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA5LiBEYXRhIFF1YWxpdHkgQXNzZXNzbWVudA0KDQojIyBDb21wbGV0ZW5lc3MgQW5hbHlzaXMNCg0KYGBge3IgZGF0YS1xdWFsaXR5fQ0KcXVhbGl0eV9tZXRyaWNzIDwtIGRhdGEuZnJhbWUoDQogIEZpZWxkID0gYygiU2NpZW50aWZpYyBOYW1lIiwgIkNvbW1vbiBOYW1lIiwgIkRhdGUiLCAiTG9jYXRpb24gKHgseSkiLCANCiAgICAgICAgICAgICJTdGF0ZSIsICJDb3VudHkiLCAiSW5mZXN0ZWQgQXJlYSIsICJHZW51cyIsICJTcGVjaWVzIiksDQogIFJlY29yZHNfV2l0aF9EYXRhID0gYygNCiAgICBzdW0oIWlzLm5hKGVkZG1hcHMkU2NpZW50aWZpY19OYW1lKSAmIGVkZG1hcHMkU2NpZW50aWZpY19OYW1lICE9ICIiKSwNCiAgICBzdW0oIWlzLm5hKGVkZG1hcHMkU1VCX25hbWUpICYgZWRkbWFwcyRTVUJfbmFtZSAhPSAiIiksDQogICAgc3VtKCFpcy5uYShlZGRtYXBzJE9ic2VydmF0aW9uRGF0ZSkpLA0KICAgIHN1bSghaXMubmEoZWRkbWFwcyR4KSAmICFpcy5uYShlZGRtYXBzJHkpKSwNCiAgICBzdW0oIWlzLm5hKGVkZG1hcHMkU3RhdGVfTmFtZSkpLA0KICAgIHN1bSghaXMubmEoZWRkbWFwcyRDb3VudHlfTmFtZSkpLA0KICAgIHN1bSghaXMubmEoZWRkbWFwcyRJbmZlc3RlZEFyZWFBY3JlcykgJiBlZGRtYXBzJEluZmVzdGVkQXJlYUFjcmVzID4gMCksDQogICAgc3VtKCFpcy5uYShlZGRtYXBzJFNVQl9nZW51cykpLA0KICAgIHN1bSghaXMubmEoZWRkbWFwcyRTVUJfc3BlY2llcykpDQogICkNCikgJT4lDQogIG11dGF0ZSgNCiAgICBQZXJjZW50X0NvbXBsZXRlID0gcm91bmQoMTAwICogUmVjb3Jkc19XaXRoX0RhdGEgLyBucm93KGVkZG1hcHMpLCAxKSwNCiAgICBRdWFsaXR5X1JhdGluZyA9IGNhc2Vfd2hlbigNCiAgICAgIFBlcmNlbnRfQ29tcGxldGUgPj0gOTUgfiAiRXhjZWxsZW50IiwNCiAgICAgIFBlcmNlbnRfQ29tcGxldGUgPj0gODAgfiAiR29vZCIsDQogICAgICBQZXJjZW50X0NvbXBsZXRlID49IDUwIH4gIkZhaXIiLA0KICAgICAgVFJVRSB+ICJQb29yIg0KICAgICkNCiAgKQ0KDQpxdWFsaXR5X21ldHJpY3MgJT4lDQogIGFycmFuZ2UoZGVzYyhQZXJjZW50X0NvbXBsZXRlKSkgDQoNCmBgYA0KDQojIyBUZW1wb3JhbCBEYXRhIFF1YWxpdHkNCg0KYGBge3IgdGVtcG9yYWwtcXVhbGl0eSwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTZ9DQplZGRtYXBzX2NsZWFuICU+JQ0KICBtdXRhdGUoDQogICAgaGFzX2FyZWEgPSAhaXMubmEoSW5mZXN0ZWRBcmVhQWNyZXMpICYgSW5mZXN0ZWRBcmVhQWNyZXMgPiAwLA0KICAgIGhhc19jb3VudHkgPSAhaXMubmEoQ291bnR5X05hbWUpDQogICkgJT4lDQogIGdyb3VwX2J5KHllYXIpICU+JQ0KICBzdW1tYXJpemUoDQogICAgdG90YWwgPSBuKCksDQogICAgd2l0aF9hcmVhID0gc3VtKGhhc19hcmVhKSwNCiAgICB3aXRoX2NvdW50eSA9IHN1bShoYXNfY291bnR5KSwNCiAgICAuZ3JvdXBzID0gImRyb3AiDQogICkgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyh3aXRoX2FyZWEsIHdpdGhfY291bnR5KSwNCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1ldHJpYyIsIHZhbHVlc190byA9ICJjb3VudCIpICU+JQ0KICBtdXRhdGUoDQogICAgcGVyY2VudCA9IDEwMCAqIGNvdW50IC8gdG90YWwsDQogICAgbWV0cmljID0gcmVjb2RlKG1ldHJpYywNCiAgICAgICAgICAgICAgICAgICAid2l0aF9hcmVhIiA9ICJIYXMgQXJlYSBEYXRhIiwNCiAgICAgICAgICAgICAgICAgICAid2l0aF9jb3VudHkiID0gIkhhcyBDb3VudHkgSW5mbyIpDQogICkgJT4lDQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBwZXJjZW50LCBjb2xvciA9IG1ldHJpYywgZ3JvdXAgPSBtZXRyaWMpKSArDQogIGdlb21fbGluZShzaXplID0gMS4yKSArDQogIGdlb21fcG9pbnQoc2l6ZSA9IDIuNSkgKw0KICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkRhdGEgUXVhbGl0eSBUcmVuZHMgT3ZlciBUaW1lIiwNCiAgICBzdWJ0aXRsZSA9ICJQZXJjZW50YWdlIG9mIHJlY29yZHMgd2l0aCBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIiwNCiAgICB4ID0gIlllYXIiLA0KICAgIHkgPSAiUGVyY2VudCBvZiBSZWNvcmRzICglKSIsDQogICAgY29sb3IgPSAiRGF0YSBUeXBlIg0KICApDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMTAuIEtub3dsZWRnZSBHcmFwaCBJbnNpZ2h0cw0KDQojIyBQYXR0ZXJucyBmb3IgUmlzayBBc3Nlc3NtZW50DQoNCkJhc2VkIG9uIHRoaXMgZXhwbG9yYXRpb24sIGhlcmUgYXJlIGtleSBwYXR0ZXJucyB0aGF0IGNvdWxkIGZlZWQgaW50byB5b3VyIGtub3dsZWRnZSBncmFwaDoNCg0KIyMjIFNwZWNpZXMgVHJhaXRzIHRvIEV4dHJhY3Q6DQoNCi0gICAqKlNwcmVhZCByYXRlKio6IFllYXItb3Zlci15ZWFyIGV4cGFuc2lvbiBpbiBudW1iZXIgb2Ygc3RhdGVzDQotICAgKipHZW9ncmFwaGljIGJyZWFkdGgqKjogTnVtYmVyIG9mIHVuaXF1ZSBzdGF0ZXMvY291bnRpZXMNCi0gICAqKkluZmVzdGF0aW9uIGludGVuc2l0eSoqOiBBY3JlcyBpbmZlc3RlZCBwZXIgcmVwb3J0DQotICAgKipEZXRlY3Rpb24gdGltaW5nKio6IFdoZW4gZmlyc3QgZGV0ZWN0ZWQgdnMuIHdoZW4gc3ByZWFkaW5nDQotICAgKipTZWFzb25hbCBhY3Rpdml0eSoqOiBQZWFrIHJlcG9ydGluZyBtb250aHMgKGJpb2xvZ2ljYWwgdGltaW5nKQ0KDQojIyMgU3BhdGlhbC1UZW1wb3JhbCBNZXRyaWNzOg0KDQpgYGB7ciBrZy1tZXRyaWNzfQ0Ka2dfbWV0cmljcyA8LSBlZGRtYXBzX2NsZWFuICU+JQ0KICBncm91cF9ieShTY2llbnRpZmljX05hbWUsIFNVQl9uYW1lKSAlPiUNCiAgc3VtbWFyaXplKA0KICAgIG5fcmVwb3J0cyA9IG4oKSwNCiAgICBuX3N0YXRlcyA9IG5fZGlzdGluY3QoU3RhdGVfTmFtZSksDQogICAgbl9jb3VudGllcyA9IG5fZGlzdGluY3QoQ291bnR5X05hbWUpLA0KICAgIHllYXJzX2FjdGl2ZSA9IG1heCh5ZWFyLCBuYS5ybSA9IFRSVUUpIC0gbWluKHllYXIsIG5hLnJtID0gVFJVRSkgKyAxLA0KICAgIGZpcnN0X3llYXIgPSBtaW4oeWVhciwgbmEucm0gPSBUUlVFKSwNCiAgICBsYXRlc3RfeWVhciA9IG1heCh5ZWFyLCBuYS5ybSA9IFRSVUUpLA0KICAgIHRvdGFsX2FjcmVzID0gc3VtKEluZmVzdGVkQXJlYUFjcmVzLCBuYS5ybSA9IFRSVUUpLA0KICAgIC5ncm91cHMgPSAiZHJvcCINCiAgKSAlPiUNCiAgbXV0YXRlKA0KICAgIHNwcmVhZF9yYXRlID0gbl9zdGF0ZXMgLyBwbWF4KHllYXJzX2FjdGl2ZSwgMSksDQogICAgZ2VvZ3JhcGhpY19icmVhZHRoID0gbl9zdGF0ZXMsDQogICAgcmVwb3J0aW5nX2ludGVuc2l0eSA9IG5fcmVwb3J0cyAvIHBtYXgoeWVhcnNfYWN0aXZlLCAxKQ0KICApICU+JQ0KICBhcnJhbmdlKGRlc2Moc3ByZWFkX3JhdGUpKQ0KDQprZ19tZXRyaWNzICU+JQ0KICBoZWFkKDIwKSAlPiUNCiAgc2VsZWN0KFNVQl9uYW1lLCBTY2llbnRpZmljX05hbWUsIG5fc3RhdGVzLCB5ZWFyc19hY3RpdmUsIHNwcmVhZF9yYXRlLCANCiAgICAgICAgIHJlcG9ydGluZ19pbnRlbnNpdHksIHRvdGFsX2FjcmVzKQ0KDQpgYGANCg0KIyMjIEhpZ2gtUmlzayBTcGVjaWVzIElkZW50aWZpY2F0aW9uDQoNClNwZWNpZXMgd2l0aCByYXBpZCBzcHJlYWQgYW5kIGJyb2FkIGdlb2dyYXBoaWMgcmFuZ2U6DQoNCmBgYHtyIGhpZ2gtcmlzay1zcGVjaWVzLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0NCmtnX21ldHJpY3MgJT4lDQogIGZpbHRlcihuX3JlcG9ydHMgPj0gMTAsIHllYXJzX2FjdGl2ZSA+PSAzKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gc3ByZWFkX3JhdGUsIHkgPSBuX3JlcG9ydHMsIA0KICAgICAgICAgICAgIHNpemUgPSB0b3RhbF9hY3JlcywgY29sb3IgPSB5ZWFyc19hY3RpdmUpKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYpICsNCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9ICJwbGFzbWEiKSArDQogIHNjYWxlX3NpemVfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSGlnaC1SaXNrIFNwZWNpZXMgUHJvZmlsZSIsDQogICAgc3VidGl0bGUgPSAiU3ByZWFkIHJhdGUgdnMuIGRldGVjdGlvbiBmcmVxdWVuY3kgKGJ1YmJsZSBzaXplID0gaW5mZXN0ZWQgYXJlYSkiLA0KICAgIHggPSAiU3ByZWFkIFJhdGUgKHN0YXRlcy95ZWFyKSIsDQogICAgeSA9ICJOdW1iZXIgb2YgUmVwb3J0cyIsDQogICAgc2l6ZSA9ICJUb3RhbCBBY3JlcyIsDQogICAgY29sb3IgPSAiWWVhcnMgQWN0aXZlIg0KICApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAxMS4gQ29ubmVjdGlvbiB0byBSZXNlYXJjaCBGcmFtZXdvcmsNCg0KIyMgSG93IFRoaXMgRGF0YSBJbmZvcm1zIFlvdXIgS25vd2xlZGdlIEdyYXBoDQoNCiMjIyBGb3IgQXJyaXZhbCBBc3Nlc3NtZW50Og0KDQotICAgKipGaXJzdCBkZXRlY3Rpb24gZGF0ZXMqKiDihpIgV2hlbiBzcGVjaWVzIGVudGVyZWQgcmVnaW9ucw0KLSAgICoqR2VvZ3JhcGhpYyBjb29yZGluYXRlcyoqIOKGkiBDbGltYXRlIG1hdGNoaW5nIHdpdGggbmF0aXZlIHJhbmdlDQotICAgKipTdGF0ZS9jb3VudHkgcGF0dGVybnMqKiDihpIgSHVtYW4tbWVkaWF0ZWQgcGF0aHdheXMNCg0KIyMjIEZvciBFc3RhYmxpc2htZW50IEFzc2Vzc21lbnQ6DQoNCi0gICAqKlllYXJzIGFjdGl2ZSoqIOKGkiBQZXJzaXN0ZW5jZSBpbiBuZXcgZW52aXJvbm1lbnRzDQotICAgKipSZXBvcnQgZnJlcXVlbmN5Kiog4oaSIFBvcHVsYXRpb24gc3RhYmlsaXR5IGluZGljYXRvcg0KLSAgICoqU2Vhc29uYWwgcGF0dGVybnMqKiDihpIgTGlmZSBjeWNsZSBjb21wbGV0aW9uDQoNCiMjIyBGb3IgU3ByZWFkIEFzc2Vzc21lbnQ6DQoNCi0gICAqKlN0YXRlcyBpbnZhZGVkIG92ZXIgdGltZSoqIOKGkiBEaXNwZXJzYWwgcmF0ZQ0KLSAgICoqSW5mZXN0ZWQgYXJlYSBncm93dGgqKiDihpIgTG9jYWwgcG9wdWxhdGlvbiBleHBhbnNpb24NCi0gICAqKkNvdW50eS1sZXZlbCBwYXR0ZXJucyoqIOKGkiBXaXRoaW4tc3RhdGUgbW92ZW1lbnQNCg0KIyMjIEZvciBJbXBhY3QgQXNzZXNzbWVudDoNCg0KLSAgICoqVG90YWwgaW5mZXN0ZWQgYWNyZXMqKiDihpIgRWNvbG9naWNhbCBmb290cHJpbnQNCi0gICAqKlJlcG9ydCBpbnRlbnNpdHkqKiDihpIgTWFuYWdlbWVudCBjb25jZXJuIGxldmVsDQotICAgKipNdWx0aS15ZWFyIHByZXNlbmNlKiog4oaSIEVyYWRpY2F0aW9uIGRpZmZpY3VsdHkNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMgMTIuIE5leHQgU3RlcHMgZm9yIEtub3dsZWRnZSBHcmFwaA0KDQojIyBEYXRhIEludGVncmF0aW9uIFByaW9yaXRpZXM6DQoNCjEuICAqKk1hdGNoIHRvIFJJSVMgZGF0YWJhc2UqKiDihpIgR2V0IGVzdGFibGlzaG1lbnQgbWVhbnMsIHBhdGh3YXlzDQoyLiAgKipMaW5rIHRvIFVTR1MgTkFTKiog4oaSIEdldCBuYXJyYXRpdmUgdmVjdG9ycywgdHJhbnNwb3J0IGluZm8NCjMuICAqKkNvbm5lY3QgdG8gdHJhaXQgZGF0YWJhc2VzKiog4oaSIEFkZCBmZWN1bmRpdHksIGRpc3BlcnNhbCBhYmlsaXR5DQo0LiAgKipPdmVybGF5IGNsaW1hdGUgZGF0YSoqIOKGkiBDbGltYXRlIG1hdGNoaW5nIHNjb3Jlcw0KNS4gICoqTGluayB0byBsaXRlcmF0dXJlKiog4oaSIE1lY2hhbmlzdGljIGV2aWRlbmNlDQoNCiMjIEtleSBWYXJpYWJsZXMgdG8gRXh0cmFjdDoNCg0KYGBge3IgdmFyaWFibGVzLW5lZWRlZH0NCnZhcmlhYmxlc190YWJsZSA8LSBkYXRhLmZyYW1lKA0KICBDYXRlZ29yeSA9IGMoIlNwZWNpZXMgSWRlbnRpdHkiLCAiU3BhdGlhbCIsICJUZW1wb3JhbCIsICJFeHRlbnQiLCANCiAgICAgICAgICAgICAgICJUcmFpdHMgKHRvIGxpbmspIiwgIlJpc2sgRmFjdG9ycyIpLA0KICBWYXJpYWJsZXMgPSBjKA0KICAgICJTY2llbnRpZmljIG5hbWUsIGdlbnVzLCBmYW1pbHksIGNvbW1vbiBuYW1lIiwNCiAgICAieCwgeSBjb29yZGluYXRlcywgc3RhdGUsIGNvdW50eSwgcmVnaW9uIiwNCiAgICAiRmlyc3QgcmVjb3JkLCBsYXRlc3QgcmVjb3JkLCB5ZWFyLCBzZWFzb24iLA0KICAgICJJbmZlc3RlZCBhY3JlcywgbnVtYmVyIG9mIHNpdGVzLCBnZW9ncmFwaGljIHNwcmVhZCIsDQogICAgIkZlY3VuZGl0eSwgZGlzcGVyc2FsLCB0b2xlcmFuY2UsIHBhdGh3YXlzIChmcm9tIG90aGVyIHNvdXJjZXMpIiwNCiAgICAiU3ByZWFkIHJhdGUsIHBlcnNpc3RlbmNlLCBhcmVhIGludmFkZWQsIGRldGVjdGlvbiBsYWciDQogICkNCikNCg0KdmFyaWFibGVzX3RhYmxlDQoNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyBTdW1tYXJ5IFN0YXRpc3RpY3MNCg0KYGBge3IgZmluYWwtc3VtbWFyeX0NCmNhdCgiXG49PT0gRURETUFQUyBEQVRBIFNVTU1BUlkgPT09XG5cbiIpDQpjYXQoIlRvdGFsIFJlY29yZHM6IiwgbnJvdyhlZGRtYXBzX2NsZWFuKSwgIlxuIikNCmNhdCgiVW5pcXVlIFNwZWNpZXM6Iiwgbl9zcGVjaWVzLCAiXG4iKQ0KY2F0KCJTdGF0ZXMgQ292ZXJlZDoiLCBuX2Rpc3RpbmN0KGVkZG1hcHNfY2xlYW4kU3RhdGVfTmFtZSksICJcbiIpDQpjYXQoIkNvdW50aWVzIENvdmVyZWQ6Iiwgbl9kaXN0aW5jdChlZGRtYXBzX2NsZWFuJENvdW50eV9OYW1lKSwgIlxuIikNCmNhdCgiRGF0ZSBSYW5nZToiLCBmb3JtYXQobWluKGVkZG1hcHNfY2xlYW4kT2JzZXJ2YXRpb25EYXRlKSwgIiVZLSVtLSVkIiksICJ0byIsIA0KICAgIGZvcm1hdChtYXgoZWRkbWFwc19jbGVhbiRPYnNlcnZhdGlvbkRhdGUpLCAiJVktJW0tJWQiKSwgIlxuIikNCmNhdCgiWWVhcnMgb2YgRGF0YToiLCBtYXgoZWRkbWFwc19jbGVhbiR5ZWFyKSAtIG1pbihlZGRtYXBzX2NsZWFuJHllYXIpICsgMSwgIlxuXG4iKQ0KDQpjYXQoIkRhdGEgQ29tcGxldGVuZXNzOlxuIikNCmNhdCgiICAtIFdpdGggYXJlYSBkYXRhOiIsIA0KICAgIHJvdW5kKDEwMCpzdW0oIWlzLm5hKGVkZG1hcHNfY2xlYW4kSW5mZXN0ZWRBcmVhQWNyZXMpKS9ucm93KGVkZG1hcHNfY2xlYW4pLCAxKSwgIiVcbiIpDQpjYXQoIiAgLSBXaXRoIGNvdW50eSBpbmZvOiIsIA0KICAgIHJvdW5kKDEwMCpzdW0oIWlzLm5hKGVkZG1hcHNfY2xlYW4kQ291bnR5X05hbWUpKS9ucm93KGVkZG1hcHNfY2xlYW4pLCAxKSwgIiVcbiIpDQpjYXQoIiAgLSBXaXRoIGNvb3JkaW5hdGVzOiIsIA0KICAgIHJvdW5kKDEwMCpzdW0oIWlzLm5hKGVkZG1hcHNfY2xlYW4keCkgJiAhaXMubmEoZWRkbWFwc19jbGVhbiR5KSkvbnJvdyhlZGRtYXBzX2NsZWFuKSwgMSksICIlXG4iKQ0KYGBgDQo=