Cyclist MidBlock

group_data <- data %>%
  filter(mode == "Cyclist", intersection == "No") # Filter data for Cyclist MidBlock

# Define the group_vars 
group_vars <- c("oneway", "twoways", "clearzone", "onstreetparking", 
                "parkinglotgarage", "concretebarrier", "laneofparkedcars", 
                "bikelanewithbarrier", "offroadbikepathcycletrack", "raisedbikelane", 
                "twowayprotectedbicyclelane", "onewayprotectedbicyclelane", 
                "bufferedbicyclelaneadjacenttocurb", "bufferedbicyclelaneoffsetfromcurb", 
                "paintedbicyclelaneadjacenttocurb", "paintedbicyclelaneoffsetfromcurb", 
                "bikeaccessibleshoulder", "sharedlanewithmarkings", "onelane", "twolanes", 
                "threelanes", "fourlanes", "fivelanes", "sixlanes")

# Grouping the variables by category
categories <- list(
  "Road Direction" = c("oneway", "twoways"),
  "Number of Lanes" = c("onelane", "twolanes", "threelanes", "fourlanes", "fivelanes", "sixlanes"),
  "Cyclist Infrastructure" = c("bikelanewithbarrier", "offroadbikepathcycletrack", 
                               "raisedbikelane", "twowayprotectedbicyclelane", 
                               "onewayprotectedbicyclelane", "bufferedbicyclelaneadjacenttocurb", 
                               "bufferedbicyclelaneoffsetfromcurb", 
                               "paintedbicyclelaneadjacenttocurb", 
                               "paintedbicyclelaneoffsetfromcurb", "bikeaccessibleshoulder", 
                               "sharedlanewithmarkings"),
  "Other Variables" = setdiff(group_vars, c("oneway", "twoways", "onelane", "twolanes", "threelanes", "fourlanes", "fivelanes", "sixlanes", "bikelanewithbarrier", "offroadbikepathcycletrack", 
                               "raisedbikelane", "twowayprotectedbicyclelane", 
                               "onewayprotectedbicyclelane", "bufferedbicyclelaneadjacenttocurb", 
                               "bufferedbicyclelaneoffsetfromcurb", 
                               "paintedbicyclelaneadjacenttocurb", 
                               "paintedbicyclelaneoffsetfromcurb", "bikeaccessibleshoulder", 
                               "sharedlanewithmarkings"))
)

odds_ratios_list <- list()
standard_columns <- c("Category", "Variable", "Level", "Injury", "Injury_Percent", "Non-Injury", "Non-Injury_Percent", "OddsRatio", "Lower", "Upper", "PValue")

for (category in names(categories)) {
  for (var in categories[[category]]) {
    if (var %in% names(group_data)) {
      odds_ratio_result <- tryCatch({
        if (n_distinct(group_data[[var]]) == 2) {
          odds_data <- group_data %>%
            select(all_of(var), crashseverity) %>%
            table() %>%
            as.matrix()
          
          if (all(dim(odds_data) == c(2, 2))) {  # Ensure the table has the correct dimensions
            
            
            
            # Use epitab to calculate odds ratio
            odds_ratio_table <- as.data.frame(epitab(odds_data, method = "oddsratio")$tab) %>%
              mutate(across(where(is.numeric), round, 4))
            
            odds_ratio_table <- cbind(Category = category, Variable = var, Level = rownames(odds_ratio_table), odds_ratio_table)
            names(odds_ratio_table) <- standard_columns
            odds_ratio_table
          } else {
            data.frame(
              Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
              Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
              Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
            )
          }
        } else {
          data.frame(
            Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
            Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
            Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
          )
        }
      }, error = function(e) {
        data.frame(
          Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
          Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
          Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
        )
      })
      
      odds_ratios_list[[var]] <- odds_ratio_result
    }
  }
}
Warning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrect
odds_ratios_data <- do.call(rbind, odds_ratios_list) # Combine results into a single data frame


odds_ratios_data$OddsRatio_CI <- ifelse(is.na(odds_ratios_data$OddsRatio), "-", 
                                        paste0(odds_ratios_data$OddsRatio, " (", odds_ratios_data$Lower, " - ", odds_ratios_data$Upper, ")"))

odds_ratios_data <- odds_ratios_data %>% select(-Lower, -Upper) # Drop Lower and Upper columns

row_colors <- rep(c("#f9f9f9", "#ffffff"), length.out = nrow(odds_ratios_data))
odds_ratios_data$Variable <- ifelse(duplicated(odds_ratios_data$Variable), "", odds_ratios_data$Variable)
odds_ratios_data$Category <- ifelse(duplicated(odds_ratios_data$Category), "", odds_ratios_data$Category) 


final_table_html <- htmlTable(
  odds_ratios_data %>% 
    mutate(Injury_Percent_Combined = paste0(Injury, " - (", Injury_Percent, ")")) %>% # Combine into a single column 
    mutate(Non_Injury_Percent_Combined = paste0(`Non-Injury`, " - (", `Non-Injury_Percent`, ")")) %>%
    
    select("Category", "Variable", "Level", "Injury_Percent_Combined", "Non_Injury_Percent_Combined", "PValue", "OddsRatio_CI") %>%
    rename("Injury (%)" = Injury_Percent_Combined, "Non-Injury (%)" = Non_Injury_Percent_Combined) %>%
   
    mutate(across(everything(), ~ ifelse(is.na(.) | . %in% c("NA", "NaN", "Inf", "-Inf"), "", .))),
  header = c("Category", "Variable", "Level", "Injury (%)", "Non-Injury (%)", "P-Value", "Odds Ratio (95% CI)"),
  align = 'lcccccc',
  rnames = FALSE,
  css.cell = "text-align: center; padding: 12px 15px; font-size: 12px; white-space: nowrap;",
  css.row = sprintf("background-color: %s;", row_colors),
  header.css = "background-color: #333333; font-size: 14px; text-align: center; color: white;",
  caption = "<h3 style='text-align: center;'>Analysis for Cyclist MidBlock</h3>
             <p style='text-align: center;'><em>This analysis focuses on mode: cyclist | intersection: no</em></p>"
)

# Display the final HTML table
print(HTML(final_table_html))

Analysis for Cyclist MidBlock

This analysis focuses on mode: cyclist | intersection: no

Category Variable Level Injury (%) Non-Injury (%) P-Value Odds Ratio (95% CI)
Road Direction oneway No 167 - (0.9598) 72 - (0.9863) 1 (NA - NA)
Yes 7 - (0.0402) 1 - (0.0137) 0.4424 0.3313 (0.04 - 2.7424)
twoways No 6 - (0.0345) 0 - (0) 1 (NA - NA)
Yes 168 - (0.9655) 73 - (1) 0.1837 Inf (NaN - Inf)
Number of Lanes onelane No 174 - (1) 72 - (0.9863) 1 (NA - NA)
Yes 0 - (0) 1 - (0.0137) 0.2955 Inf (NaN - Inf)
twolanes No 73 - (0.4195) 20 - (0.274) 1 (NA - NA)
Yes 101 - (0.5805) 53 - (0.726) 0.0321 1.9153 (1.0554 - 3.476)
threelanes No 166 - (0.954) 72 - (0.9863) 1 (NA - NA)
Yes 8 - (0.046) 1 - (0.0137) 0.2884 0.2882 (0.0354 - 2.3468)
fourlanes No 120 - (0.6897) 56 - (0.7671) 1 (NA - NA)
Yes 54 - (0.3103) 17 - (0.2329) 0.2808 0.6746 (0.3591 - 1.2675)
fivelanes No 167 - (0.9598) 73 - (1) 1 (NA - NA)
Yes 7 - (0.0402) 0 - (0) 0.1082 0 (0 - NaN)
sixlanes No 165 - (0.9483) 71 - (0.9726) 1 (NA - NA)
Yes 9 - (0.0517) 2 - (0.0274) 0.515 0.5164 (0.1088 - 2.4508)
Cyclist Infrastructure bikelanewithbarrier No 174 - (1) 71 - (0.9726) 1 (NA - NA)
Yes 0 - (0) 2 - (0.0274) 0.0865 Inf (NaN - Inf)
offroadbikepathcycletrack No 172 - (0.9885) 73 - (1) 1 (NA - NA)
Yes 2 - (0.0115) 0 - (0) 1 0 (0 - NaN)
raisedbikelane No 171 - (0.9828) 72 - (0.9863) 1 (NA - NA)
Yes 3 - (0.0172) 1 - (0.0137) 1 0.7917 (0.081 - 7.7388)
twowayprotectedbicyclelane No 171 - (0.9828) 72 - (0.9863) 1 (NA - NA)
Yes 3 - (0.0172) 1 - (0.0137) 1 0.7917 (0.081 - 7.7388)
onewayprotectedbicyclelane No 174 - (1) 72 - (0.9863) 1 (NA - NA)
Yes 0 - (0) 1 - (0.0137) 0.2955 Inf (NaN - Inf)
bufferedbicyclelaneadjacenttocurb No 169 - (0.9713) 72 - (0.9863) 1 (NA - NA)
Yes 5 - (0.0287) 1 - (0.0137) 0.6732 0.4694 (0.0539 - 4.0897)
bufferedbicyclelaneoffsetfromcurb No 174 - (1) 72 - (0.9863) 1 (NA - NA)
Yes 0 - (0) 1 - (0.0137) 0.2955 Inf (NaN - Inf)
paintedbicyclelaneadjacenttocurb No 138 - (0.7931) 57 - (0.7808) 1 (NA - NA)
Yes 36 - (0.2069) 16 - (0.2192) 0.8648 1.076 (0.5535 - 2.092)
paintedbicyclelaneoffsetfromcurb No 165 - (0.9483) 68 - (0.9315) 1 (NA - NA)
Yes 9 - (0.0517) 5 - (0.0685) 0.5621 1.348 (0.4358 - 4.1694)
bikeaccessibleshoulder No 162 - (0.931) 68 - (0.9315) 1 (NA - NA)
Yes 12 - (0.069) 5 - (0.0685) 1 0.9926 (0.3368 - 2.926)
sharedlanewithmarkings No 166 - (0.954) 72 - (0.9863) 1 (NA - NA)
Yes 8 - (0.046) 1 - (0.0137) 0.2884 0.2882 (0.0354 - 2.3468)
Other Variables clearzone No 174 - (1) 71 - (0.9726) 1 (NA - NA)
Yes 0 - (0) 2 - (0.0274) 0.0865 Inf (NaN - Inf)
onstreetparking No 97 - (0.5575) 33 - (0.4521) 1 (NA - NA)
Yes 77 - (0.4425) 40 - (0.5479) 0.1624 1.527 (0.8814 - 2.6452)
parkinglotgarage No 57 - (0.3276) 31 - (0.4247) 1 (NA - NA)
Yes 117 - (0.6724) 42 - (0.5753) 0.1491 0.66 (0.3764 - 1.1576)
concretebarrier No 172 - (0.9885) 73 - (1) 1 (NA - NA)
Yes 2 - (0.0115) 0 - (0) 1 0 (0 - NaN)
laneofparkedcars No 170 - (0.977) 73 - (1) 1 (NA - NA)
Yes 4 - (0.023) 0 - (0) 0.3224 0 (0 - NaN)
NULL
Cyclist Intersection

cyclist_intersection_data <- data %>%
  filter(mode == "Cyclist", intersection == "Yes") # Filter data for Cyclist Intersection

group_vars <- c("oneway", "twoways", "onelane", "twolanes", "threelanes", "fourlanes", 
                "fivelanes", "sixlanes", "signalized", "stopsigns", "roundabout", 
                "dedicatedsignalforcyclists", "bikeboxes", "twostageturnbox", 
                "protectedordedicatedintersection", "medianislanddiverter", 
                "combinedbikelaneturnlane", "throughbikelane")

categories <- list(
  "Road Direction" = c("oneway", "twoways"),
  "Number of Lanes" = c("onelane", "twolanes", "threelanes", "fourlanes", "fivelanes", "sixlanes"),
  "Intersection Infrastructure" = c("signalized", "stopsigns", "roundabout"),
  "Cyclist Infrastructure" = c("dedicatedsignalforcyclists", "bikeboxes", "twostageturnbox", 
                                "protectedordedicatedintersection", "medianislanddiverter", 
                                "combinedbikelaneturnlane", "throughbikelane")
)

odds_ratios_list <- list()
standard_columns <- c("Category", "Variable", "Level", "Injury", "Injury_Percent", 
                      "Non-Injury", "Non-Injury_Percent", "OddsRatio", "Lower", "Upper", "PValue")

