Github repository link

0.0 Read me

|| 0.0|READ ME || 1.0|EXTRACT || 2.0|TRANSFORM || 3.0|LOAD and ANALYSE || 4.0|ACLED data || 5.0|ACLED data ||

Purpose of section| brief outline of document, its content and prerequisites
Inputs | none
Outputs | none


0.1 Outline of document

This notebook outlines the process and code outputs associated with the project Analysing the frontlines of diplomacy: the case for unleashing GIS (A proof of concept algorithm supplemented with i| indicative clustering analysis of diplomatic post locations and ii| conflict events within cities)

Final submission can be found here 13.01.2020


Appendix G.2 | complete process flow


0.3 Pre-requisites and set up

In order for the code below to work, please ensure you have installed and loaded the relevant packages.

#Installing packages (uncomment as required)
#install.packages(c("countrycode","tidyverse","sf","mapview","ggmap","RCurl","jsonlite","RCurl","jsonlite"))

library(countrycode) #iso tagging of countries
library(tidyverse) #number of packages for data manipulation, specifically want readr, dplyr, tibble
library(sf) #creation of spatial objects
library(mapview) #mapping package
library(ggmap) #for google api access 
library(RCurl) #to download ACLED content
library(jsonlite) #to process ACLED downloaded info


Setting to use local files or refresh geocoding via API calls.

#Set this to TRUE or FALSE to determine whether to use local, pre-loaded files or refresh via relevant API, i.e. (Google) connections.
use_local_files <- TRUE

1.0 Extract GDI dataset

|| 0.0|READ ME || 1.0|EXTRACT || 2.0|TRANSFORM || 3.0|LOAD and ANALYSE || 4.0|ACLED data || 5.0|ACLED cases ||

Purpose of section | load GDI dataset from Lowy Institute site and summarise
Inputs | GDI dataset available here
Outputs| Renamed and separated .csv files

Note | downloading from the Lowy Institute site, there may be incompatibility with downloads in Safari, chrome is recommended, have not tested other browsers.


1.1 Load .csv file and check data types

Back to index

Load 2019 GDI dataset

###open latest csv document based on relative path
lowy_raw_data <- read.csv("./Data/Raw_data/2019_LowyInstituteGlobalDiplomacyIndex_FullDataset.csv")

1.2 Tidy up dataset

Back to index
Check for| factors; rename columns; seperate out relevant columns
Output|

  1. renamed data frame lowy_renamed
  2. other meta data lowy_meta
  3. ranking data lowy_rank
  4. posting data lowy_posting <=== this is the relevant one for the rest of the work

Tidy dataset, see preview for table.

###check for factor columns and change them to character columns
#more of a pre-emptive measure
i <- sapply(lowy_raw_data, is.factor)
lowy_raw_data[i] <- lapply(lowy_raw_data[i], as.character)

###column rename
#new data frame (a)
lowy_renamed <- lowy_raw_data

#rename columns
lowy_renamed <- lowy_renamed %>% dplyr::rename(
  send_country=COUNTRY,
  #metadata
  meta_gdp_usd_billions = GDP..B..USD.,
  meta_population_millions = POPULATION..M.,
  #ranking columns
  rank_g20 = G20.RANK,
  rank_OECD = OECD.RANK,
  rank_asia = ASIA.RANK,
  rank_overall = OVERALL.RANK,
  #post country info
  post_city = POST.CITY,
  post_country = POST.COUNTRY,
  post_type = TYPE.OF.POST,
  post_title = POST.TYPE.TITLE
  )

#data frame with all metadata (GDP and population) (b)
lowy_meta <- lowy_renamed[, c("send_country","meta_population_millions","meta_gdp_usd_billions")]

#data frame with all rankings (G20, OECD, Asia and overall) (c)
lowy_rank <- lowy_renamed[,c("send_country","rank_g20","rank_OECD","rank_asia","rank_overall")]

#data frame with information on sending country, post city, country as well as post type and title (d)
lowy_posting <- lowy_renamed[,c("send_country","post_city","post_country","post_type","post_title")]

###save data frames as .csv files ready for part 2
write.csv(lowy_posting,"./Data/Processing/Part_1_Out_Lowy_Posting.csv", row.names = FALSE)
write.csv(lowy_rank,"./Data/Processing/Part_1_Out_Lowy_Rank.csv", row.names = FALSE)
write.csv(lowy_meta,"./Data/Processing/Part_1_Out_Lowy_Meta.csv", row.names = FALSE)

#quick preview of data frame to work on
head(lowy_posting,3)

1.3 Raw key information

Back to index

#Length of dataframe:
cat("Lowy Institute's GDI dataset has",length(lowy_posting[[1]]),"entries.")
Lowy Institute's GDI dataset has 7317 entries.
###report all unique values/entries
#Summarise all unique values in crude print out
for(column in colnames(lowy_posting)){
  cat(column,"has",length(apply(lowy_posting[column],2,unique)),"unique values\n")
}
send_country has 61 unique values
post_city has 686 unique values
post_country has 207 unique values
post_type has 4 unique values
post_title has 141 unique values
###summary stats
lowy_type_table <- ftable(table(lowy_posting$send_country,lowy_posting$post_type))

###summarise count by send country
lowy_count_per_send_country <- lowy_posting %>% 
  group_by(send_country) %>% 
  summarise(count = n()) %>% 
  arrange(desc(count))

per_country_count <- lowy_count_per_send_country$count
par(family = "Times New Roman")
per_country_box <- boxplot(data=per_country_count, x=per_country_count, main="Diplomatic post count by sending country", 
   xlab="All post types", ylab="Count of posts",notch = TRUE)


cat("Per country count summary",
    "\nMin value:",per_country_box$stats[1,1],
    "\n25th percentile:",per_country_box$stats[2,1],
    "\nMedian value:",per_country_box$stats[3,1],
    "\n75th percentile:",per_country_box$stats[4,1],
    "\nMax value:",per_country_box$stats[5,1]
    )
Per country count summary 
Min value: 9 
25th percentile: 67 
Median value: 107 
75th percentile: 152 
Max value: 276
###summarise count by post city
lowy_count_per_post_city <- lowy_posting %>% 
  group_by(post_city) %>% 
  summarise(count = n()) %>% 
  arrange(desc(count))

per_post_city_count <- lowy_count_per_post_city$count

per_post_city_box <- boxplot(data=per_post_city_count, x=per_post_city_count, main="Diplomatic post count by post city ", 
   xlab="All post types", ylab="Count of posts",notch = TRUE)

cat("Per post city count summary",
    "\nMin value:",per_post_city_box$stats[1,1],
    "\n25th percentile:",per_post_city_box$stats[2,1],
    "\nMedian value:",per_post_city_box$stats[3,1],
    "\n75th percentile:",per_post_city_box$stats[4,1],
    "\nMax value:",per_post_city_box$stats[5,1]
    )
Per post city count summary 
Min value: 1 
25th percentile: 1 
Median value: 2 
75th percentile: 14 
Max value: 33
###summarise count by post country
lowy_count_per_post_country <- lowy_posting %>% 
  group_by(post_country) %>% 
  summarise(count = n()) %>% 
  arrange(desc(count))

per_post_country_count <- lowy_count_per_post_country$count

per_post_country_box <- boxplot(data=per_post_country_count, x=per_post_country_count, main="Diplomatic post count by post country", 
   xlab="All post types", ylab="Count of posts",notch = TRUE)

cat("Per post country count summary",
    "\nMin value:",per_post_country_box$stats[1,1],
    "\n25th percentile:",per_post_country_box$stats[2,1],
    "\nMedian value:",per_post_country_box$stats[3,1],
    "\n75th percentile:",per_post_country_box$stats[4,1],
    "\nMax value:",per_post_country_box$stats[5,1]
    )
Per post country count summary 
Min value: 1 
25th percentile: 9 
Median value: 23 
75th percentile: 44.5 
Max value: 91

2.0 Transform GDI data

|| 0.0|READ ME || 1.0|EXTRACT || 2.0|TRANSFORM || 3.0|LOAD and ANALYSE || 4.0|ACLED data || 5.0|ACLED data ||

Purpose of section | Load,.csv files; add country iso codes to main data frame; and geocode send country+post city+post+country+exact embassy location with Google API
Inputs | Part_1_Out_Lowy_Posting.csv from end of part 1
Outputs | lowy_data_iso master geocoded table


2.1 Re-load

Back to index

lowy_data <- read_csv("./Data/Processing/Part_1_Out_Lowy_Posting.csv")

2.2 ISO codes

Back to index

###Set up

#iso dataframe
lowy_data_iso <- lowy_data

#country_iso list [column 1]
iso_list_send_country <- c()
iso_list_send_country_num <- c()
#post_country_iso list [column 3]
iso_list_post_country <- c()
iso_list_post_country_num <- c()

###ending country column (1) three digit numbers
for(country in lowy_data_iso[1]){
  country_iso <- countrycode(
    #call the entry of 'Country' for the row_number currently being taken by for loop
    sourcevar = country, 
    #three letter iso code
    destination = "iso3n",
    #based on english language names
    origin = "country.name")
  
  #add value to a list
  iso_list_send_country_num <- append(iso_list_send_country_num,country_iso)
}

###sending country column (1) three digit letters
for(country in lowy_data_iso[1]){
  country_iso <- countrycode(
    #call the entry of 'Country' for the row_number currently being taken by for loop
    sourcevar = country, 
    #three letter iso code
    destination = "genc3c", 
    #based on english language names
    origin = "country.name")
  
  #add value to a list
  iso_list_send_country <- append(iso_list_send_country,country_iso)
}
#===================================
###Post country column (3) three digit numbers
for(country in lowy_data_iso[3]){
  post_country_iso <- countrycode(
    #call the entry of 'Country' for the row_number currently being taken by for loop
    sourcevar = country, 
    #three letter iso code
    destination = "iso3n", 
    #based on english language names
    origin = "country.name")
  
  #add value to a list
  iso_list_post_country_num <- append(iso_list_post_country_num,post_country_iso)
}
Some values were not matched unambiguously: Abkhazia*, Cura�ao, Jerusalem*, Kosovo*, South Ossetia*
###Post country column (3) three letters
for(country in lowy_data_iso[3]){
  post_country_iso <- countrycode(
    #call the entry of 'Country' for the row_number currently being taken by for loop
    sourcevar = country, 
    #three letter iso code
    destination = "genc3c", 
    #based on english language names
    origin = "country.name")
  
  #add value to a list
  iso_list_post_country <- append(iso_list_post_country,post_country_iso)
}
Some values were not matched unambiguously: Abkhazia*, Cura�ao, Jerusalem*, South Ossetia*
###create new columns
lowy_data_iso$send_country_iso = iso_list_send_country
lowy_data_iso$send_country_iso_num = iso_list_send_country_num

lowy_data_iso$post_country_iso = iso_list_post_country
lowy_data_iso$post_country_iso_num = iso_list_post_country_num

Click arrows to see all iso columns in data frame

###preview data frame
head(lowy_data_iso,3)

2.3 Geo-coding

Back to index
This section requires a google api key; you can set one up here through the google cloud platform

register_google(key = "")

Code to run the geocoding for each country column (send_country post_country), city post_city + post country (to avoid finding wrong city)

Part 1 cities and countries
Back to index
Generate geocoding queries:

###========Preparation

###concatenate post city and post country columns
lowy_data_iso$post_city_country = paste(
  lowy_data_iso$post_city,
  lowy_data_iso$post_country,
  sep=", ")

###geo-c - concatenated city and country names
geo_city_country <- distinct(lowy_data_iso,post_city_country)
geo_city_country_df <- as.data.frame(geo_city_country)
#geo_city_country_df <- head(geo_city_country_df,4) #for sample work only

###geo-code - sending countries
geo_send_country <- distinct(lowy_data_iso,send_country)
geo_send_country_df <- as.data.frame(geo_send_country)

Cities hosting diplomatic outposts geocoded


###========POST CITIES
#if local file tag is set to TRUE, google api will not be used
if(use_local_files) {
  #print("Local file flag set to TRUE, using local files")
  geo_city_country_df_dl <- read.csv("./Data/GoogleAPI/Geo_City_Country_DL.csv")
} else {
  #print("Local file flag set to FALSE, calling google now!")
  geo_city_country_df_dl <- mutate_geocode(geo_city_country_df, post_city_country)
  #Save .csv file
  write.csv(geo_city_country_df_dl,"./Data/GoogleAPI/Geo_City_Country_DL.csv", row.names = FALSE)
}
head(geo_city_country_df_dl,3)

Sending countries geocoded


###========SEND COUNTRIES
#if local file tag is set to TRUE, google api will not be used
if(use_local_files) {
  #print("Local file flag set to TRUE, using local files")
  geo_send_country_df_dl <- read.csv("./Data/GoogleAPI/Geo_Send_Country_DL.csv")
} else {
  #print("Local file flag set to FALSE, calling google now!")
  geo_city_country_df_dl <- mutate_geocode(geo_city_country_df, post_city_country)
  #Save .csv file
  write.csv(geo_send_country_df_dl,"./Data/GoogleAPI/Geo_Send_Country_DL.csv", row.names = FALSE)
}

head(geo_send_country_df_dl,3)

Data frame with all countries and cities geocoded (first 50 records)

###========JOIN DATA TO MASTER DATA FRAME
lowy_data_geo <- lowy_data_iso

#Join send lon/lat
lowy_data_geo <- merge(lowy_data_geo,geo_send_country_df_dl, by = "send_country")

lowy_data_geo <- lowy_data_geo %>% rename(
  send_lon =lon,
  send_lat = lat)

#Join post city lon/lat
lowy_data_geo <- merge(lowy_data_geo,geo_city_country_df_dl, by = "post_city_country")

lowy_data_geo <- lowy_data_geo %>% rename(
  post_city_lon =lon,
  post_city_lat = lat)

head(lowy_data_geo,50)

Part 2 exact embassy locations
Back to index
Code to run the geocoding for exact embassy locations. Using combination of send_country+post_country+“Embassy”+ post_city

###master dataframe with all data
lowy_data_geo_master <- lowy_data_geo

###Exact geocoding query
lowy_data_geo_master$full_geo_query = paste(
  lowy_data_geo_master$send_country,
  lowy_data_geo_master$post_title,
  lowy_data_geo_master$post_city_country,
  sep=", ")

###RUN GEOCODING
if(use_local_files) {
  print("Local file flag set to TRUE, using local files")
  lowy_data_geo_master <- read.csv("./Data/GoogleAPI/lowy_data_geo_master.csv")
} else {
  print("Local file flag set to FALSE, calling google now!")
  lowy_data_geo_master <- mutate_geocode(lowy_data_geo_master, full_geo_query)
  
  #rename columns
  lowy_data_geo_master <- lowy_data_geo_master %>% rename(
  exact_post_lon = lon,
  exact_post_lat = lat)
  
  #Save .csv file
  write.csv(geo_send_country_df_dl,"./Data/GoogleAPI/lowy_data_geo_master.csv", row.names = FALSE)
}
[1] "Local file flag set to TRUE, using local files"

First 50 records of all geocoded diplomatic outposts

head(lowy_data_geo_master,50)

3.0 Load and analyse Lowy Institute data

|| 0.0|READ ME || 1.0|EXTRACT || 2.0|TRANSFORM || 3.0|LOAD and ANALYSE || 4.0|ACLED data || 5.0|ACLED data ||

Purpose of section | visualise coordinates; and run clustering analysis
Inputs | lowy_data_geo_master (final output of part 2)
Outputs | maps and visuals


3.1 Load data and segmet usable part for analysis