for (category in names(categories)) {
  for (var in categories[[category]]) {
    if (var %in% names(cyclist_intersection_data)) {
      odds_ratio_result <- tryCatch({
        if (n_distinct(cyclist_intersection_data[[var]]) == 2) {
          odds_data <- cyclist_intersection_data %>%
            select(all_of(var), crashseverity) %>%
            table() %>%
            as.matrix()
          
          if (all(dim(odds_data) == c(2, 2))) {  
            odds_ratio_table <- as.data.frame(epitab(odds_data, method = "oddsratio")$tab) %>%
              mutate(across(where(is.numeric), round, 4))
            
            odds_ratio_table <- cbind(Category = category, Variable = var, Level = rownames(odds_ratio_table), odds_ratio_table)
            setNames(odds_ratio_table, standard_columns)  
          } else {
            setNames(data.frame(
              Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
              Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
              Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
            ), standard_columns)  
          }
        } else {
          setNames(data.frame(
            Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
            Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
            Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
          ), standard_columns)  
        }
      }, error = function(e) {
        setNames(data.frame(
          Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
          Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
          Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
        ), standard_columns)  
      })
      
      odds_ratios_list[[var]] <- odds_ratio_result
    }
  }
}
Warning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrect
odds_ratios_data <- do.call(rbind, odds_ratios_list)

odds_ratios_data$OddsRatio_CI <- ifelse(is.na(odds_ratios_data$OddsRatio), "-", 
                                        paste0(odds_ratios_data$OddsRatio, " (", odds_ratios_data$Lower, " - ", odds_ratios_data$Upper, ")"))


odds_ratios_data <- odds_ratios_data %>% select(-Lower, -Upper)


row_colors <- rep(c("#f9f9f9", "#ffffff"), length.out = nrow(odds_ratios_data))
odds_ratios_data$Variable <- ifelse(duplicated(odds_ratios_data$Variable), "", odds_ratios_data$Variable)
odds_ratios_data$Category <- ifelse(duplicated(odds_ratios_data$Category), "", odds_ratios_data$Category)

final_table_html <- htmlTable(
  odds_ratios_data %>% 
    
    mutate(Injury_Percent_Combined = paste0(Injury, " - (", Injury_Percent, ")")) %>%
   
    mutate(Non_Injury_Percent_Combined = paste0(`Non-Injury`, " - (", `Non-Injury_Percent`, ")")) %>%
    
    select("Category", "Variable", "Level", "Injury_Percent_Combined", "Non_Injury_Percent_Combined", "PValue", "OddsRatio_CI") %>%
    rename("Injury (%)" = Injury_Percent_Combined, "Non-Injury (%)" = Non_Injury_Percent_Combined) %>%
  
    mutate(across(everything(), ~ ifelse(is.na(.) | . %in% c("NA", "NaN", "Inf", "-Inf"), "", .))),
  header = c("Category", "Variable", "Level", "Injury (%)", "Non-Injury (%)", "P-Value", "Odds Ratio (95% CI)"),
  align = 'lcccccc',
  rnames = FALSE,
  css.cell = "text-align: center; padding: 12px 15px; font-size: 12px; white-space: nowrap;",
  css.row = sprintf("background-color: %s;", row_colors),
  header.css = "background-color: #333333; font-size: 14px; text-align: center; color: white;",
  caption = "<h3 style='text-align: center;'>Analysis for Cyclist Intersection</h3>
             <p style='text-align: center;'><em>This analysis focuses on mode: cyclist | intersection: yes</em></p>"
)


print(HTML(final_table_html))

Analysis for Cyclist Intersection

This analysis focuses on mode: cyclist | intersection: yes

Category Variable Level Injury (%) Non-Injury (%) P-Value Odds Ratio (95% CI)
Road Direction oneway No 293 - (0.8906) 62 - (0.9254) 1 (NA - NA)
Yes 36 - (0.1094) 5 - (0.0746) 0.5114 0.6564 (0.2476 - 1.7396)
twoways No 5 - (0.0152) 1 - (0.0149) 1 (NA - NA)
Yes 324 - (0.9848) 66 - (0.9851) 1 1.0185 (0.1171 - 8.8608)
Number of Lanes onelane No 289 - (0.8784) 62 - (0.9254) 1 (NA - NA)
Yes 40 - (0.1216) 5 - (0.0746) 0.3969 0.5827 (0.221 - 1.536)
twolanes No 53 - (0.1611) 10 - (0.1493) 1 (NA - NA)
Yes 276 - (0.8389) 57 - (0.8507) 1 1.0946 (0.5257 - 2.2791)
threelanes No 305 - (0.9271) 61 - (0.9104) 1 (NA - NA)
Yes 24 - (0.0729) 6 - (0.0896) 0.6153 1.25 (0.4903 - 3.1868)
fourlanes No 190 - (0.5775) 39 - (0.5821) 1 (NA - NA)
Yes 139 - (0.4225) 28 - (0.4179) 1 0.9814 (0.5762 - 1.6714)
fivelanes No 316 - (0.9605) 63 - (0.9403) 1 (NA - NA)
Yes 13 - (0.0395) 4 - (0.0597) 0.5049 1.5433 (0.4873 - 4.8879)
sixlanes No 306 - (0.9301) 64 - (0.9552) 1 (NA - NA)
Yes 23 - (0.0699) 3 - (0.0448) 0.5937 0.6236 (0.1818 - 2.1399)
Intersection Infrastructure signalized No 176 - (0.535) 31 - (0.4627) 1 (NA - NA)
Yes 153 - (0.465) 36 - (0.5373) 0.2868 1.3359 (0.7888 - 2.2624)
stopsigns No 153 - (0.465) 31 - (0.4627) 1 (NA - NA)
Yes 176 - (0.535) 36 - (0.5373) 1 1.0095 (0.5961 - 1.7097)
roundabout No 323 - (0.9818) 67 - (1) 1 (NA - NA)
Yes 6 - (0.0182) 0 - (0) 0.5951 0 (0 - NaN)
Cyclist Infrastructure dedicatedsignalforcyclists No 319 - (0.9696) 67 - (1) 1 (NA - NA)
Yes 10 - (0.0304) 0 - (0) 0.2236 0 (0 - NaN)
bikeboxes No 323 - (0.9818) 64 - (0.9552) 1 (NA - NA)
Yes 6 - (0.0182) 3 - (0.0448) 0.1822 2.5234 (0.6151 - 10.3526)
twostageturnbox No 323 - (0.9818) 66 - (0.9851) 1 (NA - NA)
Yes 6 - (0.0182) 1 - (0.0149) 1 0.8157 (0.0966 - 6.8877)
protectedordedicatedintersection No 315 - (0.9574) 63 - (0.9403) 1 (NA - NA)
Yes 14 - (0.0426) 4 - (0.0597) 0.5221 1.4286 (0.4552 - 4.4831)
medianislanddiverter No 320 - (0.9726) 65 - (0.9701) 1 (NA - NA)
Yes 9 - (0.0274) 2 - (0.0299) 1 1.094 (0.231 - 5.1813)
combinedbikelaneturnlane No 322 - (0.9787) 65 - (0.9701) 1 (NA - NA)
Yes 7 - (0.0213) 2 - (0.0299) 0.6523 1.4154 (0.2875 - 6.9679)
throughbikelane No 303 - (0.921) 61 - (0.9104) 1 (NA - NA)
Yes 26 - (0.079) 6 - (0.0896) 0.8056 1.1463 (0.4526 - 2.9033)
NULL
Pedestrian MidBlock

pedestrian_midblock_data <- data %>%
  filter(mode == "Pedestrian", intersection == "No") # Filter data for Pedestrian MidBlock


group_vars <- c("oneway", "twoways", "onelane", "twolanes", "threelanes", "fourlanes", 
                "fivelanes", "sixlanes", "clearzone", "onstreetparking", "standardpaint", 
                "solidpaint", "striped", "offroadmultiusepathway", "speedhump", 
                "accessibleshoulder", "sidewalks")


categories <- list(
  "Road Direction" = c("oneway", "twoways"),
  "Number of Lanes" = c("onelane", "twolanes", "threelanes", "fourlanes", "fivelanes", "sixlanes"),
  "Pedestrian Infrastructure" = c("accessibleshoulder", "sidewalks",   "standardpaint", "solidpaint", "striped"),
  "Other Infrastructure" = c( "clearzone", "onstreetparking", "offroadmultiusepathway", "speedhump")
)

odds_ratios_list <- list()
standard_columns <- c("Category", "Variable", "Level", "Injury", "Injury_Percent", 
                      "Non-Injury", "Non-Injury_Percent", "OddsRatio", "Lower", "Upper", "PValue")

for (category in names(categories)) {
  for (var in categories[[category]]) {
    if (var %in% names(pedestrian_midblock_data)) {
      odds_ratio_result <- tryCatch({
        if (n_distinct(pedestrian_midblock_data[[var]]) == 2) {
          odds_data <- pedestrian_midblock_data %>%
            select(all_of(var), crashseverity) %>%
            table() %>%
            as.matrix()
          
          if (all(dim(odds_data) == c(2, 2))) {  
            odds_ratio_table <- as.data.frame(epitab(odds_data, method = "oddsratio")$tab) %>%
              mutate(across(where(is.numeric), round, 4))
            
            odds_ratio_table <- cbind(Category = category, Variable = var, Level = rownames(odds_ratio_table), odds_ratio_table)
            setNames(odds_ratio_table, standard_columns)  
          } else {
            setNames(data.frame(
              Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
              Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
              Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
            ), standard_columns)  
          }
        } else {
          setNames(data.frame(
            Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
            Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
            Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
          ), standard_columns)  
        }
      }, error = function(e) {
        setNames(data.frame(
          Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
          Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
          Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
        ), standard_columns)  
      })
      
      odds_ratios_list[[var]] <- odds_ratio_result
    }
  }
}
Warning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrect
odds_ratios_data <- do.call(rbind, odds_ratios_list)


odds_ratios_data$OddsRatio_CI <- ifelse(is.na(odds_ratios_data$OddsRatio), "-", 
                                        paste0(odds_ratios_data$OddsRatio, " (", odds_ratios_data$Lower, " - ", odds_ratios_data$Upper, ")"))


odds_ratios_data <- odds_ratios_data %>% select(-Lower, -Upper)


row_colors <- rep(c("#f9f9f9", "#ffffff"), length.out = nrow(odds_ratios_data))
odds_ratios_data$Variable <- ifelse(duplicated(odds_ratios_data$Variable), "", odds_ratios_data$Variable)
odds_ratios_data$Category <- ifelse(duplicated(odds_ratios_data$Category), "", odds_ratios_data$Category)

 
final_table_html <- htmlTable(
  odds_ratios_data %>% 
   
    mutate(Injury_Percent_Combined = paste0(Injury, " - (", Injury_Percent, ")")) %>%
    
    mutate(Non_Injury_Percent_Combined = paste0(`Non-Injury`, " - (", `Non-Injury_Percent`, ")")) %>%
    
    select("Category", "Variable", "Level", "Injury_Percent_Combined", "Non_Injury_Percent_Combined", "PValue", "OddsRatio_CI") %>%
    rename("Injury (%)" = Injury_Percent_Combined, "Non-Injury (%)" = Non_Injury_Percent_Combined) %>%
   
    mutate(across(everything(), ~ ifelse(is.na(.) | . %in% c("NA", "NaN", "Inf", "-Inf"), "", .))),
  header = c("Category", "Variable", "Level", "Injury (%)", "Non-Injury (%)", "P-Value", "Odds Ratio (95% CI)"),
  align = 'lcccccc',
  rnames = FALSE,
  css.cell = "text-align: center; padding: 12px 15px; font-size: 12px; white-space: nowrap;",
  css.row = sprintf("background-color: %s;", row_colors),
  header.css = "background-color: #333333; font-size: 14px; text-align: center; color: white;",
  caption = "<h3 style='text-align: center;'>Analysis for Pedestrian MidBlock</h3>
             <p style='text-align: center;'><em>This analysis focuses on mode: pedestrian | intersection: no</em></p>"
)


print(HTML(final_table_html))

Analysis for Pedestrian MidBlock

This analysis focuses on mode: pedestrian | intersection: no

Category Variable Level Injury (%) Non-Injury (%) P-Value Odds Ratio (95% CI)
Road Direction oneway No 303 - (0.987) 69 - (1) 1 (NA - NA)
Yes 4 - (0.013) 0 - (0) 1 0 (0 - NaN)
twoways No 3 - (0.0098) 0 - (0) 1 (NA - NA)
Yes 304 - (0.9902) 69 - (1) 1 Inf (NaN - Inf)
Number of Lanes onelane No 302 - (0.9837) 67 - (0.971) 1 (NA - NA)
Yes 5 - (0.0163) 2 - (0.029) 0.6167 1.803 (0.3425 - 9.4924)
twolanes No 141 - (0.4593) 31 - (0.4493) 1 (NA - NA)
Yes 166 - (0.5407) 38 - (0.5507) 0.8945 1.0412 (0.6161 - 1.7597)
threelanes No 294 - (0.9577) 65 - (0.942) 1 (NA - NA)
Yes 13 - (0.0423) 4 - (0.058) 0.5286 1.3917 (0.4396 - 4.4059)
fourlanes No 198 - (0.645) 48 - (0.6957) 1 (NA - NA)
Yes 109 - (0.355) 21 - (0.3043) 0.4847 0.7947 (0.4523 - 1.3963)
fivelanes No 297 - (0.9674) 67 - (0.971) 1 (NA - NA)
Yes 10 - (0.0326) 2 - (0.029) 1 0.8866 (0.1898 - 4.1402)
sixlanes No 290 - (0.9446) 65 - (0.942) 1 (NA - NA)
Yes 17 - (0.0554) 4 - (0.058) 1 1.0498 (0.3419 - 3.2235)
Pedestrian Infrastructure accessibleshoulder No 282 - (0.9186) 61 - (0.8841) 1 (NA - NA)
Yes 25 - (0.0814) 8 - (0.1159) 0.3509 1.4793 (0.6368 - 3.4366)
sidewalks No 54 - (0.1759) 13 - (0.1884) 1 (NA - NA)
Yes 253 - (0.8241) 56 - (0.8116) 0.8618 0.9194 (0.4699 - 1.7988)
standardpaint No 276 - (0.899) 62 - (0.8986) 1 (NA - NA)
Yes 31 - (0.101) 7 - (0.1014) 1 1.0052 (0.4232 - 2.3878)
solidpaint No 303 - (0.987) 69 - (1) 1 (NA - NA)
Yes 4 - (0.013) 0 - (0) 1 0 (0 - NaN)
striped No 286 - (0.9316) 63 - (0.913) 1 (NA - NA)
Yes 21 - (0.0684) 6 - (0.087) 0.6062 1.2971 (0.5029 - 3.3451)
Other Infrastructure clearzone No 305 - (0.9935) 69 - (1) 1 (NA - NA)
Yes 2 - (0.0065) 0 - (0) 1 0 (0 - NaN)
onstreetparking No 175 - (0.57) 34 - (0.4928) 1 (NA - NA)
Yes 132 - (0.43) 35 - (0.5072) 0.2837 1.3648 (0.8087 - 2.303)
offroadmultiusepathway Not applicable - (NA) NA - (NA) -
speedhump No 304 - (0.9902) 67 - (0.971) 1 (NA - NA)
Yes 3 - (0.0098) 2 - (0.029) 0.2286 3.0249 (0.4957 - 18.4583)
NULL
Pedestrian Intersection

pedestrian_intersection_data <- data %>%
  filter(mode == "Pedestrian", intersection == "Yes") # Filter data for Pedestrian Intersection


group_vars <- c("oneway", "twoways", "onelane", "twolanes", "threelanes", "fourlanes", 
                "fivelanes", "sixlanes", "signalized", "stopsigns", "roundabout", 
                "numberoflegsintheintersection", "threelegs", "fourlegs", "fourmlegs", 
                "unsignalizedcrosswalk", "signalizedcrosswalkstreet", "standardpaint", 
                "solidpaint", "striped", "pedestrianrefugestriped", 
                "curbextensionpinchpoint", "rapidrectangularflashingbeacon", 
                "pedestriancrossedsignal")


categories <- list(
  "Road Direction" = c("oneway", "twoways"),
  "Number of Legs" = c("threelegs", "fourlegs", "fourmlegs"),
  "Number of Lanes" = c("onelane", "twolanes", "threelanes", "fourlanes", "fivelanes", "sixlanes"),
  "Pedestrian Infrastructure" = c("pedestrianrefugestriped", 
                                   "curbextensionpinchpoint", 
                                   "rapidrectangularflashingbeacon", 
                                   "pedestriancrossedsignal"),
  
  "Other Variables" = c("signalized", "stopsigns", "roundabout", 
                "numberoflegsintheintersection",   
                "unsignalizedcrosswalk", "signalizedcrosswalkstreet", "standardpaint", 
                "solidpaint", "striped")
)

odds_ratios_list <- list()
standard_columns <- c("Category", "Variable", "Level", "Injury", "Injury_Percent", 
                      "Non-Injury", "Non-Injury_Percent", "OddsRatio", "Lower", "Upper", "PValue")

for (category in names(categories)) {
  for (var in categories[[category]]) {
    if (var %in% names(pedestrian_intersection_data)) {
      odds_ratio_result <- tryCatch({
        if (n_distinct(pedestrian_intersection_data[[var]]) == 2) {
          odds_data <- pedestrian_intersection_data %>%
            select(all_of(var), crashseverity) %>%
            table() %>%
            as.matrix()
          
          if (all(dim(odds_data) == c(2, 2))) { 
            odds_ratio_table <- as.data.frame(epitab(odds_data, method = "oddsratio")$tab) %>%
              mutate(across(where(is.numeric), round, 4))
            
            odds_ratio_table <- cbind(Category = category, Variable = var, Level = rownames(odds_ratio_table), odds_ratio_table)
            setNames(odds_ratio_table, standard_columns)  
          } else {
            setNames(data.frame(
              Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
              Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
              Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
            ), standard_columns)  
          }
        } else {
          setNames(data.frame(
            Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
            Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
            Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
          ), standard_columns)  
        }
      }, error = function(e) {
        setNames(data.frame(
          Category = category, Variable = var, Level = NA, Injury = "Not applicable", 
          Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
          Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
        ), standard_columns)  
      })
      
      odds_ratios_list[[var]] <- odds_ratio_result
    } else {
      
      odds_ratios_list[[var]] <- setNames(data.frame(
        Category = category, Variable = var, Level = NA, Injury = "Variable not available", 
        Injury_Percent = NA, NonInjury = NA, Non_Injury_Percent = NA, OddsRatio = NA, 
        Lower = NA, Upper = NA, PValue = NA, stringsAsFactors = FALSE
      ), standard_columns)
    }
  }
}
Warning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrectWarning: Chi-squared approximation may be incorrect
odds_ratios_data <- do.call(rbind, odds_ratios_list)


odds_ratios_data$OddsRatio_CI <- ifelse(is.na(odds_ratios_data$OddsRatio), "-", 
                                        paste0(odds_ratios_data$OddsRatio, " (", odds_ratios_data$Lower, " - ", odds_ratios_data$Upper, ")"))


odds_ratios_data <- odds_ratios_data %>% select(-Lower, -Upper)


row_colors <- rep(c("#f9f9f9", "#ffffff"), length.out = nrow(odds_ratios_data))
odds_ratios_data$Variable <- ifelse(duplicated(odds_ratios_data$Variable), "", odds_ratios_data$Variable)
odds_ratios_data$Category <- ifelse(duplicated(odds_ratios_data$Category), "", odds_ratios_data$Category)


final_table_html <- htmlTable(
  odds_ratios_data %>% 
    
    mutate(Injury_Percent_Combined = paste0(Injury, " - (", Injury_Percent, ")")) %>%
   
    mutate(Non_Injury_Percent_Combined = paste0(`Non-Injury`, " - (", `Non-Injury_Percent`, ")")) %>%
   
    select("Category", "Variable", "Level", "Injury_Percent_Combined", "Non_Injury_Percent_Combined", "PValue", "OddsRatio_CI") %>%
    rename("Injury (%)" = Injury_Percent_Combined, "Non-Injury (%)" = Non_Injury_Percent_Combined) %>%
   
    mutate(across(everything(), ~ ifelse(is.na(.) | . %in% c("NA", "NaN", "Inf", "-Inf"), "", .))),
  header = c("Category", "Variable", "Level", "Injury (%)", "Non-Injury (%)", "P-Value", "Odds Ratio (95% CI)"),
  align = 'lcccccc',
  rnames = FALSE,
  css.cell = "text-align: center; padding: 12px 15px; font-size: 12px; white-space: nowrap;",
  css.row = sprintf("background-color: %s;", row_colors),
  header.css = "background-color: #333333; font-size: 14px; text-align: center; color: white;",
  caption = "<h3 style='text-align: center;'>Analysis for Pedestrian Intersection</h3>
             <p style='text-align: center;'><em>This analysis focuses on mode: pedestrian | intersection: yes</em></p>"
)


print(HTML(final_table_html))

Analysis for Pedestrian Intersection

This analysis focuses on mode: pedestrian | intersection: yes