Back to index

Load data

###Load data from section 2
lowy_data_complete <- read_csv("./Data/GoogleAPI/lowy_data_geo_master.csv")


Segmentation code

###Filter out unusable datapoints
#names(lowy_data_complete)

#Re-arrange columns and sort table
lowy_data_complete <-lowy_data_complete
lowy_data_complete = select(lowy_data_complete, 
                            #sending data
                            send_country_iso,
                            send_country,
                            send_lon,
                            send_lat,
                            #posting data
                            post_country_iso, 
                            post_country,
                            post_city_country,
                            post_city,
                            post_city_lon,
                            post_city_lat,
                            post_type,
                            post_title,
                            #other columns
                            everything())

lowy_data_complete <- lowy_data_complete[order(lowy_data_complete$send_country),]

#Filter out NA values (around 44 values)
geo_lowy_NA_values <- lowy_data_complete %>%
  dplyr::select(everything()) %>%
  dplyr::filter(is.na(exact_post_lon))

geo_lowy_no_NA <- lowy_data_complete %>%
  dplyr::select(everything()) %>%
  dplyr::filter(!is.na(exact_post_lon))


Summarise change to master GDI dataframe

#Preview table
report_full_length <-length(lowy_data_complete$send_country)
report_na_length <- length(geo_lowy_NA_values$send_country)
report_non_na_length <- length(geo_lowy_no_NA$send_country)

cat("There are,", report_full_length,"entries in the complete data frame.","\nFrom these,",report_na_length,"are NA/blank. (",report_na_length/report_full_length*100,"% )","\nIn total,",report_non_na_length,"entries have valid coordinates.","\nTotal coverage is:",report_non_na_length/report_full_length*100,"%")
There are, 7317 entries in the complete data frame. 
From these, 43 are NA/blank. ( 0.5876725 % ) 
In total, 7274 entries have valid coordinates. 
Total coverage is: 99.41233 %


all points visualised

geo_lowy_no_NA_vis <-
    st_as_sf(geo_lowy_no_NA,coords=c("exact_post_lon","exact_post_lat"),crs = 4326,remove = FALSE)

3.2 Geo-coding analysis function

Back to index
Function to take in data frame, and a filter to visualise and return filtered dataframe for further analysis f_exact_city_postings <- function(p_city_filter,p_df) expand to see function

###FUNCTION
f_exact_city_postings <- function(p_city_filter,p_df,p_colour = "red"){
  
  ###Subset of dataframe for chosen country
  sub_city_df <- p_df[p_df$post_city == p_city_filter,]

  #create spatial objects
  sub_city_sf <- 
    st_as_sf(sub_city_df,coords=c("post_city_lon","post_city_lat"),crs = 4326, remove = FALSE)

  sub_city_geo_sf <-
    st_as_sf(sub_city_df,coords=c("exact_post_lon","exact_post_lat"),crs = 4326,remove = FALSE)

  #map view options
  mapviewOptions(leafletWidth = 750,leafletHeight = 400)
  
  #City lat longs
  exact_map <- mapview(
      #spatial object
      sub_city_sf,
      legend = FALSE,
      layer.name = "City coordinates",
      lwd = 0.5,
      alpha.regions = 0.2,
      label = sub_city_df$post_city_country,
      cex = 10,
      colour = "black",
      col.regions = "orange",
      popup = leafpop::popupTable(subset(sub_city_sf, select = c(post_city_country)))
      )+ mapview(
      #spatial object
      sub_city_geo_sf,
      legend = FALSE,
      layer.name = "Diplomatic post locations",
      label = sub_city_df$full_geo_query,
      color = "black",
      alpha= 1,
      lwd = "0.5",
      alpha.regions = 0.5,
      col.regions = p_colour,
      cex = 6,
      popup = leafpop::popupTable(subset(sub_city_sf, select = c(send_country_iso,send_country,post_type,post_title,exact_post_lon,exact_post_lat)))
      )
  
  mapshot(exact_map, url = paste("/cloud/project/Data/MapFiles/",p_city_filter,"map.html",sep="_"))

  #return dl table,map
  return_list <- list(df = sub_city_df,map = exact_map)
  return(return_list)
}

3.3 Visualise

Back to index

Run function on array of a city of interest - Tehran

###run function
f_out <- f_exact_city_postings(p_city_filter = "Tehran",p_df = geo_lowy_no_NA)
f_out$map

3.4 Clustering

Use function output from 3.2 to run DBSCAN spatial clustering analysis

#============== function start
f_exact_city_cluster <- function(p_city_filter,p_df,p_eps,p_k = 4,p_outlier_remove = FALSE){
  
    #run original function
  f_out <- f_exact_city_postings(p_city_filter = p_city_filter,p_df = p_df)
  
  f_out$df
  
  #assign coordinates of filtered dataset to variable
  db_coordinates <- f_out$df[c("exact_post_lon","exact_post_lat")]*1000
  
  #boxplot
  db_coordinates_box <- boxplot(data=db_coordinates$exact_post_lon, x=db_coordinates$exact_post_lon, main="Boxplot for coordinate values", 
   xlab="Posts", ylab="Longitude values",notch = TRUE) #[RETURN 1]
  
  db_coordinates <- db_coordinates/1000
  db_coordinates_out <- db_coordinates_box$out/1000
  
  if(length(db_coordinates_out) == 0){
    zero_outliers <- FALSE
  }else{
    zero_outliers <- TRUE
  }
  
  #negation for filtering
  `%nin%` <- Negate("%in%")

  
  #if outlier parameter set to true, they will be removed, else can keep going as is
  if(p_outlier_remove & zero_outliers){
    
    #clustering objects
    db_coordinates_keep <- db_coordinates%>% filter(exact_post_lon %nin% db_coordinates_out)
    db_coordinates_out_cluster <- db_coordinates%>% filter(exact_post_lon %in% db_coordinates_out)
    
    #full dataframe without outliers
    f_out_df <- f_out$df %>% filter(exact_post_lon %nin% db_coordinates_out)
      
    #outlier dataframe
    f_out_outlier_df <- f_out$df %>% filter(exact_post_lon %in% db_coordinates_out)
    
    f_out_outlier_df_sf <- st_as_sf(f_out_outlier_df,
                                      coords=c("exact_post_lon","exact_post_lat"),
                                      crs = 4326,remove = FALSE)
  }else{
    db_coordinates_keep <- db_coordinates
    f_out_df <- f_out$df
  }
  
  #Indetify optimal eps 
  kNNdist_df <- dbscan::kNNdist(db_coordinates_keep, k = p_k)
  db_kNN_plot <- dbscan::kNNdistplot(db_coordinates_keep,k = p_k) #[RETURN 2]

  #now run the dbscan analysis
  db <- fpc::dbscan(db_coordinates_keep, eps = p_eps, MinPts = p_k)
  
  #now plot the results [RETURN 3]
  db_plot <- plot(db, db_coordinates_keep, main = "DBSCAN Output", frame = F)
  
  #you can add '2' to cluster to avoid 0 values as leaflet doesn't like to colour by 0 (zero) values and colour '1' is black
  db_coordinates_keep$cluster <- db$cluster+1 #+2 #not used
  dbscan_test_df <- db_coordinates_keep
  
  dbscan_test_df
  
  f_out_cluster <- f_exact_city_postings(p_city_filter = p_city_filter,p_df = f_out_df,p_colour = dbscan_test_df$cluster)
  
  if(p_outlier_remove & zero_outliers){
    f_out_cluster_map <- f_out_cluster$map + mapview(f_out_outlier_df_sf,
      legend = FALSE,
      layer.name = "Outliers",
      label = f_out_outlier_df_sf$full_geo_query,
      color = "yellow",
      alpha.regions = 1,
      col.regions = "yellow",
      cex = 4)
  }else{
    #no outliers to plot
    f_out_cluster_map <- f_out_cluster$map #[RETURN]
  }
  
  f_out_cluster$df #[RETURN]
  
   #mapshot(f_out_cluster_map, url = paste("/cloud/project/R_Notebook/Final/Data/MapFiles/",p_city_filter,"cluster_map.html",sep="_"))

  #return list of relevant elements
  return_list <- list(df_sub = f_out$df,map = f_out$map,box_plot = db_coordinates_box, k_plot = db_kNN_plot,f_cluster_map = f_out_cluster_map, f_cluster_df = f_out_cluster$df)
  
    return(return_list)
}
#============== function end

Run function

sample_anlaysis <- f_exact_city_cluster(p_city_filter = "Tehran",p_df = geo_lowy_no_NA,p_eps = 0.015,p_k =4,p_outlier_remove = TRUE)

sample_anlaysis$f_cluster_map

4.0 ACLED data

|| 0.0|READ ME || 1.0|EXTRACT || 2.0|TRANSFORM || 3.0|LOAD and ANALYSE || 4.0|ACLED data || 5.0|ACLED cases ||

Purpose of section | add data layer to output from function in 3.4
Inputs | f_exact_city_cluster() (outputs of function call in 3.4)
Outputs | maps and visuals


4.1 Set up ACLED API

Back to index

For details on how to use ACLED API please see documentation here

API structure for JSON output Full url call: https://api.acleddata.com/acled/read?terms=accept&country=Iran&year=2019&admin1=Tehran URL base: https://api.acleddata.com/acled/read?terms=accept terms=accept is required for output to be provided

after this, filters and criteria can be concatenated using &
In this case, country iso &iso=364, year &year=2019, admin1 admin1=Tehran for city

Define general purpose function

#define function, parameters same as those for clustering function in 3.4
f_acled_merge <- function(p_city_filter,p_df,p_eps,p_k,p_outlier_remove = TRUE,p_acled_year = FALSE,p_acled_city = TRUE){
  
  #function from 3.4
  f_exact_output <- f_exact_city_cluster(p_city_filter = p_city_filter,
                       p_df = p_df,p_eps = p_eps,
                       p_k = p_k,
                       p_outlier_remove = p_outlier_remove)
  
  #relevant outputs from f_exact_city_cluster() are: dataset, cluster analysis stats and map to add to.

  ###ISO
  distinct_country_iso <- distinct(f_exact_output$f_cluster_df,post_country_iso_num)
  distinct_country_iso_call <- paste("&iso=",distinct_country_iso,sep="")
  ###YEAR
  if(p_acled_year){
    acled_year <- p_acled_year 
    acled_year_call <- paste("&year=",acled_year,sep="")
  }else{
    acled_year_call <- ""
  }
  ###CITY
  if(p_acled_city){
    city_call <- paste("&admin1=",p_city_filter,sep="")
  }else{
    city_call <- ""
  }
  
  ###API
  api_base <-"https://api.acleddata.com/acled/read?terms=accept"
  
  url_call <- paste(api_base,distinct_country_iso_call,acled_year_call,city_call,sep="")
  acled_data <- getURL(url_call)
  acled_df <- fromJSON(acled_data)$data #get data right away
  acled_df <- type_convert(acled_df)
  acled_df$point_size <- acled_df$fatalities+6
  acled_df_sf <- st_as_sf(acled_df,
                                      coords=c("longitude","latitude"),
                                      crs = 4326,remove = FALSE)
  
  
  f_acled_map <- f_exact_output$f_cluster_map + mapview(acled_df_sf,
      legend = TRUE,
      layer.name = "Incidents (by type)",
      label = acled_df_sf$event_type,
      lwd = 0.5,
      color = "red",
      alpha.regions = 0.5,
      #col.regions = acled_df_sf$event_type,
      col.regions = "white",
      #zcol = "event_type",
      cex = "point_size",
      popup = leafpop::popupTable(subset(acled_df_sf, select = c(event_date,event_type,sub_event_type,fatalities,source_scale,source)))
      )
    
  mapshot(f_acled_map, url = paste("/cloud/project/Data/MapFiles/",p_city_filter,"ACLED_map.html",sep="_"))
  
  #return list of relevant elements
  return_list <- list(f_data = f_exact_output$f_cluster_df,
                      f_map = f_acled_map,
                      f_k_plot = f_exact_output$k_plot,
                      f_acled_data = acled_df_sf,
                      f_boxplot = f_exact_output$box_plot)
  
    return(return_list)
}


Run function and see completed function results


5.0 ACLED cases

|| 0.0|READ ME || 1.0|EXTRACT || 2.0|TRANSFORM || 3.0|LOAD and ANALYSE || 4.0|ACLED data || 5.0|ACLED cases ||

Purpose of section: run two case studies using function from section 4
Inputs: f_acled_merge()
Outputs: maps and plots to assess clustering


In this section, different cities are examined for qualitative assessment in the final assessment. Deselect outlier layer and city layer and focus in on ‘exact embassy locations’ layer.

5.1 | Case 1 Cairo

Back to index

5.2 | Case 2 Ankara

Back to index


REFERENCE CODE FUNCTIONS

Back to index

Calculate global population and global economy proportions for this dataset.

#ECONOMY
economy_summary <- distinct(lowy_meta_df,send_country,meta_gdp_usd_billions)
economy_summary_sum <- sum(economy_summary$meta_gdp_usd_billions)
economy_2019 <- 85.91*1000
economy_coverage <- economy_summary_sum/economy_2019

###POPULATION
people_summary <- distinct(lowy_meta_df,send_country,meta_population_millions)
people_summary_sum <- sum(people_summary$meta_population_millions)
people_2019 <- 7.594*1000
people_coverage <- people_summary_sum/people_2019