Category Variable Level Injury (%) Non-Injury (%) P-Value Odds Ratio (95% CI)
Road Direction oneway No 398 - (0.9365) 67 - (0.9306) 1 (NA - NA)
Yes 27 - (0.0635) 5 - (0.0694) 0.7968 1.1001 (0.4093 - 2.9567)
twoways No 1 - (0.0024) 1 - (0.0139) 1 (NA - NA)
Yes 424 - (0.9976) 71 - (0.9861) 0.269 0.1675 (0.0104 - 2.7078)
Number of Legs threelegs No 338 - (0.7953) 53 - (0.7361) 1 (NA - NA)
Yes 87 - (0.2047) 19 - (0.2639) 0.2766 1.3928 (0.784 - 2.4742)
fourlegs No 94 - (0.2212) 19 - (0.2639) 1 (NA - NA)
Yes 331 - (0.7788) 53 - (0.7361) 0.4478 0.7922 (0.4471 - 1.4035)
fourmlegs No 418 - (0.9835) 72 - (1) 1 (NA - NA)
Yes 7 - (0.0165) 0 - (0) 0.6007 0 (0 - NaN)
Number of Lanes onelane No 401 - (0.9435) 69 - (0.9583) 1 (NA - NA)
Yes 24 - (0.0565) 3 - (0.0417) 0.7826 0.7264 (0.213 - 2.4782)
twolanes No 94 - (0.2212) 18 - (0.25) 1 (NA - NA)
Yes 331 - (0.7788) 54 - (0.75) 0.6472 0.852 (0.4768 - 1.5225)
threelanes No 368 - (0.8659) 62 - (0.8611) 1 (NA - NA)
Yes 57 - (0.1341) 10 - (0.1389) 0.8538 1.0413 (0.5049 - 2.1475)
fourlanes No 177 - (0.4165) 32 - (0.4444) 1 (NA - NA)
Yes 248 - (0.5835) 40 - (0.5556) 0.6992 0.8921 (0.5393 - 1.4757)
fivelanes No 394 - (0.9271) 67 - (0.9306) 1 (NA - NA)
Yes 31 - (0.0729) 5 - (0.0694) 1 0.9485 (0.3562 - 2.5258)
sixlanes No 388 - (0.9129) 65 - (0.9028) 1 (NA - NA)
Yes 37 - (0.0871) 7 - (0.0972) 0.822 1.1293 (0.4829 - 2.6408)
Pedestrian Infrastructure pedestrianrefugestriped No 419 - (0.9859) 71 - (0.9861) 1 (NA - NA)
Yes 6 - (0.0141) 1 - (0.0139) 1 0.9836 (0.1167 - 8.2923)
curbextensionpinchpoint Variable not available - (NA) NA - (NA) -
rapidrectangularflashingbeacon No 422 - (0.9929) 72 - (1) 1 (NA - NA)
Yes 3 - (0.0071) 0 - (0) 1 0 (0 - NaN)
pedestriancrossedsignal No 400 - (0.9412) 72 - (1) 1 (NA - NA)
Yes 25 - (0.0588) 0 - (0) 0.0361 0 (0 - NaN)
Other Variables signalized No 137 - (0.3224) 25 - (0.3472) 1 (NA - NA)
Yes 288 - (0.6776) 47 - (0.6528) 0.6849 0.8943 (0.5285 - 1.5134)
stopsigns No 283 - (0.6659) 43 - (0.5972) 1 (NA - NA)
Yes 142 - (0.3341) 29 - (0.4028) 0.2837 1.3441 (0.8053 - 2.2434)
roundabout No 421 - (0.9906) 72 - (1) 1 (NA - NA)
Yes 4 - (0.0094) 0 - (0) 1 0 (0 - NaN)
numberoflegsintheintersection Not applicable - (NA) NA - (NA) -
unsignalizedcrosswalk No 334 - (0.7859) 55 - (0.7639) 1 (NA - NA)
Yes 91 - (0.2141) 17 - (0.2361) 0.6462 1.1345 (0.6281 - 2.049)
signalizedcrosswalkstreet No 144 - (0.3388) 28 - (0.3889) 1 (NA - NA)
Yes 281 - (0.6612) 44 - (0.6111) 0.4234 0.8053 (0.4813 - 1.3473)
standardpaint No 109 - (0.2565) 22 - (0.3056) 1 (NA - NA)
Yes 316 - (0.7435) 50 - (0.6944) 0.3877 0.7839 (0.4538 - 1.3543)
solidpaint No 412 - (0.9694) 72 - (1) 1 (NA - NA)
Yes 13 - (0.0306) 0 - (0) 0.2313 0 (0 - NaN)
striped No 343 - (0.8071) 60 - (0.8333) 1 (NA - NA)
Yes 82 - (0.1929) 12 - (0.1667) 0.7448 0.8366 (0.4302 - 1.6267)
NULL
LS0tCnRpdGxlOiAiQWNjaWRlbnQgQW5hbHlzaXM6IEN5Y2xpc3RzIGFuZCBQZWRlc3RyaWFucyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCiAgICB0aGVtZTogY29zbW8KICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogJzInCi0tLQoKYGBge3IgaW5jbHVkZT1GQUxTRX0KIyBMb2FkIGxpYnJhcmllcwpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkoZXBpdG9vbHMpCmxpYnJhcnkoaHRtbFRhYmxlKQpsaWJyYXJ5KGh0bWx0b29scykKCmRhdGEgPC0gcmVhZC5jc3YoIi9Vc2Vycy9uYXRhbGlvY2hvYS9Sb2FkU2FmZXR5L1Jlc3VsdHNJQ0JDLmNzdiIpCmBgYAoKIyMjIyMjIEN5Y2xpc3QgTWlkQmxvY2sKCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9Cgpncm91cF9kYXRhIDwtIGRhdGEgJT4lCiAgZmlsdGVyKG1vZGUgPT0gIkN5Y2xpc3QiLCBpbnRlcnNlY3Rpb24gPT0gIk5vIikgIyBGaWx0ZXIgZGF0YSBmb3IgQ3ljbGlzdCBNaWRCbG9jawoKIyBEZWZpbmUgdGhlIGdyb3VwX3ZhcnMgCmdyb3VwX3ZhcnMgPC0gYygib25ld2F5IiwgInR3b3dheXMiLCAiY2xlYXJ6b25lIiwgIm9uc3RyZWV0cGFya2luZyIsIAogICAgICAgICAgICAgICAgInBhcmtpbmdsb3RnYXJhZ2UiLCAiY29uY3JldGViYXJyaWVyIiwgImxhbmVvZnBhcmtlZGNhcnMiLCAKICAgICAgICAgICAgICAgICJiaWtlbGFuZXdpdGhiYXJyaWVyIiwgIm9mZnJvYWRiaWtlcGF0aGN5Y2xldHJhY2siLCAicmFpc2VkYmlrZWxhbmUiLCAKICAgICAgICAgICAgICAgICJ0d293YXlwcm90ZWN0ZWRiaWN5Y2xlbGFuZSIsICJvbmV3YXlwcm90ZWN0ZWRiaWN5Y2xlbGFuZSIsIAogICAgICAgICAgICAgICAgImJ1ZmZlcmVkYmljeWNsZWxhbmVhZGphY2VudHRvY3VyYiIsICJidWZmZXJlZGJpY3ljbGVsYW5lb2Zmc2V0ZnJvbWN1cmIiLCAKICAgICAgICAgICAgICAgICJwYWludGVkYmljeWNsZWxhbmVhZGphY2VudHRvY3VyYiIsICJwYWludGVkYmljeWNsZWxhbmVvZmZzZXRmcm9tY3VyYiIsIAogICAgICAgICAgICAgICAgImJpa2VhY2Nlc3NpYmxlc2hvdWxkZXIiLCAic2hhcmVkbGFuZXdpdGhtYXJraW5ncyIsICJvbmVsYW5lIiwgInR3b2xhbmVzIiwgCiAgICAgICAgICAgICAgICAidGhyZWVsYW5lcyIsICJmb3VybGFuZXMiLCAiZml2ZWxhbmVzIiwgInNpeGxhbmVzIikKCiMgR3JvdXBpbmcgdGhlIHZhcmlhYmxlcyBieSBjYXRlZ29yeQpjYXRlZ29yaWVzIDwtIGxpc3QoCiAgIlJvYWQgRGlyZWN0aW9uIiA9IGMoIm9uZXdheSIsICJ0d293YXlzIiksCiAgIk51bWJlciBvZiBMYW5lcyIgPSBjKCJvbmVsYW5lIiwgInR3b2xhbmVzIiwgInRocmVlbGFuZXMiLCAiZm91cmxhbmVzIiwgImZpdmVsYW5lcyIsICJzaXhsYW5lcyIpLAogICJDeWNsaXN0IEluZnJhc3RydWN0dXJlIiA9IGMoImJpa2VsYW5ld2l0aGJhcnJpZXIiLCAib2Zmcm9hZGJpa2VwYXRoY3ljbGV0cmFjayIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInJhaXNlZGJpa2VsYW5lIiwgInR3b3dheXByb3RlY3RlZGJpY3ljbGVsYW5lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAib25ld2F5cHJvdGVjdGVkYmljeWNsZWxhbmUiLCAiYnVmZmVyZWRiaWN5Y2xlbGFuZWFkamFjZW50dG9jdXJiIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiYnVmZmVyZWRiaWN5Y2xlbGFuZW9mZnNldGZyb21jdXJiIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGFpbnRlZGJpY3ljbGVsYW5lYWRqYWNlbnR0b2N1cmIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwYWludGVkYmljeWNsZWxhbmVvZmZzZXRmcm9tY3VyYiIsICJiaWtlYWNjZXNzaWJsZXNob3VsZGVyIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2hhcmVkbGFuZXdpdGhtYXJraW5ncyIpLAogICJPdGhlciBWYXJpYWJsZXMiID0gc2V0ZGlmZihncm91cF92YXJzLCBjKCJvbmV3YXkiLCAidHdvd2F5cyIsICJvbmVsYW5lIiwgInR3b2xhbmVzIiwgInRocmVlbGFuZXMiLCAiZm91cmxhbmVzIiwgImZpdmVsYW5lcyIsICJzaXhsYW5lcyIsICJiaWtlbGFuZXdpdGhiYXJyaWVyIiwgIm9mZnJvYWRiaWtlcGF0aGN5Y2xldHJhY2siLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyYWlzZWRiaWtlbGFuZSIsICJ0d293YXlwcm90ZWN0ZWRiaWN5Y2xlbGFuZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm9uZXdheXByb3RlY3RlZGJpY3ljbGVsYW5lIiwgImJ1ZmZlcmVkYmljeWNsZWxhbmVhZGphY2VudHRvY3VyYiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImJ1ZmZlcmVkYmljeWNsZWxhbmVvZmZzZXRmcm9tY3VyYiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBhaW50ZWRiaWN5Y2xlbGFuZWFkamFjZW50dG9jdXJiIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGFpbnRlZGJpY3ljbGVsYW5lb2Zmc2V0ZnJvbWN1cmIiLCAiYmlrZWFjY2Vzc2libGVzaG91bGRlciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNoYXJlZGxhbmV3aXRobWFya2luZ3MiKSkKKQoKb2Rkc19yYXRpb3NfbGlzdCA8LSBsaXN0KCkKc3RhbmRhcmRfY29sdW1ucyA8LSBjKCJDYXRlZ29yeSIsICJWYXJpYWJsZSIsICJMZXZlbCIsICJJbmp1cnkiLCAiSW5qdXJ5X1BlcmNlbnQiLCAiTm9uLUluanVyeSIsICJOb24tSW5qdXJ5X1BlcmNlbnQiLCAiT2Rkc1JhdGlvIiwgIkxvd2VyIiwgIlVwcGVyIiwgIlBWYWx1ZSIpCgpmb3IgKGNhdGVnb3J5IGluIG5hbWVzKGNhdGVnb3JpZXMpKSB7CiAgZm9yICh2YXIgaW4gY2F0ZWdvcmllc1tbY2F0ZWdvcnldXSkgewogICAgaWYgKHZhciAlaW4lIG5hbWVzKGdyb3VwX2RhdGEpKSB7CiAgICAgIG9kZHNfcmF0aW9fcmVzdWx0IDwtIHRyeUNhdGNoKHsKICAgICAgICBpZiAobl9kaXN0aW5jdChncm91cF9kYXRhW1t2YXJdXSkgPT0gMikgewogICAgICAgICAgb2Rkc19kYXRhIDwtIGdyb3VwX2RhdGEgJT4lCiAgICAgICAgICAgIHNlbGVjdChhbGxfb2YodmFyKSwgY3Jhc2hzZXZlcml0eSkgJT4lCiAgICAgICAgICAgIHRhYmxlKCkgJT4lCiAgICAgICAgICAgIGFzLm1hdHJpeCgpCiAgICAgICAgICAKICAgICAgICAgIGlmIChhbGwoZGltKG9kZHNfZGF0YSkgPT0gYygyLCAyKSkpIHsgICMgRW5zdXJlIHRoZSB0YWJsZSBoYXMgdGhlIGNvcnJlY3QgZGltZW5zaW9ucwogICAgICAgICAgICAKICAgICAgICAgICAgCiAgICAgICAgICAgIAogICAgICAgICAgICAjIFVzZSBlcGl0YWIgdG8gY2FsY3VsYXRlIG9kZHMgcmF0aW8KICAgICAgICAgICAgb2Rkc19yYXRpb190YWJsZSA8LSBhcy5kYXRhLmZyYW1lKGVwaXRhYihvZGRzX2RhdGEsIG1ldGhvZCA9ICJvZGRzcmF0aW8iKSR0YWIpICU+JQogICAgICAgICAgICAgIG11dGF0ZShhY3Jvc3Mod2hlcmUoaXMubnVtZXJpYyksIHJvdW5kLCA0KSkKICAgICAgICAgICAgCiAgICAgICAgICAgIG9kZHNfcmF0aW9fdGFibGUgPC0gY2JpbmQoQ2F0ZWdvcnkgPSBjYXRlZ29yeSwgVmFyaWFibGUgPSB2YXIsIExldmVsID0gcm93bmFtZXMob2Rkc19yYXRpb190YWJsZSksIG9kZHNfcmF0aW9fdGFibGUpCiAgICAgICAgICAgIG5hbWVzKG9kZHNfcmF0aW9fdGFibGUpIDwtIHN0YW5kYXJkX2NvbHVtbnMKICAgICAgICAgICAgb2Rkc19yYXRpb190YWJsZQogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgZGF0YS5mcmFtZSgKICAgICAgICAgICAgICBDYXRlZ29yeSA9IGNhdGVnb3J5LCBWYXJpYWJsZSA9IHZhciwgTGV2ZWwgPSBOQSwgSW5qdXJ5ID0gIk5vdCBhcHBsaWNhYmxlIiwgCiAgICAgICAgICAgICAgSW5qdXJ5X1BlcmNlbnQgPSBOQSwgTm9uSW5qdXJ5ID0gTkEsIE5vbl9Jbmp1cnlfUGVyY2VudCA9IE5BLCBPZGRzUmF0aW8gPSBOQSwgCiAgICAgICAgICAgICAgTG93ZXIgPSBOQSwgVXBwZXIgPSBOQSwgUFZhbHVlID0gTkEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICAgICApCiAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIGRhdGEuZnJhbWUoCiAgICAgICAgICAgIENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IE5BLCBJbmp1cnkgPSAiTm90IGFwcGxpY2FibGUiLCAKICAgICAgICAgICAgSW5qdXJ5X1BlcmNlbnQgPSBOQSwgTm9uSW5qdXJ5ID0gTkEsIE5vbl9Jbmp1cnlfUGVyY2VudCA9IE5BLCBPZGRzUmF0aW8gPSBOQSwgCiAgICAgICAgICAgIExvd2VyID0gTkEsIFVwcGVyID0gTkEsIFBWYWx1ZSA9IE5BLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgICAgICkKICAgICAgICB9CiAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogICAgICAgIGRhdGEuZnJhbWUoCiAgICAgICAgICBDYXRlZ29yeSA9IGNhdGVnb3J5LCBWYXJpYWJsZSA9IHZhciwgTGV2ZWwgPSBOQSwgSW5qdXJ5ID0gIk5vdCBhcHBsaWNhYmxlIiwgCiAgICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICAgIExvd2VyID0gTkEsIFVwcGVyID0gTkEsIFBWYWx1ZSA9IE5BLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgICApCiAgICAgIH0pCiAgICAgIAogICAgICBvZGRzX3JhdGlvc19saXN0W1t2YXJdXSA8LSBvZGRzX3JhdGlvX3Jlc3VsdAogICAgfQogIH0KfQoKCm9kZHNfcmF0aW9zX2RhdGEgPC0gZG8uY2FsbChyYmluZCwgb2Rkc19yYXRpb3NfbGlzdCkgIyBDb21iaW5lIHJlc3VsdHMgaW50byBhIHNpbmdsZSBkYXRhIGZyYW1lCgoKb2Rkc19yYXRpb3NfZGF0YSRPZGRzUmF0aW9fQ0kgPC0gaWZlbHNlKGlzLm5hKG9kZHNfcmF0aW9zX2RhdGEkT2Rkc1JhdGlvKSwgIi0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMChvZGRzX3JhdGlvc19kYXRhJE9kZHNSYXRpbywgIiAoIiwgb2Rkc19yYXRpb3NfZGF0YSRMb3dlciwgIiAtICIsIG9kZHNfcmF0aW9zX2RhdGEkVXBwZXIsICIpIikpCgpvZGRzX3JhdGlvc19kYXRhIDwtIG9kZHNfcmF0aW9zX2RhdGEgJT4lIHNlbGVjdCgtTG93ZXIsIC1VcHBlcikgIyBEcm9wIExvd2VyIGFuZCBVcHBlciBjb2x1bW5zCgpyb3dfY29sb3JzIDwtIHJlcChjKCIjZjlmOWY5IiwgIiNmZmZmZmYiKSwgbGVuZ3RoLm91dCA9IG5yb3cob2Rkc19yYXRpb3NfZGF0YSkpCm9kZHNfcmF0aW9zX2RhdGEkVmFyaWFibGUgPC0gaWZlbHNlKGR1cGxpY2F0ZWQob2Rkc19yYXRpb3NfZGF0YSRWYXJpYWJsZSksICIiLCBvZGRzX3JhdGlvc19kYXRhJFZhcmlhYmxlKQpvZGRzX3JhdGlvc19kYXRhJENhdGVnb3J5IDwtIGlmZWxzZShkdXBsaWNhdGVkKG9kZHNfcmF0aW9zX2RhdGEkQ2F0ZWdvcnkpLCAiIiwgb2Rkc19yYXRpb3NfZGF0YSRDYXRlZ29yeSkgCgoKZmluYWxfdGFibGVfaHRtbCA8LSBodG1sVGFibGUoCiAgb2Rkc19yYXRpb3NfZGF0YSAlPiUgCiAgICBtdXRhdGUoSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQgPSBwYXN0ZTAoSW5qdXJ5LCAiIC0gKCIsIEluanVyeV9QZXJjZW50LCAiKSIpKSAlPiUgIyBDb21iaW5lIGludG8gYSBzaW5nbGUgY29sdW1uIAogICAgbXV0YXRlKE5vbl9Jbmp1cnlfUGVyY2VudF9Db21iaW5lZCA9IHBhc3RlMChgTm9uLUluanVyeWAsICIgLSAoIiwgYE5vbi1Jbmp1cnlfUGVyY2VudGAsICIpIikpICU+JQogICAgCiAgICBzZWxlY3QoIkNhdGVnb3J5IiwgIlZhcmlhYmxlIiwgIkxldmVsIiwgIkluanVyeV9QZXJjZW50X0NvbWJpbmVkIiwgIk5vbl9Jbmp1cnlfUGVyY2VudF9Db21iaW5lZCIsICJQVmFsdWUiLCAiT2Rkc1JhdGlvX0NJIikgJT4lCiAgICByZW5hbWUoIkluanVyeSAoJSkiID0gSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQsICJOb24tSW5qdXJ5ICglKSIgPSBOb25fSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQpICU+JQogICAKICAgIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+IGlmZWxzZShpcy5uYSguKSB8IC4gJWluJSBjKCJOQSIsICJOYU4iLCAiSW5mIiwgIi1JbmYiKSwgIiIsIC4pKSksCiAgaGVhZGVyID0gYygiQ2F0ZWdvcnkiLCAiVmFyaWFibGUiLCAiTGV2ZWwiLCAiSW5qdXJ5ICglKSIsICJOb24tSW5qdXJ5ICglKSIsICJQLVZhbHVlIiwgIk9kZHMgUmF0aW8gKDk1JSBDSSkiKSwKICBhbGlnbiA9ICdsY2NjY2NjJywKICBybmFtZXMgPSBGQUxTRSwKICBjc3MuY2VsbCA9ICJ0ZXh0LWFsaWduOiBjZW50ZXI7IHBhZGRpbmc6IDEycHggMTVweDsgZm9udC1zaXplOiAxMnB4OyB3aGl0ZS1zcGFjZTogbm93cmFwOyIsCiAgY3NzLnJvdyA9IHNwcmludGYoImJhY2tncm91bmQtY29sb3I6ICVzOyIsIHJvd19jb2xvcnMpLAogIGhlYWRlci5jc3MgPSAiYmFja2dyb3VuZC1jb2xvcjogIzMzMzMzMzsgZm9udC1zaXplOiAxNHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNvbG9yOiB3aGl0ZTsiLAogIGNhcHRpb24gPSAiPGgzIHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz5BbmFseXNpcyBmb3IgQ3ljbGlzdCBNaWRCbG9jazwvaDM+CiAgICAgICAgICAgICA8cCBzdHlsZT0ndGV4dC1hbGlnbjogY2VudGVyOyc+PGVtPlRoaXMgYW5hbHlzaXMgZm9jdXNlcyBvbiBtb2RlOiBjeWNsaXN0IHwgaW50ZXJzZWN0aW9uOiBubzwvZW0+PC9wPiIKKQoKIyBEaXNwbGF5IHRoZSBmaW5hbCBIVE1MIHRhYmxlCnByaW50KEhUTUwoZmluYWxfdGFibGVfaHRtbCkpCgpgYGAKCiMjIyMjIyBDeWNsaXN0IEludGVyc2VjdGlvbgoKYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRSwgcGFnZWQucHJpbnQ9VFJVRX0KCmN5Y2xpc3RfaW50ZXJzZWN0aW9uX2RhdGEgPC0gZGF0YSAlPiUKICBmaWx0ZXIobW9kZSA9PSAiQ3ljbGlzdCIsIGludGVyc2VjdGlvbiA9PSAiWWVzIikgIyBGaWx0ZXIgZGF0YSBmb3IgQ3ljbGlzdCBJbnRlcnNlY3Rpb24KCmdyb3VwX3ZhcnMgPC0gYygib25ld2F5IiwgInR3b3dheXMiLCAib25lbGFuZSIsICJ0d29sYW5lcyIsICJ0aHJlZWxhbmVzIiwgImZvdXJsYW5lcyIsIAogICAgICAgICAgICAgICAgImZpdmVsYW5lcyIsICJzaXhsYW5lcyIsICJzaWduYWxpemVkIiwgInN0b3BzaWducyIsICJyb3VuZGFib3V0IiwgCiAgICAgICAgICAgICAgICAiZGVkaWNhdGVkc2lnbmFsZm9yY3ljbGlzdHMiLCAiYmlrZWJveGVzIiwgInR3b3N0YWdldHVybmJveCIsIAogICAgICAgICAgICAgICAgInByb3RlY3RlZG9yZGVkaWNhdGVkaW50ZXJzZWN0aW9uIiwgIm1lZGlhbmlzbGFuZGRpdmVydGVyIiwgCiAgICAgICAgICAgICAgICAiY29tYmluZWRiaWtlbGFuZXR1cm5sYW5lIiwgInRocm91Z2hiaWtlbGFuZSIpCgpjYXRlZ29yaWVzIDwtIGxpc3QoCiAgIlJvYWQgRGlyZWN0aW9uIiA9IGMoIm9uZXdheSIsICJ0d293YXlzIiksCiAgIk51bWJlciBvZiBMYW5lcyIgPSBjKCJvbmVsYW5lIiwgInR3b2xhbmVzIiwgInRocmVlbGFuZXMiLCAiZm91cmxhbmVzIiwgImZpdmVsYW5lcyIsICJzaXhsYW5lcyIpLAogICJJbnRlcnNlY3Rpb24gSW5mcmFzdHJ1Y3R1cmUiID0gYygic2lnbmFsaXplZCIsICJzdG9wc2lnbnMiLCAicm91bmRhYm91dCIpLAogICJDeWNsaXN0IEluZnJhc3RydWN0dXJlIiA9IGMoImRlZGljYXRlZHNpZ25hbGZvcmN5Y2xpc3RzIiwgImJpa2Vib3hlcyIsICJ0d29zdGFnZXR1cm5ib3giLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJvdGVjdGVkb3JkZWRpY2F0ZWRpbnRlcnNlY3Rpb24iLCAibWVkaWFuaXNsYW5kZGl2ZXJ0ZXIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29tYmluZWRiaWtlbGFuZXR1cm5sYW5lIiwgInRocm91Z2hiaWtlbGFuZSIpCikKCm9kZHNfcmF0aW9zX2xpc3QgPC0gbGlzdCgpCnN0YW5kYXJkX2NvbHVtbnMgPC0gYygiQ2F0ZWdvcnkiLCAiVmFyaWFibGUiLCAiTGV2ZWwiLCAiSW5qdXJ5IiwgIkluanVyeV9QZXJjZW50IiwgCiAgICAgICAgICAgICAgICAgICAgICAiTm9uLUluanVyeSIsICJOb24tSW5qdXJ5X1BlcmNlbnQiLCAiT2Rkc1JhdGlvIiwgIkxvd2VyIiwgIlVwcGVyIiwgIlBWYWx1ZSIpCgpmb3IgKGNhdGVnb3J5IGluIG5hbWVzKGNhdGVnb3JpZXMpKSB7CiAgZm9yICh2YXIgaW4gY2F0ZWdvcmllc1tbY2F0ZWdvcnldXSkgewogICAgaWYgKHZhciAlaW4lIG5hbWVzKGN5Y2xpc3RfaW50ZXJzZWN0aW9uX2RhdGEpKSB7CiAgICAgIG9kZHNfcmF0aW9fcmVzdWx0IDwtIHRyeUNhdGNoKHsKICAgICAgICBpZiAobl9kaXN0aW5jdChjeWNsaXN0X2ludGVyc2VjdGlvbl9kYXRhW1t2YXJdXSkgPT0gMikgewogICAgICAgICAgb2Rkc19kYXRhIDwtIGN5Y2xpc3RfaW50ZXJzZWN0aW9uX2RhdGEgJT4lCiAgICAgICAgICAgIHNlbGVjdChhbGxfb2YodmFyKSwgY3Jhc2hzZXZlcml0eSkgJT4lCiAgICAgICAgICAgIHRhYmxlKCkgJT4lCiAgICAgICAgICAgIGFzLm1hdHJpeCgpCiAgICAgICAgICAKICAgICAgICAgIGlmIChhbGwoZGltKG9kZHNfZGF0YSkgPT0gYygyLCAyKSkpIHsgIAogICAgICAgICAgICBvZGRzX3JhdGlvX3RhYmxlIDwtIGFzLmRhdGEuZnJhbWUoZXBpdGFiKG9kZHNfZGF0YSwgbWV0aG9kID0gIm9kZHNyYXRpbyIpJHRhYikgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKGFjcm9zcyh3aGVyZShpcy5udW1lcmljKSwgcm91bmQsIDQpKQogICAgICAgICAgICAKICAgICAgICAgICAgb2Rkc19yYXRpb190YWJsZSA8LSBjYmluZChDYXRlZ29yeSA9IGNhdGVnb3J5LCBWYXJpYWJsZSA9IHZhciwgTGV2ZWwgPSByb3duYW1lcyhvZGRzX3JhdGlvX3RhYmxlKSwgb2Rkc19yYXRpb190YWJsZSkKICAgICAgICAgICAgc2V0TmFtZXMob2Rkc19yYXRpb190YWJsZSwgc3RhbmRhcmRfY29sdW1ucykgIAogICAgICAgICAgfSBlbHNlIHsKICAgICAgICAgICAgc2V0TmFtZXMoZGF0YS5mcmFtZSgKICAgICAgICAgICAgICBDYXRlZ29yeSA9IGNhdGVnb3J5LCBWYXJpYWJsZSA9IHZhciwgTGV2ZWwgPSBOQSwgSW5qdXJ5ID0gIk5vdCBhcHBsaWNhYmxlIiwgCiAgICAgICAgICAgICAgSW5qdXJ5X1BlcmNlbnQgPSBOQSwgTm9uSW5qdXJ5ID0gTkEsIE5vbl9Jbmp1cnlfUGVyY2VudCA9IE5BLCBPZGRzUmF0aW8gPSBOQSwgCiAgICAgICAgICAgICAgTG93ZXIgPSBOQSwgVXBwZXIgPSBOQSwgUFZhbHVlID0gTkEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICAgICApLCBzdGFuZGFyZF9jb2x1bW5zKSAgCiAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIHsKICAgICAgICAgIHNldE5hbWVzKGRhdGEuZnJhbWUoCiAgICAgICAgICAgIENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IE5BLCBJbmp1cnkgPSAiTm90IGFwcGxpY2FibGUiLCAKICAgICAgICAgICAgSW5qdXJ5X1BlcmNlbnQgPSBOQSwgTm9uSW5qdXJ5ID0gTkEsIE5vbl9Jbmp1cnlfUGVyY2VudCA9IE5BLCBPZGRzUmF0aW8gPSBOQSwgCiAgICAgICAgICAgIExvd2VyID0gTkEsIFVwcGVyID0gTkEsIFBWYWx1ZSA9IE5BLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgICAgICksIHN0YW5kYXJkX2NvbHVtbnMpICAKICAgICAgICB9CiAgICAgIH0sIGVycm9yID0gZnVuY3Rpb24oZSkgewogICAgICAgIHNldE5hbWVzKGRhdGEuZnJhbWUoCiAgICAgICAgICBDYXRlZ29yeSA9IGNhdGVnb3J5LCBWYXJpYWJsZSA9IHZhciwgTGV2ZWwgPSBOQSwgSW5qdXJ5ID0gIk5vdCBhcHBsaWNhYmxlIiwgCiAgICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICAgIExvd2VyID0gTkEsIFVwcGVyID0gTkEsIFBWYWx1ZSA9IE5BLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgICApLCBzdGFuZGFyZF9jb2x1bW5zKSAgCiAgICAgIH0pCiAgICAgIAogICAgICBvZGRzX3JhdGlvc19saXN0W1t2YXJdXSA8LSBvZGRzX3JhdGlvX3Jlc3VsdAogICAgfQogIH0KfQoKCm9kZHNfcmF0aW9zX2RhdGEgPC0gZG8uY2FsbChyYmluZCwgb2Rkc19yYXRpb3NfbGlzdCkKCm9kZHNfcmF0aW9zX2RhdGEkT2Rkc1JhdGlvX0NJIDwtIGlmZWxzZShpcy5uYShvZGRzX3JhdGlvc19kYXRhJE9kZHNSYXRpbyksICItIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAob2Rkc19yYXRpb3NfZGF0YSRPZGRzUmF0aW8sICIgKCIsIG9kZHNfcmF0aW9zX2RhdGEkTG93ZXIsICIgLSAiLCBvZGRzX3JhdGlvc19kYXRhJFVwcGVyLCAiKSIpKQoKCm9kZHNfcmF0aW9zX2RhdGEgPC0gb2Rkc19yYXRpb3NfZGF0YSAlPiUgc2VsZWN0KC1Mb3dlciwgLVVwcGVyKQoKCnJvd19jb2xvcnMgPC0gcmVwKGMoIiNmOWY5ZjkiLCAiI2ZmZmZmZiIpLCBsZW5ndGgub3V0ID0gbnJvdyhvZGRzX3JhdGlvc19kYXRhKSkKb2Rkc19yYXRpb3NfZGF0YSRWYXJpYWJsZSA8LSBpZmVsc2UoZHVwbGljYXRlZChvZGRzX3JhdGlvc19kYXRhJFZhcmlhYmxlKSwgIiIsIG9kZHNfcmF0aW9zX2RhdGEkVmFyaWFibGUpCm9kZHNfcmF0aW9zX2RhdGEkQ2F0ZWdvcnkgPC0gaWZlbHNlKGR1cGxpY2F0ZWQob2Rkc19yYXRpb3NfZGF0YSRDYXRlZ29yeSksICIiLCBvZGRzX3JhdGlvc19kYXRhJENhdGVnb3J5KQoKZmluYWxfdGFibGVfaHRtbCA8LSBodG1sVGFibGUoCiAgb2Rkc19yYXRpb3NfZGF0YSAlPiUgCiAgICAKICAgIG11dGF0ZShJbmp1cnlfUGVyY2VudF9Db21iaW5lZCA9IHBhc3RlMChJbmp1cnksICIgLSAoIiwgSW5qdXJ5X1BlcmNlbnQsICIpIikpICU+JQogICAKICAgIG11dGF0ZShOb25fSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQgPSBwYXN0ZTAoYE5vbi1Jbmp1cnlgLCAiIC0gKCIsIGBOb24tSW5qdXJ5X1BlcmNlbnRgLCAiKSIpKSAlPiUKICAgIAogICAgc2VsZWN0KCJDYXRlZ29yeSIsICJWYXJpYWJsZSIsICJMZXZlbCIsICJJbmp1cnlfUGVyY2VudF9Db21iaW5lZCIsICJOb25fSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQiLCAiUFZhbHVlIiwgIk9kZHNSYXRpb19DSSIpICU+JQogICAgcmVuYW1lKCJJbmp1cnkgKCUpIiA9IEluanVyeV9QZXJjZW50X0NvbWJpbmVkLCAiTm9uLUluanVyeSAoJSkiID0gTm9uX0luanVyeV9QZXJjZW50X0NvbWJpbmVkKSAlPiUKICAKICAgIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+IGlmZWxzZShpcy5uYSguKSB8IC4gJWluJSBjKCJOQSIsICJOYU4iLCAiSW5mIiwgIi1JbmYiKSwgIiIsIC4pKSksCiAgaGVhZGVyID0gYygiQ2F0ZWdvcnkiLCAiVmFyaWFibGUiLCAiTGV2ZWwiLCAiSW5qdXJ5ICglKSIsICJOb24tSW5qdXJ5ICglKSIsICJQLVZhbHVlIiwgIk9kZHMgUmF0aW8gKDk1JSBDSSkiKSwKICBhbGlnbiA9ICdsY2NjY2NjJywKICBybmFtZXMgPSBGQUxTRSwKICBjc3MuY2VsbCA9ICJ0ZXh0LWFsaWduOiBjZW50ZXI7IHBhZGRpbmc6IDEycHggMTVweDsgZm9udC1zaXplOiAxMnB4OyB3aGl0ZS1zcGFjZTogbm93cmFwOyIsCiAgY3NzLnJvdyA9IHNwcmludGYoImJhY2tncm91bmQtY29sb3I6ICVzOyIsIHJvd19jb2xvcnMpLAogIGhlYWRlci5jc3MgPSAiYmFja2dyb3VuZC1jb2xvcjogIzMzMzMzMzsgZm9udC1zaXplOiAxNHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNvbG9yOiB3aGl0ZTsiLAogIGNhcHRpb24gPSAiPGgzIHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz5BbmFseXNpcyBmb3IgQ3ljbGlzdCBJbnRlcnNlY3Rpb248L2gzPgogICAgICAgICAgICAgPHAgc3R5bGU9J3RleHQtYWxpZ246IGNlbnRlcjsnPjxlbT5UaGlzIGFuYWx5c2lzIGZvY3VzZXMgb24gbW9kZTogY3ljbGlzdCB8IGludGVyc2VjdGlvbjogeWVzPC9lbT48L3A+IgopCgoKcHJpbnQoSFRNTChmaW5hbF90YWJsZV9odG1sKSkKCmBgYAoKIyMjIyMjIFBlZGVzdHJpYW4gTWlkQmxvY2sKCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1UUlVFLCB3YXJuaW5nPVRSVUV9CgpwZWRlc3RyaWFuX21pZGJsb2NrX2RhdGEgPC0gZGF0YSAlPiUKICBmaWx0ZXIobW9kZSA9PSAiUGVkZXN0cmlhbiIsIGludGVyc2VjdGlvbiA9PSAiTm8iKSAjIEZpbHRlciBkYXRhIGZvciBQZWRlc3RyaWFuIE1pZEJsb2NrCgoKZ3JvdXBfdmFycyA8LSBjKCJvbmV3YXkiLCAidHdvd2F5cyIsICJvbmVsYW5lIiwgInR3b2xhbmVzIiwgInRocmVlbGFuZXMiLCAiZm91cmxhbmVzIiwgCiAgICAgICAgICAgICAgICAiZml2ZWxhbmVzIiwgInNpeGxhbmVzIiwgImNsZWFyem9uZSIsICJvbnN0cmVldHBhcmtpbmciLCAic3RhbmRhcmRwYWludCIsIAogICAgICAgICAgICAgICAgInNvbGlkcGFpbnQiLCAic3RyaXBlZCIsICJvZmZyb2FkbXVsdGl1c2VwYXRod2F5IiwgInNwZWVkaHVtcCIsIAogICAgICAgICAgICAgICAgImFjY2Vzc2libGVzaG91bGRlciIsICJzaWRld2Fsa3MiKQoKCmNhdGVnb3JpZXMgPC0gbGlzdCgKICAiUm9hZCBEaXJlY3Rpb24iID0gYygib25ld2F5IiwgInR3b3dheXMiKSwKICAiTnVtYmVyIG9mIExhbmVzIiA9IGMoIm9uZWxhbmUiLCAidHdvbGFuZXMiLCAidGhyZWVsYW5lcyIsICJmb3VybGFuZXMiLCAiZml2ZWxhbmVzIiwgInNpeGxhbmVzIiksCiAgIlBlZGVzdHJpYW4gSW5mcmFzdHJ1Y3R1cmUiID0gYygiYWNjZXNzaWJsZXNob3VsZGVyIiwgInNpZGV3YWxrcyIsICAgInN0YW5kYXJkcGFpbnQiLCAic29saWRwYWludCIsICJzdHJpcGVkIiksCiAgIk90aGVyIEluZnJhc3RydWN0dXJlIiA9IGMoICJjbGVhcnpvbmUiLCAib25zdHJlZXRwYXJraW5nIiwgIm9mZnJvYWRtdWx0aXVzZXBhdGh3YXkiLCAic3BlZWRodW1wIikKKQoKb2Rkc19yYXRpb3NfbGlzdCA8LSBsaXN0KCkKc3RhbmRhcmRfY29sdW1ucyA8LSBjKCJDYXRlZ29yeSIsICJWYXJpYWJsZSIsICJMZXZlbCIsICJJbmp1cnkiLCAiSW5qdXJ5X1BlcmNlbnQiLCAKICAgICAgICAgICAgICAgICAgICAgICJOb24tSW5qdXJ5IiwgIk5vbi1Jbmp1cnlfUGVyY2VudCIsICJPZGRzUmF0aW8iLCAiTG93ZXIiLCAiVXBwZXIiLCAiUFZhbHVlIikKCmZvciAoY2F0ZWdvcnkgaW4gbmFtZXMoY2F0ZWdvcmllcykpIHsKICBmb3IgKHZhciBpbiBjYXRlZ29yaWVzW1tjYXRlZ29yeV1dKSB7CiAgICBpZiAodmFyICVpbiUgbmFtZXMocGVkZXN0cmlhbl9taWRibG9ja19kYXRhKSkgewogICAgICBvZGRzX3JhdGlvX3Jlc3VsdCA8LSB0cnlDYXRjaCh7CiAgICAgICAgaWYgKG5fZGlzdGluY3QocGVkZXN0cmlhbl9taWRibG9ja19kYXRhW1t2YXJdXSkgPT0gMikgewogICAgICAgICAgb2Rkc19kYXRhIDwtIHBlZGVzdHJpYW5fbWlkYmxvY2tfZGF0YSAlPiUKICAgICAgICAgICAgc2VsZWN0KGFsbF9vZih2YXIpLCBjcmFzaHNldmVyaXR5KSAlPiUKICAgICAgICAgICAgdGFibGUoKSAlPiUKICAgICAgICAgICAgYXMubWF0cml4KCkKICAgICAgICAgIAogICAgICAgICAgaWYgKGFsbChkaW0ob2Rkc19kYXRhKSA9PSBjKDIsIDIpKSkgeyAgCiAgICAgICAgICAgIG9kZHNfcmF0aW9fdGFibGUgPC0gYXMuZGF0YS5mcmFtZShlcGl0YWIob2Rkc19kYXRhLCBtZXRob2QgPSAib2Rkc3JhdGlvIikkdGFiKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgNCkpCiAgICAgICAgICAgIAogICAgICAgICAgICBvZGRzX3JhdGlvX3RhYmxlIDwtIGNiaW5kKENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IHJvd25hbWVzKG9kZHNfcmF0aW9fdGFibGUpLCBvZGRzX3JhdGlvX3RhYmxlKQogICAgICAgICAgICBzZXROYW1lcyhvZGRzX3JhdGlvX3RhYmxlLCBzdGFuZGFyZF9jb2x1bW5zKSAgCiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBzZXROYW1lcyhkYXRhLmZyYW1lKAogICAgICAgICAgICAgIENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IE5BLCBJbmp1cnkgPSAiTm90IGFwcGxpY2FibGUiLCAKICAgICAgICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICAgICAgICBMb3dlciA9IE5BLCBVcHBlciA9IE5BLCBQVmFsdWUgPSBOQSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICAgICAgICAgICksIHN0YW5kYXJkX2NvbHVtbnMpICAKICAgICAgICAgIH0KICAgICAgICB9IGVsc2UgewogICAgICAgICAgc2V0TmFtZXMoZGF0YS5mcmFtZSgKICAgICAgICAgICAgQ2F0ZWdvcnkgPSBjYXRlZ29yeSwgVmFyaWFibGUgPSB2YXIsIExldmVsID0gTkEsIEluanVyeSA9ICJOb3QgYXBwbGljYWJsZSIsIAogICAgICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICAgICAgTG93ZXIgPSBOQSwgVXBwZXIgPSBOQSwgUFZhbHVlID0gTkEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICAgKSwgc3RhbmRhcmRfY29sdW1ucykgIAogICAgICAgIH0KICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgICAgICAgc2V0TmFtZXMoZGF0YS5mcmFtZSgKICAgICAgICAgIENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IE5BLCBJbmp1cnkgPSAiTm90IGFwcGxpY2FibGUiLCAKICAgICAgICAgIEluanVyeV9QZXJjZW50ID0gTkEsIE5vbkluanVyeSA9IE5BLCBOb25fSW5qdXJ5X1BlcmNlbnQgPSBOQSwgT2Rkc1JhdGlvID0gTkEsIAogICAgICAgICAgTG93ZXIgPSBOQSwgVXBwZXIgPSBOQSwgUFZhbHVlID0gTkEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICksIHN0YW5kYXJkX2NvbHVtbnMpICAKICAgICAgfSkKICAgICAgCiAgICAgIG9kZHNfcmF0aW9zX2xpc3RbW3Zhcl1dIDwtIG9kZHNfcmF0aW9fcmVzdWx0CiAgICB9CiAgfQp9CgoKb2Rkc19yYXRpb3NfZGF0YSA8LSBkby5jYWxsKHJiaW5kLCBvZGRzX3JhdGlvc19saXN0KQoKCm9kZHNfcmF0aW9zX2RhdGEkT2Rkc1JhdGlvX0NJIDwtIGlmZWxzZShpcy5uYShvZGRzX3JhdGlvc19kYXRhJE9kZHNSYXRpbyksICItIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAob2Rkc19yYXRpb3NfZGF0YSRPZGRzUmF0aW8sICIgKCIsIG9kZHNfcmF0aW9zX2RhdGEkTG93ZXIsICIgLSAiLCBvZGRzX3JhdGlvc19kYXRhJFVwcGVyLCAiKSIpKQoKCm9kZHNfcmF0aW9zX2RhdGEgPC0gb2Rkc19yYXRpb3NfZGF0YSAlPiUgc2VsZWN0KC1Mb3dlciwgLVVwcGVyKQoKCnJvd19jb2xvcnMgPC0gcmVwKGMoIiNmOWY5ZjkiLCAiI2ZmZmZmZiIpLCBsZW5ndGgub3V0ID0gbnJvdyhvZGRzX3JhdGlvc19kYXRhKSkKb2Rkc19yYXRpb3NfZGF0YSRWYXJpYWJsZSA8LSBpZmVsc2UoZHVwbGljYXRlZChvZGRzX3JhdGlvc19kYXRhJFZhcmlhYmxlKSwgIiIsIG9kZHNfcmF0aW9zX2RhdGEkVmFyaWFibGUpCm9kZHNfcmF0aW9zX2RhdGEkQ2F0ZWdvcnkgPC0gaWZlbHNlKGR1cGxpY2F0ZWQob2Rkc19yYXRpb3NfZGF0YSRDYXRlZ29yeSksICIiLCBvZGRzX3JhdGlvc19kYXRhJENhdGVnb3J5KQoKIApmaW5hbF90YWJsZV9odG1sIDwtIGh0bWxUYWJsZSgKICBvZGRzX3JhdGlvc19kYXRhICU+JSAKICAgCiAgICBtdXRhdGUoSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQgPSBwYXN0ZTAoSW5qdXJ5LCAiIC0gKCIsIEluanVyeV9QZXJjZW50LCAiKSIpKSAlPiUKICAgIAogICAgbXV0YXRlKE5vbl9Jbmp1cnlfUGVyY2VudF9Db21iaW5lZCA9IHBhc3RlMChgTm9uLUluanVyeWAsICIgLSAoIiwgYE5vbi1Jbmp1cnlfUGVyY2VudGAsICIpIikpICU+JQogICAgCiAgICBzZWxlY3QoIkNhdGVnb3J5IiwgIlZhcmlhYmxlIiwgIkxldmVsIiwgIkluanVyeV9QZXJjZW50X0NvbWJpbmVkIiwgIk5vbl9Jbmp1cnlfUGVyY2VudF9Db21iaW5lZCIsICJQVmFsdWUiLCAiT2Rkc1JhdGlvX0NJIikgJT4lCiAgICByZW5hbWUoIkluanVyeSAoJSkiID0gSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQsICJOb24tSW5qdXJ5ICglKSIgPSBOb25fSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQpICU+JQogICAKICAgIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCB+IGlmZWxzZShpcy5uYSguKSB8IC4gJWluJSBjKCJOQSIsICJOYU4iLCAiSW5mIiwgIi1JbmYiKSwgIiIsIC4pKSksCiAgaGVhZGVyID0gYygiQ2F0ZWdvcnkiLCAiVmFyaWFibGUiLCAiTGV2ZWwiLCAiSW5qdXJ5ICglKSIsICJOb24tSW5qdXJ5ICglKSIsICJQLVZhbHVlIiwgIk9kZHMgUmF0aW8gKDk1JSBDSSkiKSwKICBhbGlnbiA9ICdsY2NjY2NjJywKICBybmFtZXMgPSBGQUxTRSwKICBjc3MuY2VsbCA9ICJ0ZXh0LWFsaWduOiBjZW50ZXI7IHBhZGRpbmc6IDEycHggMTVweDsgZm9udC1zaXplOiAxMnB4OyB3aGl0ZS1zcGFjZTogbm93cmFwOyIsCiAgY3NzLnJvdyA9IHNwcmludGYoImJhY2tncm91bmQtY29sb3I6ICVzOyIsIHJvd19jb2xvcnMpLAogIGhlYWRlci5jc3MgPSAiYmFja2dyb3VuZC1jb2xvcjogIzMzMzMzMzsgZm9udC1zaXplOiAxNHB4OyB0ZXh0LWFsaWduOiBjZW50ZXI7IGNvbG9yOiB3aGl0ZTsiLAogIGNhcHRpb24gPSAiPGgzIHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz5BbmFseXNpcyBmb3IgUGVkZXN0cmlhbiBNaWRCbG9jazwvaDM+CiAgICAgICAgICAgICA8cCBzdHlsZT0ndGV4dC1hbGlnbjogY2VudGVyOyc+PGVtPlRoaXMgYW5hbHlzaXMgZm9jdXNlcyBvbiBtb2RlOiBwZWRlc3RyaWFuIHwgaW50ZXJzZWN0aW9uOiBubzwvZW0+PC9wPiIKKQoKCnByaW50KEhUTUwoZmluYWxfdGFibGVfaHRtbCkpCgpgYGAKCiMjIyMjIyBQZWRlc3RyaWFuIEludGVyc2VjdGlvbgoKYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9VFJVRX0KCnBlZGVzdHJpYW5faW50ZXJzZWN0aW9uX2RhdGEgPC0gZGF0YSAlPiUKICBmaWx0ZXIobW9kZSA9PSAiUGVkZXN0cmlhbiIsIGludGVyc2VjdGlvbiA9PSAiWWVzIikgIyBGaWx0ZXIgZGF0YSBmb3IgUGVkZXN0cmlhbiBJbnRlcnNlY3Rpb24KCgpncm91cF92YXJzIDwtIGMoIm9uZXdheSIsICJ0d293YXlzIiwgIm9uZWxhbmUiLCAidHdvbGFuZXMiLCAidGhyZWVsYW5lcyIsICJmb3VybGFuZXMiLCAKICAgICAgICAgICAgICAgICJmaXZlbGFuZXMiLCAic2l4bGFuZXMiLCAic2lnbmFsaXplZCIsICJzdG9wc2lnbnMiLCAicm91bmRhYm91dCIsIAogICAgICAgICAgICAgICAgIm51bWJlcm9mbGVnc2ludGhlaW50ZXJzZWN0aW9uIiwgInRocmVlbGVncyIsICJmb3VybGVncyIsICJmb3VybWxlZ3MiLCAKICAgICAgICAgICAgICAgICJ1bnNpZ25hbGl6ZWRjcm9zc3dhbGsiLCAic2lnbmFsaXplZGNyb3Nzd2Fsa3N0cmVldCIsICJzdGFuZGFyZHBhaW50IiwgCiAgICAgICAgICAgICAgICAic29saWRwYWludCIsICJzdHJpcGVkIiwgInBlZGVzdHJpYW5yZWZ1Z2VzdHJpcGVkIiwgCiAgICAgICAgICAgICAgICAiY3VyYmV4dGVuc2lvbnBpbmNocG9pbnQiLCAicmFwaWRyZWN0YW5ndWxhcmZsYXNoaW5nYmVhY29uIiwgCiAgICAgICAgICAgICAgICAicGVkZXN0cmlhbmNyb3NzZWRzaWduYWwiKQoKCmNhdGVnb3JpZXMgPC0gbGlzdCgKICAiUm9hZCBEaXJlY3Rpb24iID0gYygib25ld2F5IiwgInR3b3dheXMiKSwKICAiTnVtYmVyIG9mIExlZ3MiID0gYygidGhyZWVsZWdzIiwgImZvdXJsZWdzIiwgImZvdXJtbGVncyIpLAogICJOdW1iZXIgb2YgTGFuZXMiID0gYygib25lbGFuZSIsICJ0d29sYW5lcyIsICJ0aHJlZWxhbmVzIiwgImZvdXJsYW5lcyIsICJmaXZlbGFuZXMiLCAic2l4bGFuZXMiKSwKICAiUGVkZXN0cmlhbiBJbmZyYXN0cnVjdHVyZSIgPSBjKCJwZWRlc3RyaWFucmVmdWdlc3RyaXBlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjdXJiZXh0ZW5zaW9ucGluY2hwb2ludCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJyYXBpZHJlY3Rhbmd1bGFyZmxhc2hpbmdiZWFjb24iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicGVkZXN0cmlhbmNyb3NzZWRzaWduYWwiKSwKICAKICAiT3RoZXIgVmFyaWFibGVzIiA9IGMoInNpZ25hbGl6ZWQiLCAic3RvcHNpZ25zIiwgInJvdW5kYWJvdXQiLCAKICAgICAgICAgICAgICAgICJudW1iZXJvZmxlZ3NpbnRoZWludGVyc2VjdGlvbiIsICAgCiAgICAgICAgICAgICAgICAidW5zaWduYWxpemVkY3Jvc3N3YWxrIiwgInNpZ25hbGl6ZWRjcm9zc3dhbGtzdHJlZXQiLCAic3RhbmRhcmRwYWludCIsIAogICAgICAgICAgICAgICAgInNvbGlkcGFpbnQiLCAic3RyaXBlZCIpCikKCm9kZHNfcmF0aW9zX2xpc3QgPC0gbGlzdCgpCnN0YW5kYXJkX2NvbHVtbnMgPC0gYygiQ2F0ZWdvcnkiLCAiVmFyaWFibGUiLCAiTGV2ZWwiLCAiSW5qdXJ5IiwgIkluanVyeV9QZXJjZW50IiwgCiAgICAgICAgICAgICAgICAgICAgICAiTm9uLUluanVyeSIsICJOb24tSW5qdXJ5X1BlcmNlbnQiLCAiT2Rkc1JhdGlvIiwgIkxvd2VyIiwgIlVwcGVyIiwgIlBWYWx1ZSIpCgpmb3IgKGNhdGVnb3J5IGluIG5hbWVzKGNhdGVnb3JpZXMpKSB7CiAgZm9yICh2YXIgaW4gY2F0ZWdvcmllc1tbY2F0ZWdvcnldXSkgewogICAgaWYgKHZhciAlaW4lIG5hbWVzKHBlZGVzdHJpYW5faW50ZXJzZWN0aW9uX2RhdGEpKSB7CiAgICAgIG9kZHNfcmF0aW9fcmVzdWx0IDwtIHRyeUNhdGNoKHsKICAgICAgICBpZiAobl9kaXN0aW5jdChwZWRlc3RyaWFuX2ludGVyc2VjdGlvbl9kYXRhW1t2YXJdXSkgPT0gMikgewogICAgICAgICAgb2Rkc19kYXRhIDwtIHBlZGVzdHJpYW5faW50ZXJzZWN0aW9uX2RhdGEgJT4lCiAgICAgICAgICAgIHNlbGVjdChhbGxfb2YodmFyKSwgY3Jhc2hzZXZlcml0eSkgJT4lCiAgICAgICAgICAgIHRhYmxlKCkgJT4lCiAgICAgICAgICAgIGFzLm1hdHJpeCgpCiAgICAgICAgICAKICAgICAgICAgIGlmIChhbGwoZGltKG9kZHNfZGF0YSkgPT0gYygyLCAyKSkpIHsgCiAgICAgICAgICAgIG9kZHNfcmF0aW9fdGFibGUgPC0gYXMuZGF0YS5mcmFtZShlcGl0YWIob2Rkc19kYXRhLCBtZXRob2QgPSAib2Rkc3JhdGlvIikkdGFiKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLm51bWVyaWMpLCByb3VuZCwgNCkpCiAgICAgICAgICAgIAogICAgICAgICAgICBvZGRzX3JhdGlvX3RhYmxlIDwtIGNiaW5kKENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IHJvd25hbWVzKG9kZHNfcmF0aW9fdGFibGUpLCBvZGRzX3JhdGlvX3RhYmxlKQogICAgICAgICAgICBzZXROYW1lcyhvZGRzX3JhdGlvX3RhYmxlLCBzdGFuZGFyZF9jb2x1bW5zKSAgCiAgICAgICAgICB9IGVsc2UgewogICAgICAgICAgICBzZXROYW1lcyhkYXRhLmZyYW1lKAogICAgICAgICAgICAgIENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IE5BLCBJbmp1cnkgPSAiTm90IGFwcGxpY2FibGUiLCAKICAgICAgICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICAgICAgICBMb3dlciA9IE5BLCBVcHBlciA9IE5BLCBQVmFsdWUgPSBOQSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICAgICAgICAgICksIHN0YW5kYXJkX2NvbHVtbnMpICAKICAgICAgICAgIH0KICAgICAgICB9IGVsc2UgewogICAgICAgICAgc2V0TmFtZXMoZGF0YS5mcmFtZSgKICAgICAgICAgICAgQ2F0ZWdvcnkgPSBjYXRlZ29yeSwgVmFyaWFibGUgPSB2YXIsIExldmVsID0gTkEsIEluanVyeSA9ICJOb3QgYXBwbGljYWJsZSIsIAogICAgICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICAgICAgTG93ZXIgPSBOQSwgVXBwZXIgPSBOQSwgUFZhbHVlID0gTkEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICAgKSwgc3RhbmRhcmRfY29sdW1ucykgIAogICAgICAgIH0KICAgICAgfSwgZXJyb3IgPSBmdW5jdGlvbihlKSB7CiAgICAgICAgc2V0TmFtZXMoZGF0YS5mcmFtZSgKICAgICAgICAgIENhdGVnb3J5ID0gY2F0ZWdvcnksIFZhcmlhYmxlID0gdmFyLCBMZXZlbCA9IE5BLCBJbmp1cnkgPSAiTm90IGFwcGxpY2FibGUiLCAKICAgICAgICAgIEluanVyeV9QZXJjZW50ID0gTkEsIE5vbkluanVyeSA9IE5BLCBOb25fSW5qdXJ5X1BlcmNlbnQgPSBOQSwgT2Rkc1JhdGlvID0gTkEsIAogICAgICAgICAgTG93ZXIgPSBOQSwgVXBwZXIgPSBOQSwgUFZhbHVlID0gTkEsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICAgICAgICksIHN0YW5kYXJkX2NvbHVtbnMpICAKICAgICAgfSkKICAgICAgCiAgICAgIG9kZHNfcmF0aW9zX2xpc3RbW3Zhcl1dIDwtIG9kZHNfcmF0aW9fcmVzdWx0CiAgICB9IGVsc2UgewogICAgICAKICAgICAgb2Rkc19yYXRpb3NfbGlzdFtbdmFyXV0gPC0gc2V0TmFtZXMoZGF0YS5mcmFtZSgKICAgICAgICBDYXRlZ29yeSA9IGNhdGVnb3J5LCBWYXJpYWJsZSA9IHZhciwgTGV2ZWwgPSBOQSwgSW5qdXJ5ID0gIlZhcmlhYmxlIG5vdCBhdmFpbGFibGUiLCAKICAgICAgICBJbmp1cnlfUGVyY2VudCA9IE5BLCBOb25Jbmp1cnkgPSBOQSwgTm9uX0luanVyeV9QZXJjZW50ID0gTkEsIE9kZHNSYXRpbyA9IE5BLCAKICAgICAgICBMb3dlciA9IE5BLCBVcHBlciA9IE5BLCBQVmFsdWUgPSBOQSwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCiAgICAgICksIHN0YW5kYXJkX2NvbHVtbnMpCiAgICB9CiAgfQp9CgoKb2Rkc19yYXRpb3NfZGF0YSA8LSBkby5jYWxsKHJiaW5kLCBvZGRzX3JhdGlvc19saXN0KQoKCm9kZHNfcmF0aW9zX2RhdGEkT2Rkc1JhdGlvX0NJIDwtIGlmZWxzZShpcy5uYShvZGRzX3JhdGlvc19kYXRhJE9kZHNSYXRpbyksICItIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZTAob2Rkc19yYXRpb3NfZGF0YSRPZGRzUmF0aW8sICIgKCIsIG9kZHNfcmF0aW9zX2RhdGEkTG93ZXIsICIgLSAiLCBvZGRzX3JhdGlvc19kYXRhJFVwcGVyLCAiKSIpKQoKCm9kZHNfcmF0aW9zX2RhdGEgPC0gb2Rkc19yYXRpb3NfZGF0YSAlPiUgc2VsZWN0KC1Mb3dlciwgLVVwcGVyKQoKCnJvd19jb2xvcnMgPC0gcmVwKGMoIiNmOWY5ZjkiLCAiI2ZmZmZmZiIpLCBsZW5ndGgub3V0ID0gbnJvdyhvZGRzX3JhdGlvc19kYXRhKSkKb2Rkc19yYXRpb3NfZGF0YSRWYXJpYWJsZSA8LSBpZmVsc2UoZHVwbGljYXRlZChvZGRzX3JhdGlvc19kYXRhJFZhcmlhYmxlKSwgIiIsIG9kZHNfcmF0aW9zX2RhdGEkVmFyaWFibGUpCm9kZHNfcmF0aW9zX2RhdGEkQ2F0ZWdvcnkgPC0gaWZlbHNlKGR1cGxpY2F0ZWQob2Rkc19yYXRpb3NfZGF0YSRDYXRlZ29yeSksICIiLCBvZGRzX3JhdGlvc19kYXRhJENhdGVnb3J5KQoKCmZpbmFsX3RhYmxlX2h0bWwgPC0gaHRtbFRhYmxlKAogIG9kZHNfcmF0aW9zX2RhdGEgJT4lIAogICAgCiAgICBtdXRhdGUoSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQgPSBwYXN0ZTAoSW5qdXJ5LCAiIC0gKCIsIEluanVyeV9QZXJjZW50LCAiKSIpKSAlPiUKICAgCiAgICBtdXRhdGUoTm9uX0luanVyeV9QZXJjZW50X0NvbWJpbmVkID0gcGFzdGUwKGBOb24tSW5qdXJ5YCwgIiAtICgiLCBgTm9uLUluanVyeV9QZXJjZW50YCwgIikiKSkgJT4lCiAgIAogICAgc2VsZWN0KCJDYXRlZ29yeSIsICJWYXJpYWJsZSIsICJMZXZlbCIsICJJbmp1cnlfUGVyY2VudF9Db21iaW5lZCIsICJOb25fSW5qdXJ5X1BlcmNlbnRfQ29tYmluZWQiLCAiUFZhbHVlIiwgIk9kZHNSYXRpb19DSSIpICU+JQogICAgcmVuYW1lKCJJbmp1cnkgKCUpIiA9IEluanVyeV9QZXJjZW50X0NvbWJpbmVkLCAiTm9uLUluanVyeSAoJSkiID0gTm9uX0luanVyeV9QZXJjZW50X0NvbWJpbmVkKSAlPiUKICAgCiAgICBtdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgfiBpZmVsc2UoaXMubmEoLikgfCAuICVpbiUgYygiTkEiLCAiTmFOIiwgIkluZiIsICItSW5mIiksICIiLCAuKSkpLAogIGhlYWRlciA9IGMoIkNhdGVnb3J5IiwgIlZhcmlhYmxlIiwgIkxldmVsIiwgIkluanVyeSAoJSkiLCAiTm9uLUluanVyeSAoJSkiLCAiUC1WYWx1ZSIsICJPZGRzIFJhdGlvICg5NSUgQ0kpIiksCiAgYWxpZ24gPSAnbGNjY2NjYycsCiAgcm5hbWVzID0gRkFMU0UsCiAgY3NzLmNlbGwgPSAidGV4dC1hbGlnbjogY2VudGVyOyBwYWRkaW5nOiAxMnB4IDE1cHg7IGZvbnQtc2l6ZTogMTJweDsgd2hpdGUtc3BhY2U6IG5vd3JhcDsiLAogIGNzcy5yb3cgPSBzcHJpbnRmKCJiYWNrZ3JvdW5kLWNvbG9yOiAlczsiLCByb3dfY29sb3JzKSwKICBoZWFkZXIuY3NzID0gImJhY2tncm91bmQtY29sb3I6ICMzMzMzMzM7IGZvbnQtc2l6ZTogMTRweDsgdGV4dC1hbGlnbjogY2VudGVyOyBjb2xvcjogd2hpdGU7IiwKICBjYXB0aW9uID0gIjxoMyBzdHlsZT0ndGV4dC1hbGlnbjogY2VudGVyOyc+QW5hbHlzaXMgZm9yIFBlZGVzdHJpYW4gSW50ZXJzZWN0aW9uPC9oMz4KICAgICAgICAgICAgIDxwIHN0eWxlPSd0ZXh0LWFsaWduOiBjZW50ZXI7Jz48ZW0+VGhpcyBhbmFseXNpcyBmb2N1c2VzIG9uIG1vZGU6IHBlZGVzdHJpYW4gfCBpbnRlcnNlY3Rpb246IHllczwvZW0+PC9wPiIKKQoKCnByaW50KEhUTUwoZmluYWxfdGFibGVfaHRtbCkpCmBgYAoKCg==