cat("Coverage of the world economy is:",economy_coverage*100,"%","\nCoverage of global population is:",people_coverage*100,"%")
Coverage of the world economy is: 91.78675 % 
Coverage of global population is: 74.7577 %
#economy from: https://data.worldbank.org/indicator/ny.gdp.mktp.cd
#people from: https://data.worldbank.org/indicator/sp.pop.totl 
#85.91 GDP in current dollars in thousands of billions for 2018
#7.594thousand million population in the world in 2018
LS0tCnRpdGxlOiAiQW5hbHlzaW5nIHRoZSBmcm9udGxpbmUgb2YgZGlwbG9tYWN5OiBhY2NvbXBhbnlpbmcgY29kZSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgY29kZV9mb2xkaW5nOiBoaWRlCiAgICB0aGVtZTogdW5pdGVkCi0tLQoKW0dpdGh1YiByZXBvc2l0b3J5IGxpbmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9hbnRvbmlvc2ZpYWxhL1VDTF9DQVNBX0dJUyl7dGFyZ2V0PSJfYmxhbmsifQoKIyMjIDAuMCBSZWFkIG1lIHsjaF8wLjBfcmVhZG1lfQp8fCBbKiowLjB8UkVBRCBNRSoqXSgjaF8wLjBfcmVhZG1lKSB8fCBbMS4wfEVYVFJBQ1RdKCNoXzEuMF9leHRyYWN0KSB8fCBbMi4wfFRSQU5TRk9STV0oI2hfMi4wX3RyYW5zZm9ybSkgfHwgWzMuMHxMT0FEIGFuZCBBTkFMWVNFXSgjaF8zLjBfYW5hbHlzZSkgfHwgWzQuMHxBQ0xFRCBkYXRhXSgjaF80LjBfYWNsZWQpIHx8IFs1LjB8QUNMRUQgZGF0YV0oI2hfNS4wX2Nhc2VzKSB8fDxicj4KCj4gKipQdXJwb3NlIG9mIHNlY3Rpb24qKnwgYnJpZWYgb3V0bGluZSBvZiBkb2N1bWVudCwgaXRzIGNvbnRlbnQgYW5kIHByZXJlcXVpc2l0ZXM8YnI+Cj4gKipJbnB1dHMqKiB8IG5vbmU8YnI+Cj4gKipPdXRwdXRzKiogfCBub25lCgoqKioKCiMjIyMgMC4xIE91dGxpbmUgb2YgZG9jdW1lbnQgeyNoXzAuMV9vdXRsaW5lfQpUaGlzIG5vdGVib29rIG91dGxpbmVzIHRoZSBwcm9jZXNzIGFuZCBjb2RlIG91dHB1dHMgYXNzb2NpYXRlZCB3aXRoIHRoZSBwcm9qZWN0IEFuYWx5c2luZyB0aGUgZnJvbnRsaW5lcyBvZiBkaXBsb21hY3k6IHRoZSBjYXNlIGZvciB1bmxlYXNoaW5nIEdJUyAoQSBwcm9vZiBvZiBjb25jZXB0IGFsZ29yaXRobSBzdXBwbGVtZW50ZWQgd2l0aCBpfCBpbmRpY2F0aXZlIGNsdXN0ZXJpbmcgYW5hbHlzaXMgb2YgZGlwbG9tYXRpYyBwb3N0IGxvY2F0aW9ucyBhbmQgaWl8IGNvbmZsaWN0IGV2ZW50cyB3aXRoaW4gY2l0aWVzKQoKKipGaW5hbCBzdWJtaXNzaW9uKiogY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vYW50b25pb3NmaWFsYS9VQ0xfQ0FTQV9HSVMvYmxvYi9tYXN0ZXIvUl9Ob3RlYm9vay9GaW5hbC9TdWJtaXR0ZWQlMjBwYXBlci9HSVNfV3JpdGVVcF8yMDIwMDExMl92MS4wX2xvd2VyX3NpemUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IGAxMy4wMS4yMDIwYDxicj4KCgoqKioKCgojIyMjIDAuMiBEb2N1bWVudCBpbmRleCB7I2hfMC4yX2luZGV4fQoKMC4gW1JlYWQgbWVdKCNoXzAuMF9yZWFkbWUpCiAgICArIHx8IFswLjEtT3V0bGluZV0oI2hfMC4xX291dGxpbmUpIHx8IFswLjItSW5kZXhdKCNoXzAuMl9pbmRleCkgfHwgWzAuMy1TZXR1cF0oI2hfMC4zX3ByZXJlcSkgfHwKMS4gW0V4dHJhY3RdKCNoXzAuMF9yZWFkbWUpCiAgICArIHx8IFsxLjEtTG9hZF0oI2hfMS4xX2xvYWQpIHx8IFsxLjItVGlkeV0oI2hfMS4yX3RpZHkpIHx8IFsxLjMtU3VtbWFyaXNlXSgjaF8xLjNfc3VtbWFyaXNlKSB8fAoyLiBbVHJhbnNmb3JtXSgjaF8yLjBfdHJhbnNmb3JtKQogICAgKyB8fCBbMi4xLVJlbG9hZCBkYXRhXSgjaF8yLjFfbG9hZCkgfHwgWzIuMi1JU08gZW5jb2RpbmddKCNoXzIuMl9pc28pIHx8IFsyLjMtR2VvY29kaW5nXSgjaF8yLjNfZ2VvY29kZSkgfHwgCjMuIFtMb2FkIGFuZCBhbmFseXNlXSgjaF8zLjBfYW5hbHlzZSkKICAgICsgfHwgWzMuMS1Mb2FkIGFuZCBzZWdtZW50IGRhdGEgZnJhbWVdKCNoXzMuMV9hbmFseXNpc19wcmVwKSB8fCBbMy4yLURlZmluZSB2aXN1YWxpc2F0aW9uIGZ1bmN0aW9uXSgjaF8zLjJfZ2VvX2Z1bmN0aW9uKSB8fCBbMy4zLVZpc3VhbGlzZSAxLjBdKCNoXzMuM19nZW9fdmlzKSB8fCBbMy40LVZpc3VhbGlzZSAyLjAsIGNsdXN0ZXJpbmcgYW5kIG91dGxpZXIgcmVtb3ZhbF0oI2hfMy40X2NsdXN0ZXIpIHx8CjQuIFtBQ0xFRCBkYXRhXSgjaF80LjBfYWNsZWQpCiAgICArIHx8IFs0LjEtQUNMRUQgYXBpIGZ1bmN0aW9uXSgjaF80LjFfYWNsZWRfYXBpKSB8fCBbNC4yLUFDTEVEIGZ1bmN0aW9uIG91dHB1dF0oI2hfNC4yX2FjbGVkX291dCkKNS4gW0Nhc2Ugc3R1ZGllc10oI2hfNS4wX2Nhc2VzKQogICAgKyB8fCBbNS4xLUNhc2UgMSAtIENhaXJvXSgjaF81LjFfY2FzZSkgfHwgWzUuMi1DYXNlIDIgLSBBbmthcmFdKCNoXzUuMl9jYXNlKQoKKiAgW1JlZmVyZW5jZSBjb2RlIHNlY3Rpb25dKCNyZWZfY29kZSkKCk92ZXJ2aWV3IG9mIHRoZSBzdGVwcyBvZiB0aGlzIHdvcmtmbG93IGluIHRoZSBjaGFydCBiZWxvdyAoQXBwZW5kaXggRy4yIGluIHRoZSBzdWJtaXR0ZWQgd29yayk8YnI+CgojIyMjIEFwcGVuZGl4IEcuMiB8IGNvbXBsZXRlIHByb2Nlc3MgZmxvdwohW10oL2Nsb3VkL3Byb2plY3QvUl9Ob3RlYm9vay9GaW5hbC9JbWFnZXMvQ0FTQV9HSVNfZGlwbG9tYWN5X2RldGFpbGVkLnBuZykKCioqKgoKIyMjIyAwLjMgUHJlLXJlcXVpc2l0ZXMgYW5kIHNldCB1cCB7I2hfMC4zX3ByZXJlcX0KCkluIG9yZGVyIGZvciB0aGUgY29kZSBiZWxvdyB0byB3b3JrLCBwbGVhc2UgZW5zdXJlIHlvdSBoYXZlIGluc3RhbGxlZCBhbmQgbG9hZGVkIHRoZSByZWxldmFudCBwYWNrYWdlcy4KCmBgYHtyIGVjaG8gPSBUUlVFLCByZXN1bHRzID0gJ2hpZGUnfQojSW5zdGFsbGluZyBwYWNrYWdlcyAodW5jb21tZW50IGFzIHJlcXVpcmVkKQojaW5zdGFsbC5wYWNrYWdlcyhjKCJjb3VudHJ5Y29kZSIsInRpZHl2ZXJzZSIsInNmIiwibWFwdmlldyIsImdnbWFwIiwiUkN1cmwiLCJqc29ubGl0ZSIsIlJDdXJsIiwianNvbmxpdGUiKSkKCmxpYnJhcnkoY291bnRyeWNvZGUpICNpc28gdGFnZ2luZyBvZiBjb3VudHJpZXMKbGlicmFyeSh0aWR5dmVyc2UpICNudW1iZXIgb2YgcGFja2FnZXMgZm9yIGRhdGEgbWFuaXB1bGF0aW9uLCBzcGVjaWZpY2FsbHkgd2FudCByZWFkciwgZHBseXIsIHRpYmJsZQpsaWJyYXJ5KHNmKSAjY3JlYXRpb24gb2Ygc3BhdGlhbCBvYmplY3RzCmxpYnJhcnkobWFwdmlldykgI21hcHBpbmcgcGFja2FnZQpsaWJyYXJ5KGdnbWFwKSAjZm9yIGdvb2dsZSBhcGkgYWNjZXNzIApsaWJyYXJ5KFJDdXJsKSAjdG8gZG93bmxvYWQgQUNMRUQgY29udGVudApsaWJyYXJ5KGpzb25saXRlKSAjdG8gcHJvY2VzcyBBQ0xFRCBkb3dubG9hZGVkIGluZm8KYGBgCjxicj4KU2V0dGluZyB0byB1c2UgbG9jYWwgZmlsZXMgb3IgcmVmcmVzaCBnZW9jb2RpbmcgdmlhIEFQSSBjYWxscy4KYGBge3J9CiNTZXQgdGhpcyB0byBUUlVFIG9yIEZBTFNFIHRvIGRldGVybWluZSB3aGV0aGVyIHRvIHVzZSBsb2NhbCwgcHJlLWxvYWRlZCBmaWxlcyBvciByZWZyZXNoIHZpYSByZWxldmFudCBBUEksIGkuZS4gKEdvb2dsZSkgY29ubmVjdGlvbnMuCnVzZV9sb2NhbF9maWxlcyA8LSBUUlVFCmBgYAoKKioqCgojIyMgMS4wIEV4dHJhY3QgR0RJIGRhdGFzZXQgeyNoXzEuMF9leHRyYWN0fQp8fCBbMC4wfFJFQUQgTUVdKCNoXzAuMF9yZWFkbWUpIHx8IFsqKjEuMHxFWFRSQUNUKipdKCNoXzEuMF9leHRyYWN0KSB8fCBbMi4wfFRSQU5TRk9STV0oI2hfMi4wX3RyYW5zZm9ybSkgfHwgWzMuMHxMT0FEIGFuZCBBTkFMWVNFXSgjaF8zLjBfYW5hbHlzZSkgfHwgWzQuMHxBQ0xFRCBkYXRhXSgjaF80LjBfYWNsZWQpIHx8IFs1LjB8QUNMRUQgY2FzZXNdKCNoXzUuMF9jYXNlcykgfHw8YnI+Cgo+ICoqUHVycG9zZSBvZiBzZWN0aW9uKiogfCBsb2FkIEdESSBkYXRhc2V0IGZyb20gTG93eSBJbnN0aXR1dGUgc2l0ZSBhbmQgc3VtbWFyaXNlIDxicj4KPiAqKklucHV0cyoqIHwgR0RJIGRhdGFzZXQgYXZhaWxhYmxlIFtoZXJlXShodHRwczovL2dsb2JhbGRpcGxvbWFjeWluZGV4Lmxvd3lpbnN0aXR1dGUub3JnL2Fib3V0Lmh0bWwjKXt0YXJnZXQ9Il9ibGFuayJ9IDxicj4KPiAqKk91dHB1dHMqKnwgUmVuYW1lZCBhbmQgc2VwYXJhdGVkIC5jc3YgZmlsZXMKCl8qKk5vdGUqKiB8IGRvd25sb2FkaW5nIGZyb20gdGhlIExvd3kgSW5zdGl0dXRlIHNpdGUsIHRoZXJlIG1heSBiZSBpbmNvbXBhdGliaWxpdHkgd2l0aCBkb3dubG9hZHMgaW4gU2FmYXJpLCBjaHJvbWUgaXMgcmVjb21tZW5kZWQsIGhhdmUgbm90IHRlc3RlZCBvdGhlciBicm93c2Vycy5fCgoqKioKCiMjIyMgMS4xIExvYWQgLmNzdiBmaWxlIGFuZCBjaGVjayBkYXRhIHR5cGVzIHsjaF8xLjFfbG9hZH0KW0JhY2sgdG8gaW5kZXhdKCNoXzAuMl9pbmRleCkKCkxvYWQgMjAxOSBHREkgZGF0YXNldApgYGB7ciByZXN1bHRzID0gJ2hpZGUnfQojIyNvcGVuIGxhdGVzdCBjc3YgZG9jdW1lbnQgYmFzZWQgb24gcmVsYXRpdmUgcGF0aApsb3d5X3Jhd19kYXRhIDwtIHJlYWQuY3N2KCIuL0RhdGEvUmF3X2RhdGEvMjAxOV9Mb3d5SW5zdGl0dXRlR2xvYmFsRGlwbG9tYWN5SW5kZXhfRnVsbERhdGFzZXQuY3N2IikKYGBgCgojIyMjIDEuMiBUaWR5IHVwIGRhdGFzZXQgeyNoXzEuMl90aWR5fQpbQmFjayB0byBpbmRleF0oI2hfMC4yX2luZGV4KTxicj4KKipDaGVjayBmb3IqKnwgZmFjdG9yczsgcmVuYW1lIGNvbHVtbnM7IHNlcGVyYXRlIG91dCByZWxldmFudCBjb2x1bW5zIDxicj4KKipPdXRwdXQqKnwgPGJyPgoKYS4gcmVuYW1lZCBkYXRhIGZyYW1lIGBsb3d5X3JlbmFtZWRgCmIuIG90aGVyIG1ldGEgZGF0YSBgbG93eV9tZXRhYApjLiByYW5raW5nIGRhdGEgYGxvd3lfcmFua2AKZC4gKipwb3N0aW5nIGRhdGEqKiBgbG93eV9wb3N0aW5nYCAqKjw9PT0qKiB0aGlzIGlzIHRoZSByZWxldmFudCBvbmUgZm9yIHRoZSByZXN0IG9mIHRoZSB3b3JrCgpUaWR5IGRhdGFzZXQsIHNlZSBwcmV2aWV3IGZvciB0YWJsZS4KYGBge3J9CiMjI2NoZWNrIGZvciBmYWN0b3IgY29sdW1ucyBhbmQgY2hhbmdlIHRoZW0gdG8gY2hhcmFjdGVyIGNvbHVtbnMKI21vcmUgb2YgYSBwcmUtZW1wdGl2ZSBtZWFzdXJlCmkgPC0gc2FwcGx5KGxvd3lfcmF3X2RhdGEsIGlzLmZhY3RvcikKbG93eV9yYXdfZGF0YVtpXSA8LSBsYXBwbHkobG93eV9yYXdfZGF0YVtpXSwgYXMuY2hhcmFjdGVyKQoKIyMjY29sdW1uIHJlbmFtZQojbmV3IGRhdGEgZnJhbWUgKGEpCmxvd3lfcmVuYW1lZCA8LSBsb3d5X3Jhd19kYXRhCgojcmVuYW1lIGNvbHVtbnMKbG93eV9yZW5hbWVkIDwtIGxvd3lfcmVuYW1lZCAlPiUgZHBseXI6OnJlbmFtZSgKICBzZW5kX2NvdW50cnk9Q09VTlRSWSwKICAjbWV0YWRhdGEKICBtZXRhX2dkcF91c2RfYmlsbGlvbnMgPSBHRFAuLkIuLlVTRC4sCiAgbWV0YV9wb3B1bGF0aW9uX21pbGxpb25zID0gUE9QVUxBVElPTi4uTS4sCiAgI3JhbmtpbmcgY29sdW1ucwogIHJhbmtfZzIwID0gRzIwLlJBTkssCiAgcmFua19PRUNEID0gT0VDRC5SQU5LLAogIHJhbmtfYXNpYSA9IEFTSUEuUkFOSywKICByYW5rX292ZXJhbGwgPSBPVkVSQUxMLlJBTkssCiAgI3Bvc3QgY291bnRyeSBpbmZvCiAgcG9zdF9jaXR5ID0gUE9TVC5DSVRZLAogIHBvc3RfY291bnRyeSA9IFBPU1QuQ09VTlRSWSwKICBwb3N0X3R5cGUgPSBUWVBFLk9GLlBPU1QsCiAgcG9zdF90aXRsZSA9IFBPU1QuVFlQRS5USVRMRQogICkKCiNkYXRhIGZyYW1lIHdpdGggYWxsIG1ldGFkYXRhIChHRFAgYW5kIHBvcHVsYXRpb24pIChiKQpsb3d5X21ldGEgPC0gbG93eV9yZW5hbWVkWywgYygic2VuZF9jb3VudHJ5IiwibWV0YV9wb3B1bGF0aW9uX21pbGxpb25zIiwibWV0YV9nZHBfdXNkX2JpbGxpb25zIildCgojZGF0YSBmcmFtZSB3aXRoIGFsbCByYW5raW5ncyAoRzIwLCBPRUNELCBBc2lhIGFuZCBvdmVyYWxsKSAoYykKbG93eV9yYW5rIDwtIGxvd3lfcmVuYW1lZFssYygic2VuZF9jb3VudHJ5IiwicmFua19nMjAiLCJyYW5rX09FQ0QiLCJyYW5rX2FzaWEiLCJyYW5rX292ZXJhbGwiKV0KCiNkYXRhIGZyYW1lIHdpdGggaW5mb3JtYXRpb24gb24gc2VuZGluZyBjb3VudHJ5LCBwb3N0IGNpdHksIGNvdW50cnkgYXMgd2VsbCBhcyBwb3N0IHR5cGUgYW5kIHRpdGxlIChkKQpsb3d5X3Bvc3RpbmcgPC0gbG93eV9yZW5hbWVkWyxjKCJzZW5kX2NvdW50cnkiLCJwb3N0X2NpdHkiLCJwb3N0X2NvdW50cnkiLCJwb3N0X3R5cGUiLCJwb3N0X3RpdGxlIildCgojIyNzYXZlIGRhdGEgZnJhbWVzIGFzIC5jc3YgZmlsZXMgcmVhZHkgZm9yIHBhcnQgMgp3cml0ZS5jc3YobG93eV9wb3N0aW5nLCIuL0RhdGEvUHJvY2Vzc2luZy9QYXJ0XzFfT3V0X0xvd3lfUG9zdGluZy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGxvd3lfcmFuaywiLi9EYXRhL1Byb2Nlc3NpbmcvUGFydF8xX091dF9Mb3d5X1JhbmsuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihsb3d5X21ldGEsIi4vRGF0YS9Qcm9jZXNzaW5nL1BhcnRfMV9PdXRfTG93eV9NZXRhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKI3F1aWNrIHByZXZpZXcgb2YgZGF0YSBmcmFtZSB0byB3b3JrIG9uCmhlYWQobG93eV9wb3N0aW5nLDMpCmBgYAoKIyMjIyAxLjMgUmF3IGtleSBpbmZvcm1hdGlvbiB7I2hfMS4zX3N1bW1hcmlzZX0KW0JhY2sgdG8gaW5kZXhdKCNoXzAuMl9pbmRleCkKCmBgYHtyfQojTGVuZ3RoIG9mIGRhdGFmcmFtZToKY2F0KCJMb3d5IEluc3RpdHV0ZSdzIEdESSBkYXRhc2V0IGhhcyIsbGVuZ3RoKGxvd3lfcG9zdGluZ1tbMV1dKSwiZW50cmllcy4iKQoKIyMjcmVwb3J0IGFsbCB1bmlxdWUgdmFsdWVzL2VudHJpZXMKI1N1bW1hcmlzZSBhbGwgdW5pcXVlIHZhbHVlcyBpbiBjcnVkZSBwcmludCBvdXQKZm9yKGNvbHVtbiBpbiBjb2xuYW1lcyhsb3d5X3Bvc3RpbmcpKXsKICBjYXQoY29sdW1uLCJoYXMiLGxlbmd0aChhcHBseShsb3d5X3Bvc3RpbmdbY29sdW1uXSwyLHVuaXF1ZSkpLCJ1bmlxdWUgdmFsdWVzXG4iKQp9CgojIyNzdW1tYXJ5IHN0YXRzCmxvd3lfdHlwZV90YWJsZSA8LSBmdGFibGUodGFibGUobG93eV9wb3N0aW5nJHNlbmRfY291bnRyeSxsb3d5X3Bvc3RpbmckcG9zdF90eXBlKSkKCiMjI3N1bW1hcmlzZSBjb3VudCBieSBzZW5kIGNvdW50cnkKbG93eV9jb3VudF9wZXJfc2VuZF9jb3VudHJ5IDwtIGxvd3lfcG9zdGluZyAlPiUgCiAgZ3JvdXBfYnkoc2VuZF9jb3VudHJ5KSAlPiUgCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKGNvdW50KSkKCnBlcl9jb3VudHJ5X2NvdW50IDwtIGxvd3lfY291bnRfcGVyX3NlbmRfY291bnRyeSRjb3VudApwYXIoZmFtaWx5ID0gIlRpbWVzIE5ldyBSb21hbiIpCnBlcl9jb3VudHJ5X2JveCA8LSBib3hwbG90KGRhdGE9cGVyX2NvdW50cnlfY291bnQsIHg9cGVyX2NvdW50cnlfY291bnQsIG1haW49IkRpcGxvbWF0aWMgcG9zdCBjb3VudCBieSBzZW5kaW5nIGNvdW50cnkiLCAKICAgeGxhYj0iQWxsIHBvc3QgdHlwZXMiLCB5bGFiPSJDb3VudCBvZiBwb3N0cyIsbm90Y2ggPSBUUlVFKQoKY2F0KCJQZXIgY291bnRyeSBjb3VudCBzdW1tYXJ5IiwKICAgICJcbk1pbiB2YWx1ZToiLHBlcl9jb3VudHJ5X2JveCRzdGF0c1sxLDFdLAogICAgIlxuMjV0aCBwZXJjZW50aWxlOiIscGVyX2NvdW50cnlfYm94JHN0YXRzWzIsMV0sCiAgICAiXG5NZWRpYW4gdmFsdWU6IixwZXJfY291bnRyeV9ib3gkc3RhdHNbMywxXSwKICAgICJcbjc1dGggcGVyY2VudGlsZToiLHBlcl9jb3VudHJ5X2JveCRzdGF0c1s0LDFdLAogICAgIlxuTWF4IHZhbHVlOiIscGVyX2NvdW50cnlfYm94JHN0YXRzWzUsMV0KICAgICkKIyMjc3VtbWFyaXNlIGNvdW50IGJ5IHBvc3QgY2l0eQpsb3d5X2NvdW50X3Blcl9wb3N0X2NpdHkgPC0gbG93eV9wb3N0aW5nICU+JSAKICBncm91cF9ieShwb3N0X2NpdHkpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKcGVyX3Bvc3RfY2l0eV9jb3VudCA8LSBsb3d5X2NvdW50X3Blcl9wb3N0X2NpdHkkY291bnQKCnBlcl9wb3N0X2NpdHlfYm94IDwtIGJveHBsb3QoZGF0YT1wZXJfcG9zdF9jaXR5X2NvdW50LCB4PXBlcl9wb3N0X2NpdHlfY291bnQsIG1haW49IkRpcGxvbWF0aWMgcG9zdCBjb3VudCBieSBwb3N0IGNpdHkgIiwgCiAgIHhsYWI9IkFsbCBwb3N0IHR5cGVzIiwgeWxhYj0iQ291bnQgb2YgcG9zdHMiLG5vdGNoID0gVFJVRSkKY2F0KCJQZXIgcG9zdCBjaXR5IGNvdW50IHN1bW1hcnkiLAogICAgIlxuTWluIHZhbHVlOiIscGVyX3Bvc3RfY2l0eV9ib3gkc3RhdHNbMSwxXSwKICAgICJcbjI1dGggcGVyY2VudGlsZToiLHBlcl9wb3N0X2NpdHlfYm94JHN0YXRzWzIsMV0sCiAgICAiXG5NZWRpYW4gdmFsdWU6IixwZXJfcG9zdF9jaXR5X2JveCRzdGF0c1szLDFdLAogICAgIlxuNzV0aCBwZXJjZW50aWxlOiIscGVyX3Bvc3RfY2l0eV9ib3gkc3RhdHNbNCwxXSwKICAgICJcbk1heCB2YWx1ZToiLHBlcl9wb3N0X2NpdHlfYm94JHN0YXRzWzUsMV0KICAgICkKIyMjc3VtbWFyaXNlIGNvdW50IGJ5IHBvc3QgY291bnRyeQpsb3d5X2NvdW50X3Blcl9wb3N0X2NvdW50cnkgPC0gbG93eV9wb3N0aW5nICU+JSAKICBncm91cF9ieShwb3N0X2NvdW50cnkpICU+JSAKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JSAKICBhcnJhbmdlKGRlc2MoY291bnQpKQoKcGVyX3Bvc3RfY291bnRyeV9jb3VudCA8LSBsb3d5X2NvdW50X3Blcl9wb3N0X2NvdW50cnkkY291bnQKCnBlcl9wb3N0X2NvdW50cnlfYm94IDwtIGJveHBsb3QoZGF0YT1wZXJfcG9zdF9jb3VudHJ5X2NvdW50LCB4PXBlcl9wb3N0X2NvdW50cnlfY291bnQsIG1haW49IkRpcGxvbWF0aWMgcG9zdCBjb3VudCBieSBwb3N0IGNvdW50cnkiLCAKICAgeGxhYj0iQWxsIHBvc3QgdHlwZXMiLCB5bGFiPSJDb3VudCBvZiBwb3N0cyIsbm90Y2ggPSBUUlVFKQpjYXQoIlBlciBwb3N0IGNvdW50cnkgY291bnQgc3VtbWFyeSIsCiAgICAiXG5NaW4gdmFsdWU6IixwZXJfcG9zdF9jb3VudHJ5X2JveCRzdGF0c1sxLDFdLAogICAgIlxuMjV0aCBwZXJjZW50aWxlOiIscGVyX3Bvc3RfY291bnRyeV9ib3gkc3RhdHNbMiwxXSwKICAgICJcbk1lZGlhbiB2YWx1ZToiLHBlcl9wb3N0X2NvdW50cnlfYm94JHN0YXRzWzMsMV0sCiAgICAiXG43NXRoIHBlcmNlbnRpbGU6IixwZXJfcG9zdF9jb3VudHJ5X2JveCRzdGF0c1s0LDFdLAogICAgIlxuTWF4IHZhbHVlOiIscGVyX3Bvc3RfY291bnRyeV9ib3gkc3RhdHNbNSwxXQogICAgKQpgYGAKCmBgYHtyIGluY2x1ZGU9RkFMU0V9CiMjI3VubG9hZCBkYXRhIGZyYW1lcyBmcm9tIGdsb2JhbCBlbnZpcm9ubWVudCB0aGF0IGFyZSBub3QgbmVlZGVkLgpscygpCnJtKGxpc3QgPSBscygpW2dyZXBsKCJib3giLCBscygpKV0pICNyZW1vdmUgYWxsIGJveHBsb3RzCnJtKGxpc3QgPSBscygpW2dyZXBsKCJwZXIiLCBscygpKV0pICNwZXIgY291bnRpbmcgZGF0YWZyYW1lcwojcm0obGlzdCA9IGxzKCkpICNwdXJnZSBhbGwgZWxzZSAoY29tbWVudC91bmNvbW1lbnQgYXMgbmVlZGVkKQpgYGAKCgoqKioKCiMjIyAyLjAgVHJhbnNmb3JtIEdESSBkYXRhIHsjaF8yLjBfdHJhbnNmb3JtfQp8fCBbMC4wfFJFQUQgTUVdKCNoXzAuMF9yZWFkbWUpIHx8IFsxLjB8RVhUUkFDVF0oI2hfMS4wX2V4dHJhY3QpIHx8IFsqKjIuMHxUUkFOU0ZPUk0qKl0oI2hfMi4wX3RyYW5zZm9ybSkgfHwgWzMuMHxMT0FEIGFuZCBBTkFMWVNFXSgjaF8zLjBfYW5hbHlzZSkgfHwgWzQuMHxBQ0xFRCBkYXRhXSgjaF80LjBfYWNsZWQpIHx8IFs1LjB8QUNMRUQgZGF0YV0oI2hfNS4wX2Nhc2VzKSB8fDxicj4KCgo+ICoqUHVycG9zZSBvZiBzZWN0aW9uKiogfCBMb2FkLC5jc3YgZmlsZXM7IGFkZCBjb3VudHJ5IGlzbyBjb2RlcyB0byBtYWluIGRhdGEgZnJhbWU7IGFuZCBnZW9jb2RlIHNlbmQgY291bnRyeStwb3N0IGNpdHkrcG9zdCtjb3VudHJ5K2V4YWN0IGVtYmFzc3kgbG9jYXRpb24gd2l0aCBHb29nbGUgQVBJIDxicj4KPiAqKklucHV0cyoqIHwgYFBhcnRfMV9PdXRfTG93eV9Qb3N0aW5nLmNzdmAgZnJvbSBlbmQgb2YgcGFydCAxPGJyPgo+ICoqT3V0cHV0cyoqIHwgYGxvd3lfZGF0YV9pc29gIG1hc3RlciBnZW9jb2RlZCB0YWJsZSAKCioqKgoKIyMjIyAyLjEgUmUtbG9hZCB7I2hfMi4xX2xvYWR9CltCYWNrIHRvIGluZGV4XSgjaF8wLjJfaW5kZXgpCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9Cmxvd3lfZGF0YSA8LSByZWFkX2NzdigiLi9EYXRhL1Byb2Nlc3NpbmcvUGFydF8xX091dF9Mb3d5X1Bvc3RpbmcuY3N2IikKYGBgCgojIyMjIDIuMiBJU08gY29kZXMgeyNoXzIuMl9pc299CltCYWNrIHRvIGluZGV4XSgjaF8wLjJfaW5kZXgpCmBgYHtyfQojIyNTZXQgdXAKCiNpc28gZGF0YWZyYW1lCmxvd3lfZGF0YV9pc28gPC0gbG93eV9kYXRhCgojY291bnRyeV9pc28gbGlzdCBbY29sdW1uIDFdCmlzb19saXN0X3NlbmRfY291bnRyeSA8LSBjKCkKaXNvX2xpc3Rfc2VuZF9jb3VudHJ5X251bSA8LSBjKCkKI3Bvc3RfY291bnRyeV9pc28gbGlzdCBbY29sdW1uIDNdCmlzb19saXN0X3Bvc3RfY291bnRyeSA8LSBjKCkKaXNvX2xpc3RfcG9zdF9jb3VudHJ5X251bSA8LSBjKCkKCiMjI2VuZGluZyBjb3VudHJ5IGNvbHVtbiAoMSkgdGhyZWUgZGlnaXQgbnVtYmVycwpmb3IoY291bnRyeSBpbiBsb3d5X2RhdGFfaXNvWzFdKXsKICBjb3VudHJ5X2lzbyA8LSBjb3VudHJ5Y29kZSgKICAgICNjYWxsIHRoZSBlbnRyeSBvZiAnQ291bnRyeScgZm9yIHRoZSByb3dfbnVtYmVyIGN1cnJlbnRseSBiZWluZyB0YWtlbiBieSBmb3IgbG9vcAogICAgc291cmNldmFyID0gY291bnRyeSwgCiAgICAjdGhyZWUgbGV0dGVyIGlzbyBjb2RlCiAgICBkZXN0aW5hdGlvbiA9ICJpc28zbiIsCiAgICAjYmFzZWQgb24gZW5nbGlzaCBsYW5ndWFnZSBuYW1lcwogICAgb3JpZ2luID0gImNvdW50cnkubmFtZSIpCiAgCiAgI2FkZCB2YWx1ZSB0byBhIGxpc3QKICBpc29fbGlzdF9zZW5kX2NvdW50cnlfbnVtIDwtIGFwcGVuZChpc29fbGlzdF9zZW5kX2NvdW50cnlfbnVtLGNvdW50cnlfaXNvKQp9CgojIyNzZW5kaW5nIGNvdW50cnkgY29sdW1uICgxKSB0aHJlZSBkaWdpdCBsZXR0ZXJzCmZvcihjb3VudHJ5IGluIGxvd3lfZGF0YV9pc29bMV0pewogIGNvdW50cnlfaXNvIDwtIGNvdW50cnljb2RlKAogICAgI2NhbGwgdGhlIGVudHJ5IG9mICdDb3VudHJ5JyBmb3IgdGhlIHJvd19udW1iZXIgY3VycmVudGx5IGJlaW5nIHRha2VuIGJ5IGZvciBsb29wCiAgICBzb3VyY2V2YXIgPSBjb3VudHJ5LCAKICAgICN0aHJlZSBsZXR0ZXIgaXNvIGNvZGUKICAgIGRlc3RpbmF0aW9uID0gImdlbmMzYyIsIAogICAgI2Jhc2VkIG9uIGVuZ2xpc2ggbGFuZ3VhZ2UgbmFtZXMKICAgIG9yaWdpbiA9ICJjb3VudHJ5Lm5hbWUiKQogIAogICNhZGQgdmFsdWUgdG8gYSBsaXN0CiAgaXNvX2xpc3Rfc2VuZF9jb3VudHJ5IDwtIGFwcGVuZChpc29fbGlzdF9zZW5kX2NvdW50cnksY291bnRyeV9pc28pCn0KIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMjI1Bvc3QgY291bnRyeSBjb2x1bW4gKDMpIHRocmVlIGRpZ2l0IG51bWJlcnMKZm9yKGNvdW50cnkgaW4gbG93eV9kYXRhX2lzb1szXSl7CiAgcG9zdF9jb3VudHJ5X2lzbyA8LSBjb3VudHJ5Y29kZSgKICAgICNjYWxsIHRoZSBlbnRyeSBvZiAnQ291bnRyeScgZm9yIHRoZSByb3dfbnVtYmVyIGN1cnJlbnRseSBiZWluZyB0YWtlbiBieSBmb3IgbG9vcAogICAgc291cmNldmFyID0gY291bnRyeSwgCiAgICAjdGhyZWUgbGV0dGVyIGlzbyBjb2RlCiAgICBkZXN0aW5hdGlvbiA9ICJpc28zbiIsIAogICAgI2Jhc2VkIG9uIGVuZ2xpc2ggbGFuZ3VhZ2UgbmFtZXMKICAgIG9yaWdpbiA9ICJjb3VudHJ5Lm5hbWUiKQogIAogICNhZGQgdmFsdWUgdG8gYSBsaXN0CiAgaXNvX2xpc3RfcG9zdF9jb3VudHJ5X251bSA8LSBhcHBlbmQoaXNvX2xpc3RfcG9zdF9jb3VudHJ5X251bSxwb3N0X2NvdW50cnlfaXNvKQp9CgojIyNQb3N0IGNvdW50cnkgY29sdW1uICgzKSB0aHJlZSBsZXR0ZXJzCmZvcihjb3VudHJ5IGluIGxvd3lfZGF0YV9pc29bM10pewogIHBvc3RfY291bnRyeV9pc28gPC0gY291bnRyeWNvZGUoCiAgICAjY2FsbCB0aGUgZW50cnkgb2YgJ0NvdW50cnknIGZvciB0aGUgcm93X251bWJlciBjdXJyZW50bHkgYmVpbmcgdGFrZW4gYnkgZm9yIGxvb3AKICAgIHNvdXJjZXZhciA9IGNvdW50cnksIAogICAgI3RocmVlIGxldHRlciBpc28gY29kZQogICAgZGVzdGluYXRpb24gPSAiZ2VuYzNjIiwgCiAgICAjYmFzZWQgb24gZW5nbGlzaCBsYW5ndWFnZSBuYW1lcwogICAgb3JpZ2luID0gImNvdW50cnkubmFtZSIpCiAgCiAgI2FkZCB2YWx1ZSB0byBhIGxpc3QKICBpc29fbGlzdF9wb3N0X2NvdW50cnkgPC0gYXBwZW5kKGlzb19saXN0X3Bvc3RfY291bnRyeSxwb3N0X2NvdW50cnlfaXNvKQp9CgojIyNjcmVhdGUgbmV3IGNvbHVtbnMKbG93eV9kYXRhX2lzbyRzZW5kX2NvdW50cnlfaXNvID0gaXNvX2xpc3Rfc2VuZF9jb3VudHJ5Cmxvd3lfZGF0YV9pc28kc2VuZF9jb3VudHJ5X2lzb19udW0gPSBpc29fbGlzdF9zZW5kX2NvdW50cnlfbnVtCgpsb3d5X2RhdGFfaXNvJHBvc3RfY291bnRyeV9pc28gPSBpc29fbGlzdF9wb3N0X2NvdW50cnkKbG93eV9kYXRhX2lzbyRwb3N0X2NvdW50cnlfaXNvX251bSA9IGlzb19saXN0X3Bvc3RfY291bnRyeV9udW0KYGBgCgpDbGljayBhcnJvd3MgdG8gc2VlIGFsbCBpc28gY29sdW1ucyBpbiBkYXRhIGZyYW1lCmBgYHtyfQojIyNwcmV2aWV3IGRhdGEgZnJhbWUKaGVhZChsb3d5X2RhdGFfaXNvLDMpCmBgYAoKCiMjIyMgMi4zIEdlby1jb2RpbmcgeyNoXzIuM19nZW9jb2RlfQpbQmFjayB0byBpbmRleF0oI2hfMC4yX2luZGV4KTxicj4KVGhpcyBzZWN0aW9uIHJlcXVpcmVzIGEgZ29vZ2xlIGFwaSBrZXk7IHlvdSBjYW4gc2V0IG9uZSB1cCBbaGVyZV0oaHR0cHM6Ly9jb25zb2xlLmNsb3VkLmdvb2dsZS5jb20vKXt0YXJnZXQ9Il9ibGFuayJ9IHRocm91Z2ggdGhlIGdvb2dsZSBjbG91ZCBwbGF0Zm9ybQoKYGBge3J9CnJlZ2lzdGVyX2dvb2dsZShrZXkgPSAiIikKYGBgCgpDb2RlIHRvIHJ1biB0aGUgZ2VvY29kaW5nIGZvciBlYWNoIGNvdW50cnkgY29sdW1uIChgc2VuZF9jb3VudHJ5YCBgcG9zdF9jb3VudHJ5YCksIGNpdHkgYHBvc3RfY2l0eWAgKyBgcG9zdCBjb3VudHJ5YCAodG8gYXZvaWQgZmluZGluZyB3cm9uZyBjaXR5KTxicj4KCioqUGFydCAxIGNpdGllcyBhbmQgY291bnRyaWVzKio8YnI+CltCYWNrIHRvIGluZGV4XSgjaF8wLjJfaW5kZXgpPGJyPgpHZW5lcmF0ZSBnZW9jb2RpbmcgcXVlcmllczoKYGBge3J9CiMjIz09PT09PT09UHJlcGFyYXRpb24KCiMjI2NvbmNhdGVuYXRlIHBvc3QgY2l0eSBhbmQgcG9zdCBjb3VudHJ5IGNvbHVtbnMKbG93eV9kYXRhX2lzbyRwb3N0X2NpdHlfY291bnRyeSA9IHBhc3RlKAogIGxvd3lfZGF0YV9pc28kcG9zdF9jaXR5LAogIGxvd3lfZGF0YV9pc28kcG9zdF9jb3VudHJ5LAogIHNlcD0iLCAiKQoKIyMjZ2VvLWMgLSBjb25jYXRlbmF0ZWQgY2l0eSBhbmQgY291bnRyeSBuYW1lcwpnZW9fY2l0eV9jb3VudHJ5IDwtIGRpc3RpbmN0KGxvd3lfZGF0YV9pc28scG9zdF9jaXR5X2NvdW50cnkpCmdlb19jaXR5X2NvdW50cnlfZGYgPC0gYXMuZGF0YS5mcmFtZShnZW9fY2l0eV9jb3VudHJ5KQojZ2VvX2NpdHlfY291bnRyeV9kZiA8LSBoZWFkKGdlb19jaXR5X2NvdW50cnlfZGYsNCkgI2ZvciBzYW1wbGUgd29yayBvbmx5CgojIyNnZW8tY29kZSAtIHNlbmRpbmcgY291bnRyaWVzCmdlb19zZW5kX2NvdW50cnkgPC0gZGlzdGluY3QobG93eV9kYXRhX2lzbyxzZW5kX2NvdW50cnkpCmdlb19zZW5kX2NvdW50cnlfZGYgPC0gYXMuZGF0YS5mcmFtZShnZW9fc2VuZF9jb3VudHJ5KQpgYGAKCkNpdGllcyBob3N0aW5nIGRpcGxvbWF0aWMgb3V0cG9zdHMgZ2VvY29kZWQKYGBge3J9CgojIyM9PT09PT09PVBPU1QgQ0lUSUVTCiNpZiBsb2NhbCBmaWxlIHRhZyBpcyBzZXQgdG8gVFJVRSwgZ29vZ2xlIGFwaSB3aWxsIG5vdCBiZSB1c2VkCmlmKHVzZV9sb2NhbF9maWxlcykgewogICNwcmludCgiTG9jYWwgZmlsZSBmbGFnIHNldCB0byBUUlVFLCB1c2luZyBsb2NhbCBmaWxlcyIpCiAgZ2VvX2NpdHlfY291bnRyeV9kZl9kbCA8LSByZWFkLmNzdigiLi9EYXRhL0dvb2dsZUFQSS9HZW9fQ2l0eV9Db3VudHJ5X0RMLmNzdiIpCn0gZWxzZSB7CiAgI3ByaW50KCJMb2NhbCBmaWxlIGZsYWcgc2V0IHRvIEZBTFNFLCBjYWxsaW5nIGdvb2dsZSBub3chIikKICBnZW9fY2l0eV9jb3VudHJ5X2RmX2RsIDwtIG11dGF0ZV9nZW9jb2RlKGdlb19jaXR5X2NvdW50cnlfZGYsIHBvc3RfY2l0eV9jb3VudHJ5KQogICNTYXZlIC5jc3YgZmlsZQogIHdyaXRlLmNzdihnZW9fY2l0eV9jb3VudHJ5X2RmX2RsLCIuL0RhdGEvR29vZ2xlQVBJL0dlb19DaXR5X0NvdW50cnlfREwuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCn0KaGVhZChnZW9fY2l0eV9jb3VudHJ5X2RmX2RsLDMpCmBgYAoKU2VuZGluZyBjb3VudHJpZXMgZ2VvY29kZWQKYGBge3J9CgojIyM9PT09PT09PVNFTkQgQ09VTlRSSUVTCiNpZiBsb2NhbCBmaWxlIHRhZyBpcyBzZXQgdG8gVFJVRSwgZ29vZ2xlIGFwaSB3aWxsIG5vdCBiZSB1c2VkCmlmKHVzZV9sb2NhbF9maWxlcykgewogICNwcmludCgiTG9jYWwgZmlsZSBmbGFnIHNldCB0byBUUlVFLCB1c2luZyBsb2NhbCBmaWxlcyIpCiAgZ2VvX3NlbmRfY291bnRyeV9kZl9kbCA8LSByZWFkLmNzdigiLi9EYXRhL0dvb2dsZUFQSS9HZW9fU2VuZF9Db3VudHJ5X0RMLmNzdiIpCn0gZWxzZSB7CiAgI3ByaW50KCJMb2NhbCBmaWxlIGZsYWcgc2V0IHRvIEZBTFNFLCBjYWxsaW5nIGdvb2dsZSBub3chIikKICBnZW9fY2l0eV9jb3VudHJ5X2RmX2RsIDwtIG11dGF0ZV9nZW9jb2RlKGdlb19jaXR5X2NvdW50cnlfZGYsIHBvc3RfY2l0eV9jb3VudHJ5KQogICNTYXZlIC5jc3YgZmlsZQogIHdyaXRlLmNzdihnZW9fc2VuZF9jb3VudHJ5X2RmX2RsLCIuL0RhdGEvR29vZ2xlQVBJL0dlb19TZW5kX0NvdW50cnlfREwuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCn0KCmhlYWQoZ2VvX3NlbmRfY291bnRyeV9kZl9kbCwzKQpgYGAKCkRhdGEgZnJhbWUgd2l0aCBhbGwgY291bnRyaWVzIGFuZCBjaXRpZXMgZ2VvY29kZWQgKGZpcnN0IDUwIHJlY29yZHMpCmBgYHtyfQojIyM9PT09PT09PUpPSU4gREFUQSBUTyBNQVNURVIgREFUQSBGUkFNRQpsb3d5X2RhdGFfZ2VvIDwtIGxvd3lfZGF0YV9pc28KCiNKb2luIHNlbmQgbG9uL2xhdApsb3d5X2RhdGFfZ2VvIDwtIG1lcmdlKGxvd3lfZGF0YV9nZW8sZ2VvX3NlbmRfY291bnRyeV9kZl9kbCwgYnkgPSAic2VuZF9jb3VudHJ5IikKCmxvd3lfZGF0YV9nZW8gPC0gbG93eV9kYXRhX2dlbyAlPiUgcmVuYW1lKAogIHNlbmRfbG9uID1sb24sCiAgc2VuZF9sYXQgPSBsYXQpCgojSm9pbiBwb3N0IGNpdHkgbG9uL2xhdApsb3d5X2RhdGFfZ2VvIDwtIG1lcmdlKGxvd3lfZGF0YV9nZW8sZ2VvX2NpdHlfY291bnRyeV9kZl9kbCwgYnkgPSAicG9zdF9jaXR5X2NvdW50cnkiKQoKbG93eV9kYXRhX2dlbyA8LSBsb3d5X2RhdGFfZ2VvICU+JSByZW5hbWUoCiAgcG9zdF9jaXR5X2xvbiA9bG9uLAogIHBvc3RfY2l0eV9sYXQgPSBsYXQpCgpoZWFkKGxvd3lfZGF0YV9nZW8sNTApCmBgYAoKKipQYXJ0IDIgZXhhY3QgZW1iYXNzeSBsb2NhdGlvbnMqKjxicj4KW0JhY2sgdG8gaW5kZXhdKCNoXzAuMl9pbmRleCk8YnI+CkNvZGUgdG8gcnVuIHRoZSBnZW9jb2RpbmcgZm9yIGV4YWN0IGVtYmFzc3kgbG9jYXRpb25zLiBVc2luZyBjb21iaW5hdGlvbiBvZiBgc2VuZF9jb3VudHJ5YCtgcG9zdF9jb3VudHJ5YCsiRW1iYXNzeSIrIGBwb3N0X2NpdHlgCmBgYHtyfQojIyNtYXN0ZXIgZGF0YWZyYW1lIHdpdGggYWxsIGRhdGEKbG93eV9kYXRhX2dlb19tYXN0ZXIgPC0gbG93eV9kYXRhX2dlbwoKIyMjRXhhY3QgZ2VvY29kaW5nIHF1ZXJ5Cmxvd3lfZGF0YV9nZW9fbWFzdGVyJGZ1bGxfZ2VvX3F1ZXJ5ID0gcGFzdGUoCiAgbG93eV9kYXRhX2dlb19tYXN0ZXIkc2VuZF9jb3VudHJ5LAogIGxvd3lfZGF0YV9nZW9fbWFzdGVyJHBvc3RfdGl0bGUsCiAgbG93eV9kYXRhX2dlb19tYXN0ZXIkcG9zdF9jaXR5X2NvdW50cnksCiAgc2VwPSIsICIpCgojIyNSVU4gR0VPQ09ESU5HCmlmKHVzZV9sb2NhbF9maWxlcykgewogIHByaW50KCJMb2NhbCBmaWxlIGZsYWcgc2V0IHRvIFRSVUUsIHVzaW5nIGxvY2FsIGZpbGVzIikKICBsb3d5X2RhdGFfZ2VvX21hc3RlciA8LSByZWFkLmNzdigiLi9EYXRhL0dvb2dsZUFQSS9sb3d5X2RhdGFfZ2VvX21hc3Rlci5jc3YiKQp9IGVsc2UgewogIHByaW50KCJMb2NhbCBmaWxlIGZsYWcgc2V0IHRvIEZBTFNFLCBjYWxsaW5nIGdvb2dsZSBub3chIikKICBsb3d5X2RhdGFfZ2VvX21hc3RlciA8LSBtdXRhdGVfZ2VvY29kZShsb3d5X2RhdGFfZ2VvX21hc3RlciwgZnVsbF9nZW9fcXVlcnkpCiAgCiAgI3JlbmFtZSBjb2x1bW5zCiAgbG93eV9kYXRhX2dlb19tYXN0ZXIgPC0gbG93eV9kYXRhX2dlb19tYXN0ZXIgJT4lIHJlbmFtZSgKICBleGFjdF9wb3N0X2xvbiA9IGxvbiwKICBleGFjdF9wb3N0X2xhdCA9IGxhdCkKICAKICAjU2F2ZSAuY3N2IGZpbGUKICB3cml0ZS5jc3YoZ2VvX3NlbmRfY291bnRyeV9kZl9kbCwiLi9EYXRhL0dvb2dsZUFQSS9sb3d5X2RhdGFfZ2VvX21hc3Rlci5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKfQpgYGAKCkZpcnN0IDUwIHJlY29yZHMgb2YgYWxsIGdlb2NvZGVkIGRpcGxvbWF0aWMgb3V0cG9zdHMKYGBge3J9CmhlYWQobG93eV9kYXRhX2dlb19tYXN0ZXIsNTApCmBgYAoKYGBge3IgaW5jbHVkZT1GQUxTRX0KI1VubG9hZCB1bm5lY2Vzc2FyeSBkYXRhIGZyb20gZ2xvYmFsIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKSAjcHVyZ2UgYWxsIGVsc2UgKGNvbW1lbnQvdW5jb21tZW50IGFzIG5lZWRlZCkKYGBgCgoqKioKCiMjIyAzLjAgTG9hZCBhbmQgYW5hbHlzZSBMb3d5IEluc3RpdHV0ZSBkYXRhIHsjaF8zLjBfYW5hbHlzZX0KfHwgWzAuMHxSRUFEIE1FXSgjaF8wLjBfcmVhZG1lKSB8fCBbMS4wfEVYVFJBQ1RdKCNoXzEuMF9leHRyYWN0KSB8fCBbMi4wfFRSQU5TRk9STV0oI2hfMi4wX3RyYW5zZm9ybSkgfHwgWyoqMy4wfExPQUQgYW5kIEFOQUxZU0UqKl0oI2hfMy4wX2FuYWx5c2UpIHx8IFs0LjB8QUNMRUQgZGF0YV0oI2hfNC4wX2FjbGVkKSB8fCBbNS4wfEFDTEVEIGRhdGFdKCNoXzUuMF9jYXNlcykgfHw8YnI+Cgo+ICoqUHVycG9zZSBvZiBzZWN0aW9uKiogfCB2aXN1YWxpc2UgY29vcmRpbmF0ZXM7IGFuZCBydW4gY2x1c3RlcmluZyBhbmFseXNpczxicj4KPiAqKklucHV0cyoqIHwgYGxvd3lfZGF0YV9nZW9fbWFzdGVyYCAoZmluYWwgb3V0cHV0IG9mIHBhcnQgMik8YnI+Cj4gKipPdXRwdXRzKiogfCBtYXBzIGFuZCB2aXN1YWxzCgoqKioKCiMjIyMgMy4xIExvYWQgZGF0YSBhbmQgc2VnbWV0IHVzYWJsZSBwYXJ0IGZvciBhbmFseXNpcyB7I2hfMy4xX2FuYWx5c2lzX3ByZXB9CltCYWNrIHRvIGluZGV4XSgjaF8wLjJfaW5kZXgpPGJyPgoKTG9hZCBkYXRhCmBgYHtyIHJlc3VsdHM9J2hpZGUnfQojIyNMb2FkIGRhdGEgZnJvbSBzZWN0aW9uIDIKbG93eV9kYXRhX2NvbXBsZXRlIDwtIHJlYWRfY3N2KCIuL0RhdGEvR29vZ2xlQVBJL2xvd3lfZGF0YV9nZW9fbWFzdGVyLmNzdiIpCmBgYAo8YnI+CgpTZWdtZW50YXRpb24gY29kZQpgYGB7cn0KIyMjRmlsdGVyIG91dCB1bnVzYWJsZSBkYXRhcG9pbnRzCiNuYW1lcyhsb3d5X2RhdGFfY29tcGxldGUpCgojUmUtYXJyYW5nZSBjb2x1bW5zIGFuZCBzb3J0IHRhYmxlCmxvd3lfZGF0YV9jb21wbGV0ZSA8LWxvd3lfZGF0YV9jb21wbGV0ZQpsb3d5X2RhdGFfY29tcGxldGUgPSBzZWxlY3QobG93eV9kYXRhX2NvbXBsZXRlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICNzZW5kaW5nIGRhdGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbmRfY291bnRyeV9pc28sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW5kX2NvdW50cnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZW5kX2xvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbmRfbGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgI3Bvc3RpbmcgZGF0YQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zdF9jb3VudHJ5X2lzbywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3N0X2NvdW50cnksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3N0X2NpdHlfY291bnRyeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc3RfY2l0eSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBvc3RfY2l0eV9sb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3N0X2NpdHlfbGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zdF90eXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zdF90aXRsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICNvdGhlciBjb2x1bW5zCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBldmVyeXRoaW5nKCkpCgpsb3d5X2RhdGFfY29tcGxldGUgPC0gbG93eV9kYXRhX2NvbXBsZXRlW29yZGVyKGxvd3lfZGF0YV9jb21wbGV0ZSRzZW5kX2NvdW50cnkpLF0KCiNGaWx0ZXIgb3V0IE5BIHZhbHVlcyAoYXJvdW5kIDQ0IHZhbHVlcykKZ2VvX2xvd3lfTkFfdmFsdWVzIDwtIGxvd3lfZGF0YV9jb21wbGV0ZSAlPiUKICBkcGx5cjo6c2VsZWN0KGV2ZXJ5dGhpbmcoKSkgJT4lCiAgZHBseXI6OmZpbHRlcihpcy5uYShleGFjdF9wb3N0X2xvbikpCgpnZW9fbG93eV9ub19OQSA8LSBsb3d5X2RhdGFfY29tcGxldGUgJT4lCiAgZHBseXI6OnNlbGVjdChldmVyeXRoaW5nKCkpICU+JQogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKGV4YWN0X3Bvc3RfbG9uKSkKYGBgCjxicj4KU3VtbWFyaXNlIGNoYW5nZSB0byBtYXN0ZXIgR0RJIGRhdGFmcmFtZQpgYGB7cn0KI1ByZXZpZXcgdGFibGUKcmVwb3J0X2Z1bGxfbGVuZ3RoIDwtbGVuZ3RoKGxvd3lfZGF0YV9jb21wbGV0ZSRzZW5kX2NvdW50cnkpCnJlcG9ydF9uYV9sZW5ndGggPC0gbGVuZ3RoKGdlb19sb3d5X05BX3ZhbHVlcyRzZW5kX2NvdW50cnkpCnJlcG9ydF9ub25fbmFfbGVuZ3RoIDwtIGxlbmd0aChnZW9fbG93eV9ub19OQSRzZW5kX2NvdW50cnkpCgpjYXQoIlRoZXJlIGFyZSwiLCByZXBvcnRfZnVsbF9sZW5ndGgsImVudHJpZXMgaW4gdGhlIGNvbXBsZXRlIGRhdGEgZnJhbWUuIiwiXG5Gcm9tIHRoZXNlLCIscmVwb3J0X25hX2xlbmd0aCwiYXJlIE5BL2JsYW5rLiAoIixyZXBvcnRfbmFfbGVuZ3RoL3JlcG9ydF9mdWxsX2xlbmd0aCoxMDAsIiUgKSIsIlxuSW4gdG90YWwsIixyZXBvcnRfbm9uX25hX2xlbmd0aCwiZW50cmllcyBoYXZlIHZhbGlkIGNvb3JkaW5hdGVzLiIsIlxuVG90YWwgY292ZXJhZ2UgaXM6IixyZXBvcnRfbm9uX25hX2xlbmd0aC9yZXBvcnRfZnVsbF9sZW5ndGgqMTAwLCIlIikKYGBgCjxicj4KYWxsIHBvaW50cyB2aXN1YWxpc2VkCmBgYHtyfQpnZW9fbG93eV9ub19OQV92aXMgPC0KICAgIHN0X2FzX3NmKGdlb19sb3d5X25vX05BLGNvb3Jkcz1jKCJleGFjdF9wb3N0X2xvbiIsImV4YWN0X3Bvc3RfbGF0IiksY3JzID0gNDMyNixyZW1vdmUgPSBGQUxTRSkKCm1hcHZpZXcoZ2VvX2xvd3lfbm9fTkFfdmlzKQoKYGBgCgojIyMjIDMuMiBHZW8tY29kaW5nIGFuYWx5c2lzIGZ1bmN0aW9uIHsjaF8zLjJfZ2VvX2Z1bmN0aW9ufQpbQmFjayB0byBpbmRleF0oI2hfMC4yX2luZGV4KTxicj4KRnVuY3Rpb24gdG8gdGFrZSBpbiBkYXRhIGZyYW1lLCBhbmQgYSBmaWx0ZXIgdG8gdmlzdWFsaXNlIGFuZCByZXR1cm4gZmlsdGVyZWQgZGF0YWZyYW1lIGZvciBmdXJ0aGVyIGFuYWx5c2lzIGBmX2V4YWN0X2NpdHlfcG9zdGluZ3MgPC0gZnVuY3Rpb24ocF9jaXR5X2ZpbHRlcixwX2RmKWAgZXhwYW5kIHRvIHNlZSBmdW5jdGlvbiAKYGBge3J9CiMjI0ZVTkNUSU9OCmZfZXhhY3RfY2l0eV9wb3N0aW5ncyA8LSBmdW5jdGlvbihwX2NpdHlfZmlsdGVyLHBfZGYscF9jb2xvdXIgPSAicmVkIil7CiAgCiAgIyMjU3Vic2V0IG9mIGRhdGFmcmFtZSBmb3IgY2hvc2VuIGNvdW50cnkKICBzdWJfY2l0eV9kZiA8LSBwX2RmW3BfZGYkcG9zdF9jaXR5ID09IHBfY2l0eV9maWx0ZXIsXQoKICAjY3JlYXRlIHNwYXRpYWwgb2JqZWN0cwogIHN1Yl9jaXR5X3NmIDwtIAogICAgc3RfYXNfc2Yoc3ViX2NpdHlfZGYsY29vcmRzPWMoInBvc3RfY2l0eV9sb24iLCJwb3N0X2NpdHlfbGF0IiksY3JzID0gNDMyNiwgcmVtb3ZlID0gRkFMU0UpCgogIHN1Yl9jaXR5X2dlb19zZiA8LQogICAgc3RfYXNfc2Yoc3ViX2NpdHlfZGYsY29vcmRzPWMoImV4YWN0X3Bvc3RfbG9uIiwiZXhhY3RfcG9zdF9sYXQiKSxjcnMgPSA0MzI2LHJlbW92ZSA9IEZBTFNFKQoKICAjbWFwIHZpZXcgb3B0aW9ucwogIG1hcHZpZXdPcHRpb25zKGxlYWZsZXRXaWR0aCA9IDc1MCxsZWFmbGV0SGVpZ2h0ID0gNDAwKQogIAogICNDaXR5IGxhdCBsb25ncwogIGV4YWN0X21hcCA8LSBtYXB2aWV3KAogICAgICAjc3BhdGlhbCBvYmplY3QKICAgICAgc3ViX2NpdHlfc2YsCiAgICAgIGxlZ2VuZCA9IEZBTFNFLAogICAgICBsYXllci5uYW1lID0gIkNpdHkgY29vcmRpbmF0ZXMiLAogICAgICBsd2QgPSAwLjUsCiAgICAgIGFscGhhLnJlZ2lvbnMgPSAwLjIsCiAgICAgIGxhYmVsID0gc3ViX2NpdHlfZGYkcG9zdF9jaXR5X2NvdW50cnksCiAgICAgIGNleCA9IDEwLAogICAgICBjb2xvdXIgPSAiYmxhY2siLAogICAgICBjb2wucmVnaW9ucyA9ICJvcmFuZ2UiLAogICAgICBwb3B1cCA9IGxlYWZwb3A6OnBvcHVwVGFibGUoc3Vic2V0KHN1Yl9jaXR5X3NmLCBzZWxlY3QgPSBjKHBvc3RfY2l0eV9jb3VudHJ5KSkpCiAgICAgICkrIG1hcHZpZXcoCiAgICAgICNzcGF0aWFsIG9iamVjdAogICAgICBzdWJfY2l0eV9nZW9fc2YsCiAgICAgIGxlZ2VuZCA9IEZBTFNFLAogICAgICBsYXllci5uYW1lID0gIkRpcGxvbWF0aWMgcG9zdCBsb2NhdGlvbnMiLAogICAgICBsYWJlbCA9IHN1Yl9jaXR5X2RmJGZ1bGxfZ2VvX3F1ZXJ5LAogICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgIGFscGhhPSAxLAogICAgICBsd2QgPSAiMC41IiwKICAgICAgYWxwaGEucmVnaW9ucyA9IDAuNSwKICAgICAgY29sLnJlZ2lvbnMgPSBwX2NvbG91ciwKICAgICAgY2V4ID0gNiwKICAgICAgcG9wdXAgPSBsZWFmcG9wOjpwb3B1cFRhYmxlKHN1YnNldChzdWJfY2l0eV9zZiwgc2VsZWN0ID0gYyhzZW5kX2NvdW50cnlfaXNvLHNlbmRfY291bnRyeSxwb3N0X3R5cGUscG9zdF90aXRsZSxleGFjdF9wb3N0X2xvbixleGFjdF9wb3N0X2xhdCkpKQogICAgICApCiAgCiAgbWFwc2hvdChleGFjdF9tYXAsIHVybCA9IHBhc3RlKCIvY2xvdWQvcHJvamVjdC9EYXRhL01hcEZpbGVzLyIscF9jaXR5X2ZpbHRlciwibWFwLmh0bWwiLHNlcD0iXyIpKQoKICAjcmV0dXJuIGRsIHRhYmxlLG1hcAogIHJldHVybl9saXN0IDwtIGxpc3QoZGYgPSBzdWJfY2l0eV9kZixtYXAgPSBleGFjdF9tYXApCiAgcmV0dXJuKHJldHVybl9saXN0KQp9CmBgYAoKIyMjIyAzLjMgVmlzdWFsaXNlIHsjaF8zLjNfZ2VvX3Zpc30KW0JhY2sgdG8gaW5kZXhdKCNoXzAuMl9pbmRleCk8YnI+CgpSdW4gZnVuY3Rpb24gb24gYXJyYXkgb2YgYSBjaXR5IG9mIGludGVyZXN0IC0gVGVocmFuCmBgYHtyfQojIyNydW4gZnVuY3Rpb24KZl9vdXQgPC0gZl9leGFjdF9jaXR5X3Bvc3RpbmdzKHBfY2l0eV9maWx0ZXIgPSAiVGVocmFuIixwX2RmID0gZ2VvX2xvd3lfbm9fTkEpCmZfb3V0JG1hcApgYGAKCiMjIyMgMy40IENsdXN0ZXJpbmcgeyNoXzMuNF9jbHVzdGVyfQpVc2UgZnVuY3Rpb24gb3V0cHV0IGZyb20gMy4yIHRvIHJ1biBEQlNDQU4gc3BhdGlhbCBjbHVzdGVyaW5nIGFuYWx5c2lzCmBgYHtyfQojPT09PT09PT09PT09PT0gZnVuY3Rpb24gc3RhcnQKZl9leGFjdF9jaXR5X2NsdXN0ZXIgPC0gZnVuY3Rpb24ocF9jaXR5X2ZpbHRlcixwX2RmLHBfZXBzLHBfayA9IDQscF9vdXRsaWVyX3JlbW92ZSA9IEZBTFNFKXsKICAKICAgICNydW4gb3JpZ2luYWwgZnVuY3Rpb24KICBmX291dCA8LSBmX2V4YWN0X2NpdHlfcG9zdGluZ3MocF9jaXR5X2ZpbHRlciA9IHBfY2l0eV9maWx0ZXIscF9kZiA9IHBfZGYpCiAgCiAgZl9vdXQkZGYKICAKICAjYXNzaWduIGNvb3JkaW5hdGVzIG9mIGZpbHRlcmVkIGRhdGFzZXQgdG8gdmFyaWFibGUKICBkYl9jb29yZGluYXRlcyA8LSBmX291dCRkZltjKCJleGFjdF9wb3N0X2xvbiIsImV4YWN0X3Bvc3RfbGF0IildKjEwMDAKICAKICAjYm94cGxvdAogIGRiX2Nvb3JkaW5hdGVzX2JveCA8LSBib3hwbG90KGRhdGE9ZGJfY29vcmRpbmF0ZXMkZXhhY3RfcG9zdF9sb24sIHg9ZGJfY29vcmRpbmF0ZXMkZXhhY3RfcG9zdF9sb24sIG1haW49IkJveHBsb3QgZm9yIGNvb3JkaW5hdGUgdmFsdWVzIiwgCiAgIHhsYWI9IlBvc3RzIiwgeWxhYj0iTG9uZ2l0dWRlIHZhbHVlcyIsbm90Y2ggPSBUUlVFKSAjW1JFVFVSTiAxXQogIAogIGRiX2Nvb3JkaW5hdGVzIDwtIGRiX2Nvb3JkaW5hdGVzLzEwMDAKICBkYl9jb29yZGluYXRlc19vdXQgPC0gZGJfY29vcmRpbmF0ZXNfYm94JG91dC8xMDAwCiAgCiAgaWYobGVuZ3RoKGRiX2Nvb3JkaW5hdGVzX291dCkgPT0gMCl7CiAgICB6ZXJvX291dGxpZXJzIDwtIEZBTFNFCiAgfWVsc2V7CiAgICB6ZXJvX291dGxpZXJzIDwtIFRSVUUKICB9CiAgCiAgI25lZ2F0aW9uIGZvciBmaWx0ZXJpbmcKICBgJW5pbiVgIDwtIE5lZ2F0ZSgiJWluJSIpCgogIAogICNpZiBvdXRsaWVyIHBhcmFtZXRlciBzZXQgdG8gdHJ1ZSwgdGhleSB3aWxsIGJlIHJlbW92ZWQsIGVsc2UgY2FuIGtlZXAgZ29pbmcgYXMgaXMKICBpZihwX291dGxpZXJfcmVtb3ZlICYgemVyb19vdXRsaWVycyl7CiAgICAKICAgICNjbHVzdGVyaW5nIG9iamVjdHMKICAgIGRiX2Nvb3JkaW5hdGVzX2tlZXAgPC0gZGJfY29vcmRpbmF0ZXMlPiUgZmlsdGVyKGV4YWN0X3Bvc3RfbG9uICVuaW4lIGRiX2Nvb3JkaW5hdGVzX291dCkKICAgIGRiX2Nvb3JkaW5hdGVzX291dF9jbHVzdGVyIDwtIGRiX2Nvb3JkaW5hdGVzJT4lIGZpbHRlcihleGFjdF9wb3N0X2xvbiAlaW4lIGRiX2Nvb3JkaW5hdGVzX291dCkKICAgIAogICAgI2Z1bGwgZGF0YWZyYW1lIHdpdGhvdXQgb3V0bGllcnMKICAgIGZfb3V0X2RmIDwtIGZfb3V0JGRmICU+JSBmaWx0ZXIoZXhhY3RfcG9zdF9sb24gJW5pbiUgZGJfY29vcmRpbmF0ZXNfb3V0KQogICAgICAKICAgICNvdXRsaWVyIGRhdGFmcmFtZQogICAgZl9vdXRfb3V0bGllcl9kZiA8LSBmX291dCRkZiAlPiUgZmlsdGVyKGV4YWN0X3Bvc3RfbG9uICVpbiUgZGJfY29vcmRpbmF0ZXNfb3V0KQogICAgCiAgICBmX291dF9vdXRsaWVyX2RmX3NmIDwtIHN0X2FzX3NmKGZfb3V0X291dGxpZXJfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29vcmRzPWMoImV4YWN0X3Bvc3RfbG9uIiwiZXhhY3RfcG9zdF9sYXQiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2LHJlbW92ZSA9IEZBTFNFKQogIH1lbHNlewogICAgZGJfY29vcmRpbmF0ZXNfa2VlcCA8LSBkYl9jb29yZGluYXRlcwogICAgZl9vdXRfZGYgPC0gZl9vdXQkZGYKICB9CiAgCiAgI0luZGV0aWZ5IG9wdGltYWwgZXBzIAogIGtOTmRpc3RfZGYgPC0gZGJzY2FuOjprTk5kaXN0KGRiX2Nvb3JkaW5hdGVzX2tlZXAsIGsgPSBwX2spCiAgZGJfa05OX3Bsb3QgPC0gZGJzY2FuOjprTk5kaXN0cGxvdChkYl9jb29yZGluYXRlc19rZWVwLGsgPSBwX2spICNbUkVUVVJOIDJdCgogICNub3cgcnVuIHRoZSBkYnNjYW4gYW5hbHlzaXMKICBkYiA8LSBmcGM6OmRic2NhbihkYl9jb29yZGluYXRlc19rZWVwLCBlcHMgPSBwX2VwcywgTWluUHRzID0gcF9rKQogIAogICNub3cgcGxvdCB0aGUgcmVzdWx0cyBbUkVUVVJOIDNdCiAgZGJfcGxvdCA8LSBwbG90KGRiLCBkYl9jb29yZGluYXRlc19rZWVwLCBtYWluID0gIkRCU0NBTiBPdXRwdXQiLCBmcmFtZSA9IEYpCiAgCiAgI3lvdSBjYW4gYWRkICcyJyB0byBjbHVzdGVyIHRvIGF2b2lkIDAgdmFsdWVzIGFzIGxlYWZsZXQgZG9lc24ndCBsaWtlIHRvIGNvbG91ciBieSAwICh6ZXJvKSB2YWx1ZXMgYW5kIGNvbG91ciAnMScgaXMgYmxhY2sKICBkYl9jb29yZGluYXRlc19rZWVwJGNsdXN0ZXIgPC0gZGIkY2x1c3RlcisxICMrMiAjbm90IHVzZWQKICBkYnNjYW5fdGVzdF9kZiA8LSBkYl9jb29yZGluYXRlc19rZWVwCiAgCiAgZGJzY2FuX3Rlc3RfZGYKICAKICBmX291dF9jbHVzdGVyIDwtIGZfZXhhY3RfY2l0eV9wb3N0aW5ncyhwX2NpdHlfZmlsdGVyID0gcF9jaXR5X2ZpbHRlcixwX2RmID0gZl9vdXRfZGYscF9jb2xvdXIgPSBkYnNjYW5fdGVzdF9kZiRjbHVzdGVyKQogIAogIGlmKHBfb3V0bGllcl9yZW1vdmUgJiB6ZXJvX291dGxpZXJzKXsKICAgIGZfb3V0X2NsdXN0ZXJfbWFwIDwtIGZfb3V0X2NsdXN0ZXIkbWFwICsgbWFwdmlldyhmX291dF9vdXRsaWVyX2RmX3NmLAogICAgICBsZWdlbmQgPSBGQUxTRSwKICAgICAgbGF5ZXIubmFtZSA9ICJPdXRsaWVycyIsCiAgICAgIGxhYmVsID0gZl9vdXRfb3V0bGllcl9kZl9zZiRmdWxsX2dlb19xdWVyeSwKICAgICAgY29sb3IgPSAieWVsbG93IiwKICAgICAgYWxwaGEucmVnaW9ucyA9IDEsCiAgICAgIGNvbC5yZWdpb25zID0gInllbGxvdyIsCiAgICAgIGNleCA9IDQpCiAgfWVsc2V7CiAgICAjbm8gb3V0bGllcnMgdG8gcGxvdAogICAgZl9vdXRfY2x1c3Rlcl9tYXAgPC0gZl9vdXRfY2x1c3RlciRtYXAgI1tSRVRVUk5dCiAgfQogIAogIGZfb3V0X2NsdXN0ZXIkZGYgI1tSRVRVUk5dCiAgCiAgICNtYXBzaG90KGZfb3V0X2NsdXN0ZXJfbWFwLCB1cmwgPSBwYXN0ZSgiL2Nsb3VkL3Byb2plY3QvUl9Ob3RlYm9vay9GaW5hbC9EYXRhL01hcEZpbGVzLyIscF9jaXR5X2ZpbHRlciwiY2x1c3Rlcl9tYXAuaHRtbCIsc2VwPSJfIikpCgogICNyZXR1cm4gbGlzdCBvZiByZWxldmFudCBlbGVtZW50cwogIHJldHVybl9saXN0IDwtIGxpc3QoZGZfc3ViID0gZl9vdXQkZGYsbWFwID0gZl9vdXQkbWFwLGJveF9wbG90ID0gZGJfY29vcmRpbmF0ZXNfYm94LCBrX3Bsb3QgPSBkYl9rTk5fcGxvdCxmX2NsdXN0ZXJfbWFwID0gZl9vdXRfY2x1c3Rlcl9tYXAsIGZfY2x1c3Rlcl9kZiA9IGZfb3V0X2NsdXN0ZXIkZGYpCiAgCiAgICByZXR1cm4ocmV0dXJuX2xpc3QpCn0KIz09PT09PT09PT09PT09IGZ1bmN0aW9uIGVuZApgYGAKClJ1biBmdW5jdGlvbgpgYGB7cn0Kc2FtcGxlX2FubGF5c2lzIDwtIGZfZXhhY3RfY2l0eV9jbHVzdGVyKHBfY2l0eV9maWx0ZXIgPSAiVGVocmFuIixwX2RmID0gZ2VvX2xvd3lfbm9fTkEscF9lcHMgPSAwLjAxNSxwX2sgPTQscF9vdXRsaWVyX3JlbW92ZSA9IFRSVUUpCnNhbXBsZV9hbmxheXNpcyRmX2NsdXN0ZXJfbWFwCmBgYAoKCioqKgoKIyMjIDQuMCBBQ0xFRCBkYXRhIHsjaF80LjBfYWNsZWR9Cnx8IFswLjB8UkVBRCBNRV0oI2hfMC4wX3JlYWRtZSkgfHwgWzEuMHxFWFRSQUNUXSgjaF8xLjBfZXh0cmFjdCkgfHwgWzIuMHxUUkFOU0ZPUk1dKCNoXzIuMF90cmFuc2Zvcm0pIHx8IFszLjB8TE9BRCBhbmQgQU5BTFlTRV0oI2hfMy4wX2FuYWx5c2UpIHx8IFsqKjQuMHxBQ0xFRCBkYXRhKipdKCNoXzQuMF9hY2xlZCkgfHwgWzUuMHxBQ0xFRCBjYXNlc10oI2hfNS4wX2Nhc2VzKSB8fDxicj4KCj4gKipQdXJwb3NlIG9mIHNlY3Rpb24qKiB8IGFkZCBkYXRhIGxheWVyIHRvIG91dHB1dCBmcm9tIGZ1bmN0aW9uIGluIDMuNCA8YnI+Cj4gKipJbnB1dHMqKiB8IGBmX2V4YWN0X2NpdHlfY2x1c3RlcigpYCAob3V0cHV0cyBvZiBmdW5jdGlvbiBjYWxsIGluIDMuNCk8YnI+Cj4gKipPdXRwdXRzKiogfCBtYXBzIGFuZCB2aXN1YWxzCgoqKioKCiMjIyMgNC4xIFNldCB1cCBBQ0xFRCBBUEkgeyNoXzQuMV9hY2xlZF9hcGl9CltCYWNrIHRvIGluZGV4XSgjaF8wLjJfaW5kZXgpCgpGb3IgZGV0YWlscyBvbiBob3cgdG8gdXNlIEFDTEVEIEFQSSBwbGVhc2Ugc2VlIGRvY3VtZW50YXRpb24gW2hlcmVdKGh0dHBzOi8vd3d3LmFjbGVkZGF0YS5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTMvMTIvQVBJLVVzZXItR3VpZGUtQXVndXN0LTIwMTcucGRmKXt0YXJnZXQ9Il9ibGFuayJ9CgpBUEkgc3RydWN0dXJlIGZvciBKU09OIG91dHB1dApGdWxsIHVybCBjYWxsOiBgaHR0cHM6Ly9hcGkuYWNsZWRkYXRhLmNvbS9hY2xlZC9yZWFkP3Rlcm1zPWFjY2VwdCZjb3VudHJ5PUlyYW4meWVhcj0yMDE5JmFkbWluMT1UZWhyYW5gClVSTCBiYXNlOiBgaHR0cHM6Ly9hcGkuYWNsZWRkYXRhLmNvbS9hY2xlZC9yZWFkP3Rlcm1zPWFjY2VwdGAgdGVybXM9YWNjZXB0IGlzIHJlcXVpcmVkIGZvciBvdXRwdXQgdG8gYmUgcHJvdmlkZWQKCmFmdGVyIHRoaXMsIGZpbHRlcnMgYW5kIGNyaXRlcmlhIGNhbiBiZSBjb25jYXRlbmF0ZWQgdXNpbmcgYCZgPGJyPgpJbiB0aGlzIGNhc2UsIGNvdW50cnkgaXNvIGAmaXNvPTM2NGAsIHllYXIgYCZ5ZWFyPTIwMTlgLCBhZG1pbjEgYGFkbWluMT1UZWhyYW5gIGZvciBjaXR5CgpEZWZpbmUgZ2VuZXJhbCBwdXJwb3NlIGZ1bmN0aW9uCmBgYHtyfQojZGVmaW5lIGZ1bmN0aW9uLCBwYXJhbWV0ZXJzIHNhbWUgYXMgdGhvc2UgZm9yIGNsdXN0ZXJpbmcgZnVuY3Rpb24gaW4gMy40CmZfYWNsZWRfbWVyZ2UgPC0gZnVuY3Rpb24ocF9jaXR5X2ZpbHRlcixwX2RmLHBfZXBzLHBfayxwX291dGxpZXJfcmVtb3ZlID0gVFJVRSxwX2FjbGVkX3llYXIgPSBGQUxTRSxwX2FjbGVkX2NpdHkgPSBUUlVFKXsKICAKICAjZnVuY3Rpb24gZnJvbSAzLjQKICBmX2V4YWN0X291dHB1dCA8LSBmX2V4YWN0X2NpdHlfY2x1c3RlcihwX2NpdHlfZmlsdGVyID0gcF9jaXR5X2ZpbHRlciwKICAgICAgICAgICAgICAgICAgICAgICBwX2RmID0gcF9kZixwX2VwcyA9IHBfZXBzLAogICAgICAgICAgICAgICAgICAgICAgIHBfayA9IHBfaywKICAgICAgICAgICAgICAgICAgICAgICBwX291dGxpZXJfcmVtb3ZlID0gcF9vdXRsaWVyX3JlbW92ZSkKICAKICAjcmVsZXZhbnQgb3V0cHV0cyBmcm9tIGZfZXhhY3RfY2l0eV9jbHVzdGVyKCkgYXJlOiBkYXRhc2V0LCBjbHVzdGVyIGFuYWx5c2lzIHN0YXRzIGFuZCBtYXAgdG8gYWRkIHRvLgoKICAjIyNJU08KICBkaXN0aW5jdF9jb3VudHJ5X2lzbyA8LSBkaXN0aW5jdChmX2V4YWN0X291dHB1dCRmX2NsdXN0ZXJfZGYscG9zdF9jb3VudHJ5X2lzb19udW0pCiAgZGlzdGluY3RfY291bnRyeV9pc29fY2FsbCA8LSBwYXN0ZSgiJmlzbz0iLGRpc3RpbmN0X2NvdW50cnlfaXNvLHNlcD0iIikKICAjIyNZRUFSCiAgaWYocF9hY2xlZF95ZWFyKXsKICAgIGFjbGVkX3llYXIgPC0gcF9hY2xlZF95ZWFyIAogICAgYWNsZWRfeWVhcl9jYWxsIDwtIHBhc3RlKCImeWVhcj0iLGFjbGVkX3llYXIsc2VwPSIiKQogIH1lbHNlewogICAgYWNsZWRfeWVhcl9jYWxsIDwtICIiCiAgfQogICMjI0NJVFkKICBpZihwX2FjbGVkX2NpdHkpewogICAgY2l0eV9jYWxsIDwtIHBhc3RlKCImYWRtaW4xPSIscF9jaXR5X2ZpbHRlcixzZXA9IiIpCiAgfWVsc2V7CiAgICBjaXR5X2NhbGwgPC0gIiIKICB9CiAgCiAgIyMjQVBJCiAgYXBpX2Jhc2UgPC0iaHR0cHM6Ly9hcGkuYWNsZWRkYXRhLmNvbS9hY2xlZC9yZWFkP3Rlcm1zPWFjY2VwdCIKICAKICB1cmxfY2FsbCA8LSBwYXN0ZShhcGlfYmFzZSxkaXN0aW5jdF9jb3VudHJ5X2lzb19jYWxsLGFjbGVkX3llYXJfY2FsbCxjaXR5X2NhbGwsc2VwPSIiKQogIGFjbGVkX2RhdGEgPC0gZ2V0VVJMKHVybF9jYWxsKQogIGFjbGVkX2RmIDwtIGZyb21KU09OKGFjbGVkX2RhdGEpJGRhdGEgI2dldCBkYXRhIHJpZ2h0IGF3YXkKICBhY2xlZF9kZiA8LSB0eXBlX2NvbnZlcnQoYWNsZWRfZGYpCiAgYWNsZWRfZGYkcG9pbnRfc2l6ZSA8LSBhY2xlZF9kZiRmYXRhbGl0aWVzKzYKICBhY2xlZF9kZl9zZiA8LSBzdF9hc19zZihhY2xlZF9kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb29yZHM9YygibG9uZ2l0dWRlIiwibGF0aXR1ZGUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjcnMgPSA0MzI2LHJlbW92ZSA9IEZBTFNFKQogIAogIAogIGZfYWNsZWRfbWFwIDwtIGZfZXhhY3Rfb3V0cHV0JGZfY2x1c3Rlcl9tYXAgKyBtYXB2aWV3KGFjbGVkX2RmX3NmLAogICAgICBsZWdlbmQgPSBUUlVFLAogICAgICBsYXllci5uYW1lID0gIkluY2lkZW50cyAoYnkgdHlwZSkiLAogICAgICBsYWJlbCA9IGFjbGVkX2RmX3NmJGV2ZW50X3R5cGUsCiAgICAgIGx3ZCA9IDAuNSwKICAgICAgY29sb3IgPSAicmVkIiwKICAgICAgYWxwaGEucmVnaW9ucyA9IDAuNSwKICAgICAgI2NvbC5yZWdpb25zID0gYWNsZWRfZGZfc2YkZXZlbnRfdHlwZSwKICAgICAgY29sLnJlZ2lvbnMgPSAid2hpdGUiLAogICAgICAjemNvbCA9ICJldmVudF90eXBlIiwKICAgICAgY2V4ID0gInBvaW50X3NpemUiLAogICAgICBwb3B1cCA9IGxlYWZwb3A6OnBvcHVwVGFibGUoc3Vic2V0KGFjbGVkX2RmX3NmLCBzZWxlY3QgPSBjKGV2ZW50X2RhdGUsZXZlbnRfdHlwZSxzdWJfZXZlbnRfdHlwZSxmYXRhbGl0aWVzLHNvdXJjZV9zY2FsZSxzb3VyY2UpKSkKICAgICAgKQogICAgCiAgbWFwc2hvdChmX2FjbGVkX21hcCwgdXJsID0gcGFzdGUoIi9jbG91ZC9wcm9qZWN0L0RhdGEvTWFwRmlsZXMvIixwX2NpdHlfZmlsdGVyLCJBQ0xFRF9tYXAuaHRtbCIsc2VwPSJfIikpCiAgCiAgI3JldHVybiBsaXN0IG9mIHJlbGV2YW50IGVsZW1lbnRzCiAgcmV0dXJuX2xpc3QgPC0gbGlzdChmX2RhdGEgPSBmX2V4YWN0X291dHB1dCRmX2NsdXN0ZXJfZGYsCiAgICAgICAgICAgICAgICAgICAgICBmX21hcCA9IGZfYWNsZWRfbWFwLAogICAgICAgICAgICAgICAgICAgICAgZl9rX3Bsb3QgPSBmX2V4YWN0X291dHB1dCRrX3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICBmX2FjbGVkX2RhdGEgPSBhY2xlZF9kZl9zZiwKICAgICAgICAgICAgICAgICAgICAgIGZfYm94cGxvdCA9IGZfZXhhY3Rfb3V0cHV0JGJveF9wbG90KQogIAogICAgcmV0dXJuKHJldHVybl9saXN0KQp9CmBgYAo8YnI+CgpSdW4gZnVuY3Rpb24gYW5kIHNlZSBjb21wbGV0ZWQgZnVuY3Rpb24gcmVzdWx0cwpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CmZfYWNsZWRfcmVzdWx0cyA8LSBmX2FjbGVkX21lcmdlKAogICNzdGFuZGFyZCBwYXJhbWV0ZXJzCiAgcF9jaXR5X2ZpbHRlciA9ICJUZWhyYW4iLHBfZGYgPSBnZW9fbG93eV9ub19OQSxwX2VwcyA9IDAuMDE1LHBfayA9NCxwX291dGxpZXJfcmVtb3ZlID0gVFJVRSwKICAjYWNsZWQgcGFyYW1ldGVycwogIHBfYWNsZWRfeWVhciA9IDIwMTksCiAgcF9hY2xlZF9jaXR5ID0gVFJVRSkKZl9hY2xlZF9yZXN1bHRzJGZfbWFwCmBgYAoKCioqKgoKIyMjIDUuMCBBQ0xFRCBjYXNlcyB7I2hfNS4wX2Nhc2VzfQp8fCBbMC4wfFJFQUQgTUVdKCNoXzAuMF9yZWFkbWUpIHx8IFsxLjB8RVhUUkFDVF0oI2hfMS4wX2V4dHJhY3QpIHx8IFsyLjB8VFJBTlNGT1JNXSgjaF8yLjBfdHJhbnNmb3JtKSB8fCBbMy4wfExPQUQgYW5kIEFOQUxZU0VdKCNoXzMuMF9hbmFseXNlKSB8fCBbNC4wfEFDTEVEIGRhdGFdKCNoXzQuMF9hY2xlZCkgfHwgWyoqNS4wfEFDTEVEIGNhc2VzKipdKCNoXzUuMF9jYXNlcykgfHw8YnI+Cgo+ICoqUHVycG9zZSBvZiBzZWN0aW9uKio6IHJ1biB0d28gY2FzZSBzdHVkaWVzIHVzaW5nIGZ1bmN0aW9uIGZyb20gc2VjdGlvbiA0IDxicj4KPiAqKklucHV0cyoqOiBgZl9hY2xlZF9tZXJnZSgpYCA8YnI+Cj4gKipPdXRwdXRzKio6IG1hcHMgYW5kIHBsb3RzIHRvIGFzc2VzcyBjbHVzdGVyaW5nCgoqKioKSW4gdGhpcyBzZWN0aW9uLCBkaWZmZXJlbnQgY2l0aWVzIGFyZSBleGFtaW5lZCBmb3IgcXVhbGl0YXRpdmUgYXNzZXNzbWVudCBpbiB0aGUgZmluYWwgYXNzZXNzbWVudC4gRGVzZWxlY3Qgb3V0bGllciBsYXllciBhbmQgY2l0eSBsYXllciBhbmQgZm9jdXMgaW4gb24g4oCYZXhhY3QgZW1iYXNzeSBsb2NhdGlvbnPigJkgbGF5ZXIuPGJyPgoKIyMjIyA1LjEgfCBDYXNlIDEgQ2Fpcm8geyNoXzUuMV9jYXNlfQpbQmFjayB0byBpbmRleF0oI2hfMC4yX2luZGV4KTxicj4KCmBgYHtyIGVjaG8gPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyMjQ0FJUk8KCmNhaXJvX3Jlc3VsdHMgPC0gZl9hY2xlZF9tZXJnZSgKICAjc3RhbmRhcmQgcGFyYW1ldGVycwogIHBfY2l0eV9maWx0ZXIgPSAiQ2Fpcm8iLHBfZGYgPSBnZW9fbG93eV9ub19OQSxwX2VwcyA9IDAuMDAzLHBfayA9NCxwX291dGxpZXJfcmVtb3ZlID0gVFJVRSwKICAjYWNsZWQgcGFyYW1ldGVycwogIHBfYWNsZWRfeWVhciA9IDIwMTksCiAgcF9hY2xlZF9jaXR5ID0gVFJVRSkKCmNhaXJvX3Jlc3VsdHMkZl9tYXAKYGBgCgojIyMjIDUuMiB8IENhc2UgMiBBbmthcmEgeyNoXzUuMl9jYXNlfQpbQmFjayB0byBpbmRleF0oI2hfMC4yX2luZGV4KQpgYGB7ciBlY2hvID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CiMjI0FOS0FSQQoKYW5rYXJhX3Jlc3VsdHMgPC0gZl9hY2xlZF9tZXJnZSgKICAjc3RhbmRhcmQgcGFyYW1ldGVycwogIHBfY2l0eV9maWx0ZXIgPSAiQW5rYXJhIixwX2RmID0gZ2VvX2xvd3lfbm9fTkEscF9lcHMgPSAwLjAwMyxwX2sgPTQscF9vdXRsaWVyX3JlbW92ZSA9IFRSVUUsCiAgI2FjbGVkIHBhcmFtZXRlcnMKICBwX2FjbGVkX3llYXIgPSAyMDE5LAogIHBfYWNsZWRfY2l0eSA9IFRSVUUpCgphbmthcmFfcmVzdWx0cyRmX21hcApgYGAKCioqKgoKIyMjIyBSRUZFUkVOQ0UgQ09ERSBGVU5DVElPTlMgeyNyZWZfY29kZX0KW0JhY2sgdG8gaW5kZXhdKCNoXzAuMl9pbmRleCkKCkNhbGN1bGF0ZSBnbG9iYWwgcG9wdWxhdGlvbiBhbmQgZ2xvYmFsIGVjb25vbXkgcHJvcG9ydGlvbnMgZm9yIHRoaXMgZGF0YXNldC4KYGBge3IgaW5jbHVkZT1GQUxTRX0KbG93eV9tZXRhX2RmIDwtIHJlYWRfY3N2KCIuL0RhdGEvUHJvY2Vzc2luZy9QYXJ0XzFfT3V0X0xvd3lfTWV0YS5jc3YiKQpgYGAKCmBgYHtyfQojRUNPTk9NWQplY29ub215X3N1bW1hcnkgPC0gZGlzdGluY3QobG93eV9tZXRhX2RmLHNlbmRfY291bnRyeSxtZXRhX2dkcF91c2RfYmlsbGlvbnMpCmVjb25vbXlfc3VtbWFyeV9zdW0gPC0gc3VtKGVjb25vbXlfc3VtbWFyeSRtZXRhX2dkcF91c2RfYmlsbGlvbnMpCmVjb25vbXlfMjAxOSA8LSA4NS45MSoxMDAwCmVjb25vbXlfY292ZXJhZ2UgPC0gZWNvbm9teV9zdW1tYXJ5X3N1bS9lY29ub215XzIwMTkKCiMjI1BPUFVMQVRJT04KcGVvcGxlX3N1bW1hcnkgPC0gZGlzdGluY3QobG93eV9tZXRhX2RmLHNlbmRfY291bnRyeSxtZXRhX3BvcHVsYXRpb25fbWlsbGlvbnMpCnBlb3BsZV9zdW1tYXJ5X3N1bSA8LSBzdW0ocGVvcGxlX3N1bW1hcnkkbWV0YV9wb3B1bGF0aW9uX21pbGxpb25zKQpwZW9wbGVfMjAxOSA8LSA3LjU5NCoxMDAwCnBlb3BsZV9jb3ZlcmFnZSA8LSBwZW9wbGVfc3VtbWFyeV9zdW0vcGVvcGxlXzIwMTkKCmNhdCgiQ292ZXJhZ2Ugb2YgdGhlIHdvcmxkIGVjb25vbXkgaXM6IixlY29ub215X2NvdmVyYWdlKjEwMCwiJSIsIlxuQ292ZXJhZ2Ugb2YgZ2xvYmFsIHBvcHVsYXRpb24gaXM6IixwZW9wbGVfY292ZXJhZ2UqMTAwLCIlIikKCgojZWNvbm9teSBmcm9tOiBodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy9pbmRpY2F0b3IvbnkuZ2RwLm1rdHAuY2QKI3Blb3BsZSBmcm9tOiBodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy9pbmRpY2F0b3Ivc3AucG9wLnRvdGwgCiM4NS45MSBHRFAgaW4gY3VycmVudCBkb2xsYXJzIGluIHRob3VzYW5kcyBvZiBiaWxsaW9ucyBmb3IgMjAxOAojNy41OTR0aG91c2FuZCBtaWxsaW9uIHBvcHVsYXRpb24gaW4gdGhlIHdvcmxkIGluIDIwMTgKYGBgCgoKCgpgYGB7ciBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpwcmludCgiU3VjY2VzZnVsbHkgcmFuIGFsbCBjb2RlLCB5YXkgOikiKQpgYGAKCgo=