Introduction

Background

This is my first data visualization project that is intended to reproduce economist plot from this article Political Science. I will use one of the most used visualization package in R which is ggplot2. First of all, let’s take a look at the economist plot that I’m talking about:

knitr::include_graphics("plot.png")

Well, it seems a bit complicated at first, but I’m trying to do my best to resemble it. This plot can give us insight that Twitter social media may reflect political polarization especially for Senate Republicans and Democrats. While following the science-related accounts are low overall, we can see that Senate Democrats three times more likely to follow science-related Twitter handles than Republicans. In other word, there is a high chance that Democrate will conduct policy based on scientific knowledge or clear evidence that may lead to a better life in the future.

Import Packages

Let’s import some packages that we’ll be using throughout the recreating plot process, we will get into the more detail explanations once we get to the implementation of these packages

library(dplyr) # data wrangling
library(reshape2) # data wrangling specifically for reshaping the structure
library(showtext) # deal with external fonts
library(ggplot2) # data visualization
library(ggthemes) # visualization theme for ggplot
library(scales) # Scaling function for visualization
library(grid) # Adding graphical object to the plot

The Data

The website mentioned above, does not tell us explicitly where is the date come from which they use for creating plot. Fortunately, we can try to search the article that they was referring to and I finally found the data that we can use. Here are some of the data the might be useful for our purpose

data1 <- read.csv("neu_m0404q02m.csv")
data2 <- read.csv("neu_m0404q15p.csv")
data3 <- read.csv("neu_m0404z286.csv")
data4 <- read.csv("politic_science.csv")
data5 <- read.csv("S114_members.csv")

Description of Data

Based on that article, the data is meant to be analysed and checked if there is a correlation between follows of science-related twitter accounts by U.S Senate and recent votes on a series of amendments. This behavior will define the “sense of the Senate” on global climate change (GCC). Basically, we can quickly see from the plot above that science-related twitter accounts can reflect polarization among the Senate Democrats and Republicans which belong to distinct group. Senate Democrats are more likely to follow science-related twitter accounts than Republicans. Since our goal is to only create the plot, I will not explain more about the article. If you are interested, you can check it from this link.

Actually, after inspecting some of those datasets, I realize that only the data4 and data5 will be used for creating plot. But, I will still explain what are those five data in just a simple way. Below are the brief description about each of our data:

  • data1: Column 1 shows the Senator’s name, and all other columns are Twitter handles followed by at least one Senator. A 1 Indicates that the Senator’s Twitter account follows that Twitter handle.
  • data2: A square matrix that which US Senators follow each other on Twitter (1: follow, 0: no follow).
  • data3: Table containing 78,000+ twitter handles followed by U.S. Senators as of February 2015. Handles are grouped in to Science (based on focus of organization), Politics, Media, and Other.
  • data4: Total number of Twitter accounts followed by each Senator and proportion categorized as Science, Politics, Media or Other.
  • data5: Table of DW-Nominate measure/score for each Senate. I actually don’t really know how are these scores calculated.

Data Exploration

Remember that I’m only going to use data4 and data5 for creating the plot, but I think we can still utilize other data for a quick analysis. Say we want to analyse which top 5 senates that follow science-related Twitter accounts the most.

First, we take the data3 and select only for science category

science <- data3[data3$Category=="Science",]
glimpse(science)
Rows: 77
Columns: 2
$ Category       <chr> "Science", "Science", "Science", "Science", "Science", "Scie…
$ Twitter.Handle <chr> "AGUSciPolicy", "MetcalfURI", "OGJOnline", "RANDCorporation"…

We can see that there are 77 science-related twitter accounts.

Next, we want to check whether Senates are following those accounts using data1. Let’s see what does data1 look like

head(data1)

It contains a lot of columns(twitter accounts) even though we only need accounts that are science-related so we want to select only those valid columns with these steps:

  1. Initiate an empty vector for valid columns
  2. Use for loop to iterate through all columns from data1
  3. Check if the columns are included in the science dataframe
  4. Store that valid columns into our empty vector.
valid_col <- c()
for(i in 2:length(colnames(data1))) {
  if(colnames(data1)[i] %in% science$Twitter.Handle){
    valid_col <- append(valid_col, colnames(data1)[i])
  } 
}
length(valid_col)
[1] 75

After that, we can subset data1 which contain only those valid columns

science_tweet <- data1[, append(valid_col, "X",0)]
head(science_tweet)

Now, we will use reshape2 package to restructure the science_tweet dataframe and get the answer that we desire. We will divide that into these steps:

  1. Use melt() function to transform all columns into only one column.
  2. Create frequency table with table() function to see how many science related-accounts that are followed by each Senate. It will be shown as the Frequency column.
  3. Use arrange function to order the frequency from the highest to the lowest to see the top 5.
# melt the twitter account columns.
melted <- melt(science_tweet,"X") 
senate_science <- data.frame(table(melted$X,melted$value))
colnames(senate_science) <- c("Name","isScience","Frequency")
senate_science <- senate_science[senate_science$isScience==1,]


# sort the data based on frequency to check the top 5
senate_science %>% 
  arrange(-Frequency) %>% 
  head(5)

Now, let’s move to the main objective which is recreating the economist plot. Recall that we will only use data4 and data5 so let’s inspect them.

# See the structure
glimpse(data4)
Rows: 89
Columns: 7
$ Last.Name <chr> "Alexander", "Ayotte", "Barrasso", "Bennet", "Blumenthal", "Blunt…
$ Party     <chr> "R", "R", "R", "D", "D", "R", "R", "D", "D", "R", "D", "R", "D", …
$ Science   <dbl> 0.000, 0.002, 0.004, 0.024, 0.014, 0.000, 0.006, 0.026, 0.023, 0.…
$ Politics  <dbl> 0.154, 0.054, 0.156, 0.337, 0.086, 0.009, 0.064, 0.553, 0.636, 0.…
$ Media     <dbl> 0.076, 0.015, 0.081, 0.000, 0.034, 0.004, 0.013, 0.009, 0.000, 0.…
$ Other     <dbl> 0.770, 0.928, 0.759, 0.639, 0.866, 0.987, 0.917, 0.412, 0.341, 0.…
$ Total     <int> 409, 1492, 456, 83, 801, 6799, 543, 114, 44, 3358, 579, 935, 263,…
glimpse(data5)
Rows: 101
Columns: 22
$ congress                      <int> 114, 114, 114, 114, 114, 114, 114, 114, 114, …
$ chamber                       <chr> "President", "Senate", "Senate", "Senate", "S…
$ icpsr                         <int> 99911, 49700, 94659, 40300, 41500, 15039, 201…
$ state_icpsr                   <int> 99, 41, 41, 81, 81, 61, 61, 42, 42, 71, 71, 6…
$ district_code                 <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ state_abbrev                  <chr> "USA", "AL", "AL", "AK", "AK", "AZ", "AZ", "A…
$ party_code                    <int> 100, 200, 200, 200, 200, 200, 200, 200, 200, …
$ occupancy                     <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ last_means                    <int> 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ bioname                       <chr> "OBAMA, Barack", "SESSIONS, Jefferson Beaureg…
$ bioguide_id                   <chr> "O000167", "S001141", "S000320", "M001153", "…
$ born                          <int> 1961, 1946, 1934, 1957, 1964, 1936, 1962, 195…
$ died                          <dbl> NA, NA, NA, NA, NA, 2018, NA, NA, NA, NA, NA,…
$ nominate_dim1                 <dbl> -0.358, 0.549, 0.448, 0.210, 0.481, 0.381, 0.…
$ nominate_dim2                 <dbl> -0.197, 0.130, 0.575, -0.302, 0.068, -0.626, …
$ nominate_log_likelihood       <dbl> NA, -109.63029, -97.49168, -134.57023, -101.4…
$ nominate_geo_mean_probability <dbl> NA, 0.76537, 0.78512, 0.72192, 0.78169, 0.749…
$ nominate_number_of_votes      <int> NA, 410, 403, 413, 412, 410, 407, 418, 410, 3…
$ nominate_number_of_errors     <int> NA, 46, 42, 65, 35, 59, 70, 24, 27, 22, 30, 4…
$ conditional                   <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ nokken_poole_dim1             <dbl> NA, 0.635, 0.587, 0.299, 0.460, 0.464, 0.686,…
$ nokken_poole_dim2             <dbl> NA, 0.200, 0.561, -0.253, 0.144, -0.312, -0.7…
head(data4)
head(data5)

From the data5, we only care about the nominate_dim1 column since this is going to be our x-axis value.

Data Cleaning

We can see from the result above that there is a different amount of row between data4 and data5, so we need to match both dataframe so they contain the exact same Senate. Here, I use grepl() function to match the Last.Name column from data4 to bioname column from data5. grepl() function has two main parameters:

  • pattern: characters or patterns that need to be matched
  • x: columns that we want to check whether the patterns are found or not
# conditional subsetting using subset with matching condition
nominate <- subset(data5, grepl(paste(data4$Last.Name, collapse="|"), data5$bioname, ignore.case = T), select = c("bioname","nominate_dim1"))
head(nominate)

Remember that the data4 contains only the last name, whereas the data5 contains full name. Here I want to split the fullname and take only the last name. We can see from the bioname column, the last nama is located before comma, so we can try to extract it and make a new column containing that last name using below code. We can use strsplit() function to split the characters and we can use sapply() to apply that function for bioname column

# Use strsplit to split the bioname based on comma and only take the last name
nominate_clean <- nominate %>% 
  mutate(name = sapply(strsplit(nominate$bioname, ","), head, 1))
head(nominate_clean)

Next, we want to match the exact character regardless of what case is being used(upper or lower), so we lowercase both column with tolower() function.

# convert both column name to lower case
data4$Last.Name <- tolower(data4$Last.Name)
nominate_clean$name <- tolower(nominate_clean$name)

Finally, we will join these two dataframes into one dataframe so we can use it for our plotting. We can use left_join() from dplyr package to join two dataframes based on matched column. The concept of join here is similar to what we have in SQL join.

Recall that from the reference plot that we have, the y axis is in the form of percentage so we need to multiply that with 100. Furthermore, we also want to match the point color in our plot. The color is based on the Party column so we can convert that column as factor with defined levels as (“D”,“R”,“I”).

# join two dataframes based on the matching names
clean_data <- data4 %>% 
  left_join(nominate_clean, by = c("Last.Name" = "name"))

# multiply science columns with 100
clean_data$Science <- clean_data$Science*100

# Convert the Party column into factor with defined levels
clean_data$Party <- factor(clean_data$Party, levels = c("D","R","I"))
head(clean_data)

Before I create the plot, I want to exclude senates who don’t actually follow science-related account and take the senates who follow less than 7000 account. The decision was based on what I have seen from the real plot.

clean_data <- clean_data[clean_data$Total <= 7000 ,]
clean_data <- clean_data[clean_data$Science > 0,]

I try to reproduce the exact same plot even the font style, so I found some fonts online that hopefully would help us making our plot looks ‘economist’. In order to load those fonts, we need to use showtext package that we’ve already imported previously

# Add and load the external fonts
font.add("ITC Officina Book", "itc-officina-sans-std-book.otf")
font.add("ITC Officina Book Italic", "itc-officina-sans-std-book-italic.otf")
font.add("ITC Officina Bold","itc-officina-sans-std-bold.otf")
font.add("ITC Officina Bold Italic", "itc-officina-sans-std-bold-italic.otf")
showtext_auto()

Next, we want to only visualize certain senates names based on the real plot

# Take only certain senates
text_label <- c("markey","reid","cochran","sanders","schatz","vitter","inhofe")
data_label <- clean_data[clean_data$Last.Name %in% text_label,]

# Create new columns for senates full name that we want to show in our plot
data_label$Full.Name <- c("Thad Cochran", "Jim Inhofe","Ed Markey","Harry Reid","Bernie Sanders", "Brian Schatz","David Vitter" )

Recreating the Plot

Before we code the actual plot, we want to set the theme for our plot as similar as possible to our reference. This actually requires several attempts, so I am sorry if the final result does not satisfy you.

theme_pol <- theme(
    # customize the plot title
    plot.title = element_text(size=14, family="ITC Officina Bold", 
                              color="black",
                              hjust=-0.075, 
                              vjust = -5),
    # Customize the plot subtitle
    plot.subtitle = element_text(size=10, family="ITC Officina Book", 
                                 color="black", 
                                 hjust = -0.155, 
                                 vjust = -8.2),
    # Customize the plot grid
    panel.border = element_blank(),
    panel.grid.major.x= element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.minor.y = element_blank(),
    panel.grid.major.y = element_blank(),
    
    # Customize plot background
    plot.background = element_rect(fill = "#FFFFFF"),
    panel.background = element_rect(fill = "#FFFFFF"),
    
    # Customize plot axis
    axis.title.y = element_text(size = 10, 
                                family="ITC Officina Book Italic", 
                                colour = "black", 
                                hjust = -1.3),
    axis.title.x = element_text(size = 10, 
                                family="ITC Officina Bold Italic", 
                                colour = "#696969"),
    axis.text = element_text(size=10, 
                             family = "ITC Officina Book", 
                             color="black"),
    axis.ticks.length.x = unit(-0.2, "lines"),
    
    # Customize plot legend
    legend.position = c(-0.07,0.88),
    legend.direction = "horizontal",
    legend.justification = "left",
    legend.text = element_text(size=10, 
                               family = "ITC Officina Book", 
                               color="black"),
    legend.title = element_text(size=10, 
                                family="ITC Officina Bold", 
                                color="black", 
                                margin = margin(r=8)),
    legend.background = element_blank(),
    legend.key=element_blank(),
    legend.spacing.x = unit(c(-0.1), "cm")
  ) 

ggplot2 is a package for declaratively creating graphics, based on The Grammar of Graphics that allows us to compose graph by combining independent components. Every ggplot2 plot has three key components:

  1. Data
  2. A set of aesthetic mappings between variables in the data and visual properties
  3. At least one layer which describes how to render each observation. Layers are usually created with a geom function.

The steps for creating ggplot graph is really straightforward:

  1. Initiate the ggplot object using ggplot(). The function has two main parameters which are data and mapping.
  2. Adding object layer that we want to be shown by our plot. It can be any geom object. We can also add more than one layer if we want to.
  3. Adding layers for plot customization.

Our plot includes point and text object, so let’s do that using geom_point() and geom_text() function.

gg <- ggplot(data = clean_data, aes(x=nominate_dim1,y=Science)) + 
  # Adding points to our plot
  geom_point(aes(color=Party, size=Total), alpha=0.5) +
  
  # Adding required senate names represented by text object
  geom_text(
    data=data_label ,
    aes(label=Full.Name,color=Party), 
    nudge_x = 0.15,
    nudge_y = 0.2,
    family = "ITC Officina Bold Italic",
    show.legend = F
  )

We also need to specify the plot title, axis, scale, and legend so it matches the actual plot.

gg <- 
  gg +
  
  # Customize axis labels
  scale_x_continuous(labels = c("1.0","0.5","0","0.5","1.0")) +
  scale_y_continuous(trans=log2_trans(),breaks=c(0.5,1.0,2.0,4.0,6.0,8.0,10.0)) +
  
  # Customize the points size
  scale_size(range = c(1,20), guide="none") +
  
  # Customize the points color. This is why converting data types into factor with defined levels comes in handy.
  scale_color_manual(
    values = c("#8dd4f0","#f48872","#4f9588"),
    guide = guide_legend(title="Party affiliation:"), 
            labels= c("D"="Democrats","R"="Republicans","I"="Independents")               
  ) +
  coord_cartesian(xlim=c(-1,1), clip = 'off') +
  labs(
    title = "Political Science", 
    subtitle = "United States senator's political ideology and scientific engagement on Twitter*",
    x = "Liberal-Conservative Score",
    y = "Science-related Twitter accounts followed, % of total, natural log scale"
  ) +
  theme_bw() 

Next,we want to create additional text and the minus(-) plus(+) sign for for the x-axis using grid package. I actually tried to find the most efficient way, and end up finding this approach

# use grobTree to add customized text into our plot
grob = grobTree(textGrob("February 2015", x=-0.06, y=1, hjust=0, vjust=4, gp=gpar(fontsize=8, fontfamily="ITC Officina Book", col="black")))
grob2 = grobTree(textGrob("-                               +", x=0.37, y=-0.04, hjust=0, vjust=0.4, gp=gpar(fontsize=15, fontfamily="ITC Officina Book", col="black")))
grob3 = grobTree(textGrob("MORE LIBERAL", x=0.05, y=1, hjust=0, vjust=56, gp=gpar(fontsize=8, fontfamily="ITC Officina Bold Italic", col="#8dd4f0")))
grob4 = grobTree(textGrob("MORE CONSERVATIVE", x=0.8, y=1, hjust=0, vjust=56, gp=gpar(fontsize=8, fontfamily="ITC Officina Bold Italic", col="#4f9588")))

Now, we should create the coecentric circles instead of the native legend. I don’t know how to customize that if I only use the basic native legend provided by ggplot, so I found that this way would be an option. Besides that, we also need to create customized grid since as we can see from the actual plot the two grids at the top are cut. This will be divided into several steps:

  1. Create dataframe that contains labels, size of each circle, and the position of both circles and labels.
  2. Create dataframe for the dash line that we have in our legend.
  3. Create the legend by using geom_point() for circles and geom_text() for the labels
  4. We also have one additional step to create customized y-axis grid because as we can see from the reference plot, the first three grids are cut in order to not overlapping the legend
# Create dataframe for circle legend
legend_bbl <- data.frame(label=c("250","1,000","5,000"),
                         size=c(250,1000,5000),
                         pos_bbl=c(8.7,9.5,12),  #c(7.7,8.5,11),
                         pos_txt=c(8.9,12.9,21))
# Create dataframe for y-axis grid
line_segment <- data.frame(xend=c(0.7,0.7,1,1,1,1,1,1),
                           y=c(10,8,6,4,2,1,0.5,0)
                           )
dash_segment <- data.frame(y=c(9.8,11.9,19.2))

After we prepare the data for creating customized legend, we can add that into our plot

final <- gg +
  # Circle legend
  geom_point(
    data=legend_bbl,
    aes(x=0.84, y=pos_bbl, size=size),
    shape = 21, color = "black", fill = "#e1e3e5",alpha=0.5
  )+
  # Label for the legend
  geom_text(
    data=legend_bbl,
    aes(x=1,y=pos_txt,label=label),
    hjust=1,
    size=3
  )+
  
  # Create customized y-axis grid
  geom_segment(data=line_segment,
               aes(x=-1.1,xend=xend,y=y,yend=y),color="grey66")+
  
  # Create dash line in our legend
  geom_segment(data = dash_segment,
               aes(x=0.83, xend=1, y=y,yend=y),linetype="dotted") +
  
  # adding our grobText object with annotation_custom
  annotation_custom(grob) +
  annotation_custom(grob2) +
  annotation_custom(grob3) +
  annotation_custom(grob4) +

  annotate("text", x = 0.75 , y = 30, label = "Twitter accounts \nfollowed, total", fontface = "bold", family = "ITC Officina Bold",lineheight = 0.6, hjust=0 ) +
  theme_pol
  

final

I know that the result does not exactly look like the original one, probably because there is a little difference between the dataset that I use from the economist plot reference. However, at least we have got a quite similar plot even though it still far from perfect.

References

LS0tDQp0aXRsZTogIlJlcHJvZHVjaW5nICdQb2xpdGljYWwgU2NpZW5jZScgRWNvbm9taXN0IFBsb3QiDQphdXRob3I6ICJKYWZhciINCmRhdGU6ICIyMDIyLTEwLTA3Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGhpZ2hsaWdodDogemVuYnVybg0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAyDQogICAgdG9jX2Zsb2F0Og0KICAgICAgY29sbGFwc2VkOiBubw0KICAgIGRmX3ByaW50OiBwYWdlZA0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjbGVhci11cCB0aGUgZW52aXJvbm1lbnQNCnJtKGxpc3QgPSBscygpKQ0KDQojIGNodW5rIG9wdGlvbnMNCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgbWVzc2FnZSA9IEZBTFNFLA0KICB3YXJuaW5nID0gRkFMU0UsDQogIGZpZy5hbGlnbiA9ICJjZW50ZXIiLA0KICBjb21tZW50ID0gIiM+IiwNCiAgY2FjaGUgPSBUDQopDQoNCm9wdGlvbnMoc2NpcGVuID0gOTk5KQ0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQojIyBCYWNrZ3JvdW5kDQpUaGlzIGlzIG15IGZpcnN0IGRhdGEgdmlzdWFsaXphdGlvbiBwcm9qZWN0IHRoYXQgaXMgaW50ZW5kZWQgdG8gcmVwcm9kdWNlIGVjb25vbWlzdCBwbG90IGZyb20gdGhpcyBhcnRpY2xlIFtQb2xpdGljYWwgU2NpZW5jZV0oaHR0cHM6Ly93d3cuZWNvbm9taXN0LmNvbS9ncmFwaGljLWRldGFpbC8yMDE2LzA2LzEwL3RoZS1zZW5hdGVzLXNjaWVudGlmaWMtZGl2aWRlKS4gSSB3aWxsIHVzZSBvbmUgb2YgdGhlIG1vc3QgdXNlZCB2aXN1YWxpemF0aW9uIHBhY2thZ2UgaW4gUiB3aGljaCBpcyBgZ2dwbG90MmAuIEZpcnN0IG9mIGFsbCwgbGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhlIGVjb25vbWlzdCBwbG90IHRoYXQgSSdtIHRhbGtpbmcgYWJvdXQ6DQoNCmBgYHtyfQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoInBsb3QucG5nIikNCmBgYA0KV2VsbCwgaXQgc2VlbXMgYSBiaXQgY29tcGxpY2F0ZWQgYXQgZmlyc3QsIGJ1dCBJJ20gdHJ5aW5nIHRvIGRvIG15IGJlc3QgdG8gcmVzZW1ibGUgaXQuIFRoaXMgcGxvdCBjYW4gZ2l2ZSB1cyBpbnNpZ2h0IHRoYXQgVHdpdHRlciBzb2NpYWwgbWVkaWEgbWF5IHJlZmxlY3QgcG9saXRpY2FsIHBvbGFyaXphdGlvbiBlc3BlY2lhbGx5IGZvciBTZW5hdGUgUmVwdWJsaWNhbnMgYW5kIERlbW9jcmF0cy4gV2hpbGUgZm9sbG93aW5nIHRoZSBzY2llbmNlLXJlbGF0ZWQgYWNjb3VudHMgYXJlIGxvdyBvdmVyYWxsLCB3ZSBjYW4gc2VlIHRoYXQgU2VuYXRlIERlbW9jcmF0cyB0aHJlZSB0aW1lcyBtb3JlIGxpa2VseSB0byBmb2xsb3cgc2NpZW5jZS1yZWxhdGVkIFR3aXR0ZXIgaGFuZGxlcyB0aGFuIFJlcHVibGljYW5zLiBJbiBvdGhlciB3b3JkLCB0aGVyZSBpcyBhIGhpZ2ggY2hhbmNlIHRoYXQgRGVtb2NyYXRlIHdpbGwgY29uZHVjdCBwb2xpY3kgYmFzZWQgb24gc2NpZW50aWZpYyBrbm93bGVkZ2Ugb3IgY2xlYXIgZXZpZGVuY2UgdGhhdCBtYXkgbGVhZCB0byBhIGJldHRlciBsaWZlIGluIHRoZSBmdXR1cmUuDQoNCg0KIyMgSW1wb3J0IFBhY2thZ2VzDQpMZXQncyBpbXBvcnQgc29tZSBwYWNrYWdlcyB0aGF0IHdlJ2xsIGJlIHVzaW5nIHRocm91Z2hvdXQgdGhlIHJlY3JlYXRpbmcgcGxvdCBwcm9jZXNzLCB3ZSB3aWxsIGdldCBpbnRvIHRoZSBtb3JlIGRldGFpbCBleHBsYW5hdGlvbnMgb25jZSB3ZSBnZXQgdG8gdGhlIGltcGxlbWVudGF0aW9uIG9mIHRoZXNlIHBhY2thZ2VzDQoNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikgIyBkYXRhIHdyYW5nbGluZw0KbGlicmFyeShyZXNoYXBlMikgIyBkYXRhIHdyYW5nbGluZyBzcGVjaWZpY2FsbHkgZm9yIHJlc2hhcGluZyB0aGUgc3RydWN0dXJlDQpsaWJyYXJ5KHNob3d0ZXh0KSAjIGRlYWwgd2l0aCBleHRlcm5hbCBmb250cw0KbGlicmFyeShnZ3Bsb3QyKSAjIGRhdGEgdmlzdWFsaXphdGlvbg0KbGlicmFyeShnZ3RoZW1lcykgIyB2aXN1YWxpemF0aW9uIHRoZW1lIGZvciBnZ3Bsb3QNCmxpYnJhcnkoc2NhbGVzKSAjIFNjYWxpbmcgZnVuY3Rpb24gZm9yIHZpc3VhbGl6YXRpb24NCmxpYnJhcnkoZ3JpZCkgIyBBZGRpbmcgZ3JhcGhpY2FsIG9iamVjdCB0byB0aGUgcGxvdA0KYGBgDQoNCiMgVGhlIERhdGENClRoZSB3ZWJzaXRlIG1lbnRpb25lZCBhYm92ZSwgZG9lcyBub3QgdGVsbCB1cyBleHBsaWNpdGx5IHdoZXJlIGlzIHRoZSBkYXRlIGNvbWUgZnJvbSB3aGljaCB0aGV5IHVzZSBmb3IgY3JlYXRpbmcgcGxvdC4gRm9ydHVuYXRlbHksIHdlIGNhbiB0cnkgdG8gc2VhcmNoIHRoZSBhcnRpY2xlIHRoYXQgdGhleSB3YXMgcmVmZXJyaW5nIHRvIGFuZCBJIGZpbmFsbHkgZm91bmQgdGhlIGRhdGEgdGhhdCB3ZSBjYW4gdXNlLiBIZXJlIGFyZSBzb21lIG9mIHRoZSBkYXRhIHRoZSBtaWdodCBiZSB1c2VmdWwgZm9yIG91ciBwdXJwb3NlDQpgYGB7cn0NCmRhdGExIDwtIHJlYWQuY3N2KCJuZXVfbTA0MDRxMDJtLmNzdiIpDQpkYXRhMiA8LSByZWFkLmNzdigibmV1X20wNDA0cTE1cC5jc3YiKQ0KZGF0YTMgPC0gcmVhZC5jc3YoIm5ldV9tMDQwNHoyODYuY3N2IikNCmRhdGE0IDwtIHJlYWQuY3N2KCJwb2xpdGljX3NjaWVuY2UuY3N2IikNCmRhdGE1IDwtIHJlYWQuY3N2KCJTMTE0X21lbWJlcnMuY3N2IikNCmBgYA0KDQoNCiMjIERlc2NyaXB0aW9uIG9mIERhdGENCkJhc2VkIG9uIHRoYXQgYXJ0aWNsZSwgdGhlIGRhdGEgaXMgbWVhbnQgdG8gYmUgYW5hbHlzZWQgYW5kIGNoZWNrZWQgaWYgdGhlcmUgaXMgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIGZvbGxvd3Mgb2Ygc2NpZW5jZS1yZWxhdGVkIHR3aXR0ZXIgYWNjb3VudHMgYnkgVS5TIFNlbmF0ZSBhbmQgcmVjZW50IHZvdGVzIG9uIGEgc2VyaWVzIG9mIGFtZW5kbWVudHMuIFRoaXMgYmVoYXZpb3Igd2lsbCBkZWZpbmUgdGhlICJzZW5zZSBvZiB0aGUgU2VuYXRlIiBvbiBnbG9iYWwgY2xpbWF0ZSBjaGFuZ2UgKEdDQykuIEJhc2ljYWxseSwgd2UgY2FuIHF1aWNrbHkgc2VlIGZyb20gdGhlIHBsb3QgYWJvdmUgdGhhdCBzY2llbmNlLXJlbGF0ZWQgdHdpdHRlciBhY2NvdW50cyBjYW4gcmVmbGVjdCBwb2xhcml6YXRpb24gYW1vbmcgdGhlIFNlbmF0ZSBEZW1vY3JhdHMgYW5kIFJlcHVibGljYW5zIHdoaWNoIGJlbG9uZyB0byBkaXN0aW5jdCBncm91cC4gU2VuYXRlIERlbW9jcmF0cyBhcmUgbW9yZSBsaWtlbHkgdG8gZm9sbG93IHNjaWVuY2UtcmVsYXRlZCB0d2l0dGVyIGFjY291bnRzIHRoYW4gUmVwdWJsaWNhbnMuIFNpbmNlIG91ciBnb2FsIGlzIHRvIG9ubHkgY3JlYXRlIHRoZSBwbG90LCBJIHdpbGwgbm90IGV4cGxhaW4gbW9yZSBhYm91dCB0aGUgYXJ0aWNsZS4gSWYgeW91IGFyZSBpbnRlcmVzdGVkLCB5b3UgY2FuIGNoZWNrIGl0IGZyb20gdGhpcyBbbGlua10oaHR0cHM6Ly9jbGltYXRlY2hhbmdlcmVzcG9uc2VzLmJpb21lZGNlbnRyYWwuY29tL2FydGljbGVzLzEwLjExODYvczQwNjY1LTAxNi0wMDE4LXopLg0KDQpBY3R1YWxseSwgYWZ0ZXIgaW5zcGVjdGluZyBzb21lIG9mIHRob3NlIGRhdGFzZXRzLCBJIHJlYWxpemUgdGhhdCBvbmx5IHRoZSBkYXRhNCBhbmQgZGF0YTUgd2lsbCBiZSB1c2VkIGZvciBjcmVhdGluZyBwbG90LiBCdXQsIEkgd2lsbCBzdGlsbCBleHBsYWluIHdoYXQgYXJlIHRob3NlIGZpdmUgZGF0YSBpbiBqdXN0IGEgc2ltcGxlIHdheS4gQmVsb3cgYXJlIHRoZSBicmllZiBkZXNjcmlwdGlvbiBhYm91dCBlYWNoIG9mIG91ciBkYXRhOg0KDQotIGBkYXRhMWA6IENvbHVtbiAxIHNob3dzIHRoZSBTZW5hdG9yJ3MgbmFtZSwgYW5kIGFsbCBvdGhlciBjb2x1bW5zIGFyZSBUd2l0dGVyIGhhbmRsZXMgZm9sbG93ZWQgYnkgYXQgbGVhc3Qgb25lIFNlbmF0b3IuIEEgMSBJbmRpY2F0ZXMgdGhhdCB0aGUgU2VuYXRvcidzIFR3aXR0ZXIgYWNjb3VudCBmb2xsb3dzIHRoYXQgVHdpdHRlciBoYW5kbGUuDQotIGBkYXRhMmA6IEEgc3F1YXJlIG1hdHJpeCB0aGF0IHdoaWNoIFVTIFNlbmF0b3JzIGZvbGxvdyBlYWNoIG90aGVyIG9uIFR3aXR0ZXIgKDE6IGZvbGxvdywgMDogbm8gZm9sbG93KS4NCi0gYGRhdGEzYDogVGFibGUgY29udGFpbmluZyA3OCwwMDArIHR3aXR0ZXIgaGFuZGxlcyBmb2xsb3dlZCBieSBVLlMuIFNlbmF0b3JzIGFzIG9mIEZlYnJ1YXJ5IDIwMTUuIEhhbmRsZXMgYXJlIGdyb3VwZWQgaW4gdG8gU2NpZW5jZSAoYmFzZWQgb24gZm9jdXMgb2Ygb3JnYW5pemF0aW9uKSwgUG9saXRpY3MsIE1lZGlhLCBhbmQgT3RoZXIuDQotIGBkYXRhNGA6IFRvdGFsIG51bWJlciBvZiBUd2l0dGVyIGFjY291bnRzIGZvbGxvd2VkIGJ5IGVhY2ggU2VuYXRvciBhbmQgcHJvcG9ydGlvbiBjYXRlZ29yaXplZCBhcyBTY2llbmNlLCBQb2xpdGljcywgTWVkaWEgb3IgT3RoZXIuDQotIGBkYXRhNWA6IFRhYmxlIG9mIERXLU5vbWluYXRlIG1lYXN1cmUvc2NvcmUgZm9yIGVhY2ggU2VuYXRlLiBJIGFjdHVhbGx5IGRvbid0IHJlYWxseSBrbm93IGhvdyBhcmUgdGhlc2Ugc2NvcmVzIGNhbGN1bGF0ZWQuDQoNCg0KIyMgRGF0YSBFeHBsb3JhdGlvbg0KUmVtZW1iZXIgdGhhdCBJJ20gb25seSBnb2luZyB0byB1c2UgKipkYXRhNCoqIGFuZCAqKmRhdGE1KiogZm9yIGNyZWF0aW5nIHRoZSBwbG90LCBidXQgSSB0aGluayB3ZSBjYW4gc3RpbGwgdXRpbGl6ZSBvdGhlciBkYXRhIGZvciBhIHF1aWNrIGFuYWx5c2lzLiBTYXkgd2Ugd2FudCB0byBhbmFseXNlIHdoaWNoIHRvcCA1IHNlbmF0ZXMgdGhhdCBmb2xsb3cgc2NpZW5jZS1yZWxhdGVkIFR3aXR0ZXIgYWNjb3VudHMgdGhlIG1vc3QuDQoNCkZpcnN0LCB3ZSB0YWtlIHRoZSBkYXRhMyBhbmQgc2VsZWN0IG9ubHkgZm9yIHNjaWVuY2UgY2F0ZWdvcnkNCmBgYHtyfSANCnNjaWVuY2UgPC0gZGF0YTNbZGF0YTMkQ2F0ZWdvcnk9PSJTY2llbmNlIixdDQpnbGltcHNlKHNjaWVuY2UpDQpgYGANCldlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgNzcgc2NpZW5jZS1yZWxhdGVkIHR3aXR0ZXIgYWNjb3VudHMuDQoNCk5leHQsIHdlIHdhbnQgdG8gY2hlY2sgd2hldGhlciBTZW5hdGVzIGFyZSBmb2xsb3dpbmcgdGhvc2UgYWNjb3VudHMgdXNpbmcgZGF0YTEuIExldCdzIHNlZSB3aGF0IGRvZXMgZGF0YTEgbG9vayBsaWtlDQpgYGB7cn0NCmhlYWQoZGF0YTEpDQpgYGANCkl0IGNvbnRhaW5zIGEgbG90IG9mIGNvbHVtbnModHdpdHRlciBhY2NvdW50cykgZXZlbiB0aG91Z2ggd2Ugb25seSBuZWVkIGFjY291bnRzIHRoYXQgYXJlIHNjaWVuY2UtcmVsYXRlZCBzbyB3ZSB3YW50IHRvIHNlbGVjdCBvbmx5IHRob3NlIHZhbGlkIGNvbHVtbnMgd2l0aCB0aGVzZSBzdGVwczoNCg0KMS4gSW5pdGlhdGUgYW4gZW1wdHkgdmVjdG9yIGZvciB2YWxpZCBjb2x1bW5zDQoyLiBVc2UgZm9yIGxvb3AgdG8gaXRlcmF0ZSB0aHJvdWdoIGFsbCBjb2x1bW5zIGZyb20gZGF0YTENCjMuIENoZWNrIGlmIHRoZSBjb2x1bW5zIGFyZSBpbmNsdWRlZCBpbiB0aGUgc2NpZW5jZSBkYXRhZnJhbWUNCjQuIFN0b3JlIHRoYXQgdmFsaWQgY29sdW1ucyBpbnRvIG91ciBlbXB0eSB2ZWN0b3IuDQpgYGB7cn0NCnZhbGlkX2NvbCA8LSBjKCkNCmZvcihpIGluIDI6bGVuZ3RoKGNvbG5hbWVzKGRhdGExKSkpIHsNCiAgaWYoY29sbmFtZXMoZGF0YTEpW2ldICVpbiUgc2NpZW5jZSRUd2l0dGVyLkhhbmRsZSl7DQogICAgdmFsaWRfY29sIDwtIGFwcGVuZCh2YWxpZF9jb2wsIGNvbG5hbWVzKGRhdGExKVtpXSkNCiAgfSANCn0NCmxlbmd0aCh2YWxpZF9jb2wpDQpgYGANCg0KQWZ0ZXIgdGhhdCwgd2UgY2FuIHN1YnNldCBkYXRhMSB3aGljaCBjb250YWluIG9ubHkgdGhvc2UgdmFsaWQgY29sdW1ucw0KYGBge3J9DQpzY2llbmNlX3R3ZWV0IDwtIGRhdGExWywgYXBwZW5kKHZhbGlkX2NvbCwgIlgiLDApXQ0KaGVhZChzY2llbmNlX3R3ZWV0KQ0KYGBgDQoNCk5vdywgd2Ugd2lsbCB1c2UgYHJlc2hhcGUyYCBwYWNrYWdlIHRvIHJlc3RydWN0dXJlIHRoZSBzY2llbmNlX3R3ZWV0IGRhdGFmcmFtZSBhbmQgZ2V0IHRoZSBhbnN3ZXIgdGhhdCB3ZSBkZXNpcmUuIFdlIHdpbGwgZGl2aWRlIHRoYXQgaW50byB0aGVzZSBzdGVwczoNCg0KMS4gVXNlIGBtZWx0KClgIGZ1bmN0aW9uIHRvIHRyYW5zZm9ybSBhbGwgY29sdW1ucyBpbnRvIG9ubHkgb25lIGNvbHVtbi4NCjIuIENyZWF0ZSBmcmVxdWVuY3kgdGFibGUgd2l0aCBgdGFibGUoKWAgZnVuY3Rpb24gdG8gc2VlIGhvdyBtYW55IHNjaWVuY2UgcmVsYXRlZC1hY2NvdW50cyB0aGF0IGFyZSBmb2xsb3dlZCBieSBlYWNoIFNlbmF0ZS4gSXQgd2lsbCBiZSBzaG93biBhcyB0aGUgYEZyZXF1ZW5jeWAgY29sdW1uLg0KMy4gVXNlIGBhcnJhbmdlYCBmdW5jdGlvbiB0byBvcmRlciB0aGUgZnJlcXVlbmN5IGZyb20gdGhlIGhpZ2hlc3QgdG8gdGhlIGxvd2VzdCB0byBzZWUgdGhlIHRvcCA1Lg0KYGBge3J9DQojIG1lbHQgdGhlIHR3aXR0ZXIgYWNjb3VudCBjb2x1bW5zLg0KbWVsdGVkIDwtIG1lbHQoc2NpZW5jZV90d2VldCwiWCIpIA0Kc2VuYXRlX3NjaWVuY2UgPC0gZGF0YS5mcmFtZSh0YWJsZShtZWx0ZWQkWCxtZWx0ZWQkdmFsdWUpKQ0KY29sbmFtZXMoc2VuYXRlX3NjaWVuY2UpIDwtIGMoIk5hbWUiLCJpc1NjaWVuY2UiLCJGcmVxdWVuY3kiKQ0Kc2VuYXRlX3NjaWVuY2UgPC0gc2VuYXRlX3NjaWVuY2Vbc2VuYXRlX3NjaWVuY2UkaXNTY2llbmNlPT0xLF0NCg0KDQojIHNvcnQgdGhlIGRhdGEgYmFzZWQgb24gZnJlcXVlbmN5IHRvIGNoZWNrIHRoZSB0b3AgNQ0Kc2VuYXRlX3NjaWVuY2UgJT4lIA0KICBhcnJhbmdlKC1GcmVxdWVuY3kpICU+JSANCiAgaGVhZCg1KQ0KYGBgDQoNCg0KTm93LCBsZXQncyBtb3ZlIHRvIHRoZSBtYWluIG9iamVjdGl2ZSB3aGljaCBpcyByZWNyZWF0aW5nIHRoZSBlY29ub21pc3QgcGxvdC4gUmVjYWxsIHRoYXQgd2Ugd2lsbCBvbmx5IHVzZSAqZGF0YTQqIGFuZCAqZGF0YTUqIHNvIGxldCdzIGluc3BlY3QgdGhlbS4gIA0KDQpgYGB7cn0NCiMgU2VlIHRoZSBzdHJ1Y3R1cmUNCmdsaW1wc2UoZGF0YTQpDQpnbGltcHNlKGRhdGE1KQ0KYGBgDQoNCg0KYGBge3J9DQpoZWFkKGRhdGE0KQ0KaGVhZChkYXRhNSkNCmBgYA0KRnJvbSB0aGUgZGF0YTUsIHdlIG9ubHkgY2FyZSBhYm91dCB0aGUgbm9taW5hdGVfZGltMSBjb2x1bW4gc2luY2UgdGhpcyBpcyBnb2luZyB0byBiZSBvdXIgeC1heGlzIHZhbHVlLg0KDQoNCiMjIERhdGEgQ2xlYW5pbmcNCldlIGNhbiBzZWUgZnJvbSB0aGUgcmVzdWx0IGFib3ZlIHRoYXQgdGhlcmUgaXMgYSBkaWZmZXJlbnQgYW1vdW50IG9mIHJvdyBiZXR3ZWVuIGRhdGE0IGFuZCBkYXRhNSwgc28gd2UgbmVlZCB0byBtYXRjaCBib3RoIGRhdGFmcmFtZSBzbyB0aGV5IGNvbnRhaW4gdGhlIGV4YWN0IHNhbWUgU2VuYXRlLiBIZXJlLCBJIHVzZSBgZ3JlcGwoKWAgZnVuY3Rpb24gdG8gbWF0Y2ggdGhlIExhc3QuTmFtZSBjb2x1bW4gZnJvbSBkYXRhNCB0byBiaW9uYW1lIGNvbHVtbiBmcm9tIGRhdGE1LiBgZ3JlcGwoKWAgZnVuY3Rpb24gaGFzIHR3byBtYWluIHBhcmFtZXRlcnM6DQoNCi0gYHBhdHRlcm5gOiBjaGFyYWN0ZXJzIG9yIHBhdHRlcm5zIHRoYXQgbmVlZCB0byBiZSBtYXRjaGVkDQotIGB4YDogY29sdW1ucyB0aGF0IHdlIHdhbnQgdG8gY2hlY2sgd2hldGhlciB0aGUgcGF0dGVybnMgYXJlIGZvdW5kIG9yIG5vdA0KYGBge3J9DQojIGNvbmRpdGlvbmFsIHN1YnNldHRpbmcgdXNpbmcgc3Vic2V0IHdpdGggbWF0Y2hpbmcgY29uZGl0aW9uDQpub21pbmF0ZSA8LSBzdWJzZXQoZGF0YTUsIGdyZXBsKHBhc3RlKGRhdGE0JExhc3QuTmFtZSwgY29sbGFwc2U9InwiKSwgZGF0YTUkYmlvbmFtZSwgaWdub3JlLmNhc2UgPSBUKSwgc2VsZWN0ID0gYygiYmlvbmFtZSIsIm5vbWluYXRlX2RpbTEiKSkNCmhlYWQobm9taW5hdGUpDQpgYGANCg0KUmVtZW1iZXIgdGhhdCB0aGUgZGF0YTQgY29udGFpbnMgb25seSB0aGUgbGFzdCBuYW1lLCB3aGVyZWFzIHRoZSBkYXRhNSBjb250YWlucyBmdWxsIG5hbWUuIEhlcmUgSSB3YW50IHRvIHNwbGl0IHRoZSBmdWxsbmFtZSBhbmQgdGFrZSBvbmx5IHRoZSBsYXN0IG5hbWUuIFdlIGNhbiBzZWUgZnJvbSB0aGUgYmlvbmFtZSBjb2x1bW4sIHRoZSBsYXN0IG5hbWEgaXMgbG9jYXRlZCBiZWZvcmUgY29tbWEsIHNvIHdlIGNhbiB0cnkgdG8gZXh0cmFjdCBpdCBhbmQgbWFrZSBhIG5ldyBjb2x1bW4gY29udGFpbmluZyB0aGF0IGxhc3QgbmFtZSB1c2luZyBiZWxvdyBjb2RlLiBXZSBjYW4gdXNlIGBzdHJzcGxpdCgpYCBmdW5jdGlvbiB0byBzcGxpdCB0aGUgY2hhcmFjdGVycyBhbmQgd2UgY2FuIHVzZSBgc2FwcGx5KClgIHRvIGFwcGx5IHRoYXQgZnVuY3Rpb24gZm9yIGJpb25hbWUgY29sdW1uDQpgYGB7cn0NCiMgVXNlIHN0cnNwbGl0IHRvIHNwbGl0IHRoZSBiaW9uYW1lIGJhc2VkIG9uIGNvbW1hIGFuZCBvbmx5IHRha2UgdGhlIGxhc3QgbmFtZQ0Kbm9taW5hdGVfY2xlYW4gPC0gbm9taW5hdGUgJT4lIA0KICBtdXRhdGUobmFtZSA9IHNhcHBseShzdHJzcGxpdChub21pbmF0ZSRiaW9uYW1lLCAiLCIpLCBoZWFkLCAxKSkNCmhlYWQobm9taW5hdGVfY2xlYW4pDQpgYGANCg0KTmV4dCwgd2Ugd2FudCB0byBtYXRjaCB0aGUgZXhhY3QgY2hhcmFjdGVyIHJlZ2FyZGxlc3Mgb2Ygd2hhdCBjYXNlIGlzIGJlaW5nIHVzZWQodXBwZXIgb3IgbG93ZXIpLCBzbyB3ZSBsb3dlcmNhc2UgYm90aCBjb2x1bW4gd2l0aCBgdG9sb3dlcigpYCBmdW5jdGlvbi4NCmBgYHtyfQ0KIyBjb252ZXJ0IGJvdGggY29sdW1uIG5hbWUgdG8gbG93ZXIgY2FzZQ0KZGF0YTQkTGFzdC5OYW1lIDwtIHRvbG93ZXIoZGF0YTQkTGFzdC5OYW1lKQ0Kbm9taW5hdGVfY2xlYW4kbmFtZSA8LSB0b2xvd2VyKG5vbWluYXRlX2NsZWFuJG5hbWUpDQpgYGANCg0KDQpGaW5hbGx5LCB3ZSB3aWxsIGpvaW4gdGhlc2UgdHdvIGRhdGFmcmFtZXMgaW50byBvbmUgZGF0YWZyYW1lIHNvIHdlIGNhbiB1c2UgaXQgZm9yIG91ciBwbG90dGluZy4gV2UgY2FuIHVzZSBgbGVmdF9qb2luKClgIGZyb20gYGRwbHlyYCBwYWNrYWdlIHRvIGpvaW4gdHdvIGRhdGFmcmFtZXMgYmFzZWQgb24gbWF0Y2hlZCBjb2x1bW4uIFRoZSBjb25jZXB0IG9mIGpvaW4gaGVyZSBpcyBzaW1pbGFyIHRvIHdoYXQgd2UgaGF2ZSBpbiAqKlNRTCoqIGpvaW4uDQoNClJlY2FsbCB0aGF0IGZyb20gdGhlIHJlZmVyZW5jZSBwbG90IHRoYXQgd2UgaGF2ZSwgdGhlIHkgYXhpcyBpcyBpbiB0aGUgZm9ybSBvZiBwZXJjZW50YWdlIHNvIHdlIG5lZWQgdG8gbXVsdGlwbHkgdGhhdCB3aXRoIDEwMC4gRnVydGhlcm1vcmUsIHdlIGFsc28gd2FudCB0byBtYXRjaCB0aGUgcG9pbnQgY29sb3IgaW4gb3VyIHBsb3QuIFRoZSBjb2xvciBpcyBiYXNlZCBvbiB0aGUgUGFydHkgY29sdW1uIHNvIHdlIGNhbiBjb252ZXJ0IHRoYXQgY29sdW1uIGFzIGZhY3RvciB3aXRoIGRlZmluZWQgbGV2ZWxzIGFzICgiRCIsIlIiLCJJIikuDQpgYGB7cn0NCiMgam9pbiB0d28gZGF0YWZyYW1lcyBiYXNlZCBvbiB0aGUgbWF0Y2hpbmcgbmFtZXMNCmNsZWFuX2RhdGEgPC0gZGF0YTQgJT4lIA0KICBsZWZ0X2pvaW4obm9taW5hdGVfY2xlYW4sIGJ5ID0gYygiTGFzdC5OYW1lIiA9ICJuYW1lIikpDQoNCiMgbXVsdGlwbHkgc2NpZW5jZSBjb2x1bW5zIHdpdGggMTAwDQpjbGVhbl9kYXRhJFNjaWVuY2UgPC0gY2xlYW5fZGF0YSRTY2llbmNlKjEwMA0KDQojIENvbnZlcnQgdGhlIFBhcnR5IGNvbHVtbiBpbnRvIGZhY3RvciB3aXRoIGRlZmluZWQgbGV2ZWxzDQpjbGVhbl9kYXRhJFBhcnR5IDwtIGZhY3RvcihjbGVhbl9kYXRhJFBhcnR5LCBsZXZlbHMgPSBjKCJEIiwiUiIsIkkiKSkNCmhlYWQoY2xlYW5fZGF0YSkNCmBgYA0KDQpCZWZvcmUgSSBjcmVhdGUgdGhlIHBsb3QsIEkgd2FudCB0byBleGNsdWRlIHNlbmF0ZXMgd2hvIGRvbid0IGFjdHVhbGx5IGZvbGxvdyBzY2llbmNlLXJlbGF0ZWQgYWNjb3VudCBhbmQgdGFrZSB0aGUgc2VuYXRlcyB3aG8gZm9sbG93IGxlc3MgdGhhbiA3MDAwIGFjY291bnQuIFRoZSBkZWNpc2lvbiB3YXMgYmFzZWQgb24gd2hhdCBJIGhhdmUgc2VlbiBmcm9tIHRoZSByZWFsIHBsb3QuDQpgYGB7cn0NCmNsZWFuX2RhdGEgPC0gY2xlYW5fZGF0YVtjbGVhbl9kYXRhJFRvdGFsIDw9IDcwMDAgLF0NCmNsZWFuX2RhdGEgPC0gY2xlYW5fZGF0YVtjbGVhbl9kYXRhJFNjaWVuY2UgPiAwLF0NCmBgYA0KDQpJIHRyeSB0byByZXByb2R1Y2UgdGhlIGV4YWN0IHNhbWUgcGxvdCBldmVuIHRoZSBmb250IHN0eWxlLCBzbyBJIGZvdW5kIHNvbWUgZm9udHMgb25saW5lIHRoYXQgaG9wZWZ1bGx5IHdvdWxkIGhlbHAgdXMgbWFraW5nIG91ciBwbG90IGxvb2tzICdlY29ub21pc3QnLiBJbiBvcmRlciB0byBsb2FkIHRob3NlIGZvbnRzLCB3ZSBuZWVkIHRvIHVzZSBgc2hvd3RleHRgIHBhY2thZ2UgdGhhdCB3ZSd2ZSBhbHJlYWR5IGltcG9ydGVkIHByZXZpb3VzbHkNCmBgYHtyfQ0KIyBBZGQgYW5kIGxvYWQgdGhlIGV4dGVybmFsIGZvbnRzDQpmb250LmFkZCgiSVRDIE9mZmljaW5hIEJvb2siLCAiaXRjLW9mZmljaW5hLXNhbnMtc3RkLWJvb2sub3RmIikNCmZvbnQuYWRkKCJJVEMgT2ZmaWNpbmEgQm9vayBJdGFsaWMiLCAiaXRjLW9mZmljaW5hLXNhbnMtc3RkLWJvb2staXRhbGljLm90ZiIpDQpmb250LmFkZCgiSVRDIE9mZmljaW5hIEJvbGQiLCJpdGMtb2ZmaWNpbmEtc2Fucy1zdGQtYm9sZC5vdGYiKQ0KZm9udC5hZGQoIklUQyBPZmZpY2luYSBCb2xkIEl0YWxpYyIsICJpdGMtb2ZmaWNpbmEtc2Fucy1zdGQtYm9sZC1pdGFsaWMub3RmIikNCnNob3d0ZXh0X2F1dG8oKQ0KYGBgDQoNCg0KTmV4dCwgd2Ugd2FudCB0byBvbmx5IHZpc3VhbGl6ZSBjZXJ0YWluIHNlbmF0ZXMgbmFtZXMgYmFzZWQgb24gdGhlIHJlYWwgcGxvdA0KYGBge3J9DQojIFRha2Ugb25seSBjZXJ0YWluIHNlbmF0ZXMNCnRleHRfbGFiZWwgPC0gYygibWFya2V5IiwicmVpZCIsImNvY2hyYW4iLCJzYW5kZXJzIiwic2NoYXR6Iiwidml0dGVyIiwiaW5ob2ZlIikNCmRhdGFfbGFiZWwgPC0gY2xlYW5fZGF0YVtjbGVhbl9kYXRhJExhc3QuTmFtZSAlaW4lIHRleHRfbGFiZWwsXQ0KDQojIENyZWF0ZSBuZXcgY29sdW1ucyBmb3Igc2VuYXRlcyBmdWxsIG5hbWUgdGhhdCB3ZSB3YW50IHRvIHNob3cgaW4gb3VyIHBsb3QNCmRhdGFfbGFiZWwkRnVsbC5OYW1lIDwtIGMoIlRoYWQgQ29jaHJhbiIsICJKaW0gSW5ob2ZlIiwiRWQgTWFya2V5IiwiSGFycnkgUmVpZCIsIkJlcm5pZSBTYW5kZXJzIiwgIkJyaWFuIFNjaGF0eiIsIkRhdmlkIFZpdHRlciIgKQ0KYGBgDQoNCg0KIyMgUmVjcmVhdGluZyB0aGUgUGxvdA0KQmVmb3JlIHdlIGNvZGUgdGhlIGFjdHVhbCBwbG90LCB3ZSB3YW50IHRvIHNldCB0aGUgdGhlbWUgZm9yIG91ciBwbG90IGFzIHNpbWlsYXIgYXMgcG9zc2libGUgdG8gb3VyIHJlZmVyZW5jZS4gVGhpcyBhY3R1YWxseSByZXF1aXJlcyBzZXZlcmFsIGF0dGVtcHRzLCBzbyBJIGFtIHNvcnJ5IGlmIHRoZSBmaW5hbCByZXN1bHQgZG9lcyBub3Qgc2F0aXNmeSB5b3UuDQpgYGB7cn0NCnRoZW1lX3BvbCA8LSB0aGVtZSgNCiAgICAjIGN1c3RvbWl6ZSB0aGUgcGxvdCB0aXRsZQ0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xNCwgZmFtaWx5PSJJVEMgT2ZmaWNpbmEgQm9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhqdXN0PS0wLjA3NSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IC01KSwNCiAgICAjIEN1c3RvbWl6ZSB0aGUgcGxvdCBzdWJ0aXRsZQ0KICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCwgZmFtaWx5PSJJVEMgT2ZmaWNpbmEgQm9vayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IC0wLjE1NSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2anVzdCA9IC04LjIpLA0KICAgICMgQ3VzdG9taXplIHRoZSBwbG90IGdyaWQNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5tYWpvci54PSBlbGVtZW50X2JsYW5rKCksDQogICAgcGFuZWwuZ3JpZC5taW5vci54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIHBhbmVsLmdyaWQubWlub3IueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgDQogICAgIyBDdXN0b21pemUgcGxvdCBiYWNrZ3JvdW5kDQogICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI0ZGRkZGRiIpLA0KICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjRkZGRkZGIiksDQogICAgDQogICAgIyBDdXN0b21pemUgcGxvdCBheGlzDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseT0iSVRDIE9mZmljaW5hIEJvb2sgSXRhbGljIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IC0xLjMpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHk9IklUQyBPZmZpY2luYSBCb2xkIEl0YWxpYyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvdXIgPSAiIzY5Njk2OSIpLA0KICAgIGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIklUQyBPZmZpY2luYSBCb29rIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIpLA0KICAgIGF4aXMudGlja3MubGVuZ3RoLnggPSB1bml0KC0wLjIsICJsaW5lcyIpLA0KICAgIA0KICAgICMgQ3VzdG9taXplIHBsb3QgbGVnZW5kDQogICAgbGVnZW5kLnBvc2l0aW9uID0gYygtMC4wNywwLjg4KSwNCiAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiLA0KICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImxlZnQiLA0KICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MTAsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJJVEMgT2ZmaWNpbmEgQm9vayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJibGFjayIpLA0KICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5PSJJVEMgT2ZmaWNpbmEgQm9sZCIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcj0iYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gbWFyZ2luKHI9OCkpLA0KICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGxlZ2VuZC5rZXk9ZWxlbWVudF9ibGFuaygpLA0KICAgIGxlZ2VuZC5zcGFjaW5nLnggPSB1bml0KGMoLTAuMSksICJjbSIpDQogICkgDQoNCmBgYA0KDQpgZ2dwbG90MmAgaXMgYSBwYWNrYWdlIGZvciBkZWNsYXJhdGl2ZWx5IGNyZWF0aW5nIGdyYXBoaWNzLCBiYXNlZCBvbiAqKlRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzKiogdGhhdCBhbGxvd3MgdXMgdG8gY29tcG9zZSBncmFwaCBieSBjb21iaW5pbmcgaW5kZXBlbmRlbnQgY29tcG9uZW50cy4gRXZlcnkgZ2dwbG90MiBwbG90IGhhcyB0aHJlZSBrZXkgY29tcG9uZW50czoNCg0KMS4gRGF0YQ0KMi4gQSBzZXQgb2YgYWVzdGhldGljIG1hcHBpbmdzIGJldHdlZW4gdmFyaWFibGVzIGluIHRoZSBkYXRhIGFuZCB2aXN1YWwgcHJvcGVydGllcw0KMy4gQXQgbGVhc3Qgb25lIGxheWVyIHdoaWNoIGRlc2NyaWJlcyBob3cgdG8gcmVuZGVyIGVhY2ggb2JzZXJ2YXRpb24uIExheWVycyBhcmUgdXN1YWxseSBjcmVhdGVkIHdpdGggYSBnZW9tIGZ1bmN0aW9uLg0KDQpUaGUgc3RlcHMgZm9yIGNyZWF0aW5nIGdncGxvdCBncmFwaCBpcyByZWFsbHkgc3RyYWlnaHRmb3J3YXJkOg0KDQoxLiBJbml0aWF0ZSB0aGUgZ2dwbG90IG9iamVjdCB1c2luZyBgZ2dwbG90KClgLiBUaGUgZnVuY3Rpb24gaGFzIHR3byBtYWluIHBhcmFtZXRlcnMgd2hpY2ggYXJlICpkYXRhKiBhbmQgKm1hcHBpbmcqLg0KMi4gQWRkaW5nIG9iamVjdCBsYXllciB0aGF0IHdlIHdhbnQgdG8gYmUgc2hvd24gYnkgb3VyIHBsb3QuIEl0IGNhbiBiZSBhbnkgZ2VvbSBvYmplY3QuIFdlIGNhbiBhbHNvIGFkZCBtb3JlIHRoYW4gb25lIGxheWVyIGlmIHdlIHdhbnQgdG8uDQozLiBBZGRpbmcgbGF5ZXJzIGZvciBwbG90IGN1c3RvbWl6YXRpb24uDQoNCk91ciBwbG90IGluY2x1ZGVzIHBvaW50IGFuZCB0ZXh0IG9iamVjdCwgc28gbGV0J3MgZG8gdGhhdCB1c2luZyBgZ2VvbV9wb2ludCgpYCBhbmQgYGdlb21fdGV4dCgpYCBmdW5jdGlvbi4gDQpgYGB7cn0NCmdnIDwtIGdncGxvdChkYXRhID0gY2xlYW5fZGF0YSwgYWVzKHg9bm9taW5hdGVfZGltMSx5PVNjaWVuY2UpKSArIA0KICAjIEFkZGluZyBwb2ludHMgdG8gb3VyIHBsb3QNCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9UGFydHksIHNpemU9VG90YWwpLCBhbHBoYT0wLjUpICsNCiAgDQogICMgQWRkaW5nIHJlcXVpcmVkIHNlbmF0ZSBuYW1lcyByZXByZXNlbnRlZCBieSB0ZXh0IG9iamVjdA0KICBnZW9tX3RleHQoDQogICAgZGF0YT1kYXRhX2xhYmVsICwNCiAgICBhZXMobGFiZWw9RnVsbC5OYW1lLGNvbG9yPVBhcnR5KSwgDQogICAgbnVkZ2VfeCA9IDAuMTUsDQogICAgbnVkZ2VfeSA9IDAuMiwNCiAgICBmYW1pbHkgPSAiSVRDIE9mZmljaW5hIEJvbGQgSXRhbGljIiwNCiAgICBzaG93LmxlZ2VuZCA9IEYNCiAgKQ0KYGBgDQoNCg0KV2UgYWxzbyBuZWVkIHRvIHNwZWNpZnkgdGhlIHBsb3QgdGl0bGUsIGF4aXMsIHNjYWxlLCBhbmQgbGVnZW5kIHNvIGl0IG1hdGNoZXMgdGhlIGFjdHVhbCBwbG90Lg0KDQpgYGB7cn0NCmdnIDwtIA0KICBnZyArDQogIA0KICAjIEN1c3RvbWl6ZSBheGlzIGxhYmVscw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gYygiMS4wIiwiMC41IiwiMCIsIjAuNSIsIjEuMCIpKSArDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucz1sb2cyX3RyYW5zKCksYnJlYWtzPWMoMC41LDEuMCwyLjAsNC4wLDYuMCw4LjAsMTAuMCkpICsNCiAgDQogICMgQ3VzdG9taXplIHRoZSBwb2ludHMgc2l6ZQ0KICBzY2FsZV9zaXplKHJhbmdlID0gYygxLDIwKSwgZ3VpZGU9Im5vbmUiKSArDQogIA0KICAjIEN1c3RvbWl6ZSB0aGUgcG9pbnRzIGNvbG9yLiBUaGlzIGlzIHdoeSBjb252ZXJ0aW5nIGRhdGEgdHlwZXMgaW50byBmYWN0b3Igd2l0aCBkZWZpbmVkIGxldmVscyBjb21lcyBpbiBoYW5keS4NCiAgc2NhbGVfY29sb3JfbWFudWFsKA0KICAgIHZhbHVlcyA9IGMoIiM4ZGQ0ZjAiLCIjZjQ4ODcyIiwiIzRmOTU4OCIpLA0KICAgIGd1aWRlID0gZ3VpZGVfbGVnZW5kKHRpdGxlPSJQYXJ0eSBhZmZpbGlhdGlvbjoiKSwgDQogICAgICAgICAgICBsYWJlbHM9IGMoIkQiPSJEZW1vY3JhdHMiLCJSIj0iUmVwdWJsaWNhbnMiLCJJIj0iSW5kZXBlbmRlbnRzIikgICAgICAgICAgICAgICANCiAgKSArDQogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoLTEsMSksIGNsaXAgPSAnb2ZmJykgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlBvbGl0aWNhbCBTY2llbmNlIiwgDQogICAgc3VidGl0bGUgPSAiVW5pdGVkIFN0YXRlcyBzZW5hdG9yJ3MgcG9saXRpY2FsIGlkZW9sb2d5IGFuZCBzY2llbnRpZmljIGVuZ2FnZW1lbnQgb24gVHdpdHRlcioiLA0KICAgIHggPSAiTGliZXJhbC1Db25zZXJ2YXRpdmUgU2NvcmUiLA0KICAgIHkgPSAiU2NpZW5jZS1yZWxhdGVkIFR3aXR0ZXIgYWNjb3VudHMgZm9sbG93ZWQsICUgb2YgdG90YWwsIG5hdHVyYWwgbG9nIHNjYWxlIg0KICApICsNCiAgdGhlbWVfYncoKSANCmBgYA0KDQoNCk5leHQsd2Ugd2FudCB0byBjcmVhdGUgYWRkaXRpb25hbCB0ZXh0IGFuZCB0aGUgbWludXMoLSkgcGx1cygrKSBzaWduIGZvciBmb3IgdGhlIHgtYXhpcyB1c2luZyBgZ3JpZGAgcGFja2FnZS4gSSBhY3R1YWxseSB0cmllZCB0byBmaW5kIHRoZSBtb3N0IGVmZmljaWVudCB3YXksIGFuZCBlbmQgdXAgZmluZGluZyB0aGlzIGFwcHJvYWNoDQpgYGB7cn0NCiMgdXNlIGdyb2JUcmVlIHRvIGFkZCBjdXN0b21pemVkIHRleHQgaW50byBvdXIgcGxvdA0KZ3JvYiA9IGdyb2JUcmVlKHRleHRHcm9iKCJGZWJydWFyeSAyMDE1IiwgeD0tMC4wNiwgeT0xLCBoanVzdD0wLCB2anVzdD00LCBncD1ncGFyKGZvbnRzaXplPTgsIGZvbnRmYW1pbHk9IklUQyBPZmZpY2luYSBCb29rIiwgY29sPSJibGFjayIpKSkNCmdyb2IyID0gZ3JvYlRyZWUodGV4dEdyb2IoIi0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKyIsIHg9MC4zNywgeT0tMC4wNCwgaGp1c3Q9MCwgdmp1c3Q9MC40LCBncD1ncGFyKGZvbnRzaXplPTE1LCBmb250ZmFtaWx5PSJJVEMgT2ZmaWNpbmEgQm9vayIsIGNvbD0iYmxhY2siKSkpDQpncm9iMyA9IGdyb2JUcmVlKHRleHRHcm9iKCJNT1JFIExJQkVSQUwiLCB4PTAuMDUsIHk9MSwgaGp1c3Q9MCwgdmp1c3Q9NTYsIGdwPWdwYXIoZm9udHNpemU9OCwgZm9udGZhbWlseT0iSVRDIE9mZmljaW5hIEJvbGQgSXRhbGljIiwgY29sPSIjOGRkNGYwIikpKQ0KZ3JvYjQgPSBncm9iVHJlZSh0ZXh0R3JvYigiTU9SRSBDT05TRVJWQVRJVkUiLCB4PTAuOCwgeT0xLCBoanVzdD0wLCB2anVzdD01NiwgZ3A9Z3Bhcihmb250c2l6ZT04LCBmb250ZmFtaWx5PSJJVEMgT2ZmaWNpbmEgQm9sZCBJdGFsaWMiLCBjb2w9IiM0Zjk1ODgiKSkpDQpgYGANCg0KDQpOb3csIHdlIHNob3VsZCBjcmVhdGUgdGhlIGNvZWNlbnRyaWMgY2lyY2xlcyBpbnN0ZWFkIG9mIHRoZSBuYXRpdmUgbGVnZW5kLiBJIGRvbid0IGtub3cgaG93IHRvIGN1c3RvbWl6ZSB0aGF0IGlmIEkgb25seSB1c2UgdGhlIGJhc2ljIG5hdGl2ZSBsZWdlbmQgcHJvdmlkZWQgYnkgZ2dwbG90LCBzbyBJIGZvdW5kIHRoYXQgdGhpcyB3YXkgd291bGQgYmUgYW4gb3B0aW9uLiBCZXNpZGVzIHRoYXQsIHdlIGFsc28gbmVlZCB0byBjcmVhdGUgY3VzdG9taXplZCBncmlkIHNpbmNlIGFzIHdlIGNhbiBzZWUgZnJvbSB0aGUgYWN0dWFsIHBsb3QgdGhlIHR3byBncmlkcyBhdCB0aGUgdG9wIGFyZSBjdXQuIFRoaXMgd2lsbCBiZSBkaXZpZGVkIGludG8gc2V2ZXJhbCBzdGVwczoNCg0KMS4gQ3JlYXRlIGRhdGFmcmFtZSB0aGF0IGNvbnRhaW5zIGxhYmVscywgc2l6ZSBvZiBlYWNoIGNpcmNsZSwgYW5kIHRoZSBwb3NpdGlvbiBvZiBib3RoIGNpcmNsZXMgYW5kIGxhYmVscy4NCjIuIENyZWF0ZSBkYXRhZnJhbWUgZm9yIHRoZSBkYXNoIGxpbmUgdGhhdCB3ZSBoYXZlIGluIG91ciBsZWdlbmQuDQozLiBDcmVhdGUgdGhlIGxlZ2VuZCBieSB1c2luZyBgZ2VvbV9wb2ludCgpYCBmb3IgY2lyY2xlcyBhbmQgYGdlb21fdGV4dCgpYCBmb3IgdGhlIGxhYmVscw0KNC4gV2UgYWxzbyBoYXZlIG9uZSBhZGRpdGlvbmFsIHN0ZXAgdG8gY3JlYXRlIGN1c3RvbWl6ZWQgeS1heGlzIGdyaWQgYmVjYXVzZSBhcyB3ZSBjYW4gc2VlIGZyb20gdGhlIHJlZmVyZW5jZSBwbG90LCB0aGUgZmlyc3QgdGhyZWUgZ3JpZHMgYXJlIGN1dCBpbiBvcmRlciB0byBub3Qgb3ZlcmxhcHBpbmcgdGhlIGxlZ2VuZA0KYGBge3J9DQojIENyZWF0ZSBkYXRhZnJhbWUgZm9yIGNpcmNsZSBsZWdlbmQNCmxlZ2VuZF9iYmwgPC0gZGF0YS5mcmFtZShsYWJlbD1jKCIyNTAiLCIxLDAwMCIsIjUsMDAwIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT1jKDI1MCwxMDAwLDUwMDApLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBvc19iYmw9Yyg4LjcsOS41LDEyKSwgICNjKDcuNyw4LjUsMTEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIHBvc190eHQ9Yyg4LjksMTIuOSwyMSkpDQojIENyZWF0ZSBkYXRhZnJhbWUgZm9yIHktYXhpcyBncmlkDQpsaW5lX3NlZ21lbnQgPC0gZGF0YS5mcmFtZSh4ZW5kPWMoMC43LDAuNywxLDEsMSwxLDEsMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5PWMoMTAsOCw2LDQsMiwxLDAuNSwwKQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KZGFzaF9zZWdtZW50IDwtIGRhdGEuZnJhbWUoeT1jKDkuOCwxMS45LDE5LjIpKQ0KYGBgDQoNCg0KQWZ0ZXIgd2UgcHJlcGFyZSB0aGUgZGF0YSBmb3IgY3JlYXRpbmcgY3VzdG9taXplZCBsZWdlbmQsIHdlIGNhbiBhZGQgdGhhdCBpbnRvIG91ciBwbG90DQpgYGB7cn0NCmZpbmFsIDwtIGdnICsNCiAgIyBDaXJjbGUgbGVnZW5kDQogIGdlb21fcG9pbnQoDQogICAgZGF0YT1sZWdlbmRfYmJsLA0KICAgIGFlcyh4PTAuODQsIHk9cG9zX2JibCwgc2l6ZT1zaXplKSwNCiAgICBzaGFwZSA9IDIxLCBjb2xvciA9ICJibGFjayIsIGZpbGwgPSAiI2UxZTNlNSIsYWxwaGE9MC41DQogICkrDQogICMgTGFiZWwgZm9yIHRoZSBsZWdlbmQNCiAgZ2VvbV90ZXh0KA0KICAgIGRhdGE9bGVnZW5kX2JibCwNCiAgICBhZXMoeD0xLHk9cG9zX3R4dCxsYWJlbD1sYWJlbCksDQogICAgaGp1c3Q9MSwNCiAgICBzaXplPTMNCiAgKSsNCiAgDQogICMgQ3JlYXRlIGN1c3RvbWl6ZWQgeS1heGlzIGdyaWQNCiAgZ2VvbV9zZWdtZW50KGRhdGE9bGluZV9zZWdtZW50LA0KICAgICAgICAgICAgICAgYWVzKHg9LTEuMSx4ZW5kPXhlbmQseT15LHllbmQ9eSksY29sb3I9ImdyZXk2NiIpKw0KICANCiAgIyBDcmVhdGUgZGFzaCBsaW5lIGluIG91ciBsZWdlbmQNCiAgZ2VvbV9zZWdtZW50KGRhdGEgPSBkYXNoX3NlZ21lbnQsDQogICAgICAgICAgICAgICBhZXMoeD0wLjgzLCB4ZW5kPTEsIHk9eSx5ZW5kPXkpLGxpbmV0eXBlPSJkb3R0ZWQiKSArDQogIA0KICAjIGFkZGluZyBvdXIgZ3JvYlRleHQgb2JqZWN0IHdpdGggYW5ub3RhdGlvbl9jdXN0b20NCiAgYW5ub3RhdGlvbl9jdXN0b20oZ3JvYikgKw0KICBhbm5vdGF0aW9uX2N1c3RvbShncm9iMikgKw0KICBhbm5vdGF0aW9uX2N1c3RvbShncm9iMykgKw0KICBhbm5vdGF0aW9uX2N1c3RvbShncm9iNCkgKw0KDQogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDAuNzUgLCB5ID0gMzAsIGxhYmVsID0gIlR3aXR0ZXIgYWNjb3VudHMgXG5mb2xsb3dlZCwgdG90YWwiLCBmb250ZmFjZSA9ICJib2xkIiwgZmFtaWx5ID0gIklUQyBPZmZpY2luYSBCb2xkIixsaW5laGVpZ2h0ID0gMC42LCBoanVzdD0wICkgKw0KICB0aGVtZV9wb2wNCiAgDQoNCmZpbmFsDQpgYGANCkkga25vdyB0aGF0IHRoZSByZXN1bHQgZG9lcyBub3QgZXhhY3RseSBsb29rIGxpa2UgdGhlIG9yaWdpbmFsIG9uZSwgcHJvYmFibHkgYmVjYXVzZSB0aGVyZSBpcyBhIGxpdHRsZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIGRhdGFzZXQgdGhhdCBJIHVzZSBmcm9tIHRoZSBlY29ub21pc3QgcGxvdCByZWZlcmVuY2UuIEhvd2V2ZXIsIGF0IGxlYXN0IHdlIGhhdmUgZ290IGEgcXVpdGUgc2ltaWxhciBwbG90IGV2ZW4gdGhvdWdoIGl0IHN0aWxsIGZhciBmcm9tIHBlcmZlY3QuDQoNCg0KYGBge3IgZXZhbD1GQUxTRSwgZWNobz1GQUxTRX0NCiAgIyBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjgsIHkgPSAzMCwgbGFiZWwgPSBzdHJpbmdyOjpzdHJfd3JhcCgNCiAgIyAgICJUd2l0dGVyIGFjY291bnRzIGZvbGxvd2VkLCB0b3RhbCIsDQogICMgICAgd2lkdGggPSAyMCksIGZvbnRmYWNlID0gImJvbGQiLCBmYW1pbHkgPSAiSVRDIE9mZmljaW5hIEJvbGQiICkgDQojZ2dzYXZlKCJuZXcucG5nIiwgaGVpZ2h0ID0gMywgd2lkdGggPTYsIGRwaSA9IDIwMCkNCmxpYnJhcnkoZ2dwdWJyKQ0KZ2dleHBvcnQoZmluYWwsIGZpbGVuYW1lID0gImJhcnUucGRmIikNCmBgYA0KDQpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPUZBTFNFfQ0KZ2dzYXZlKGZpbGVuYW1lPSJjb2JhLnBuZyIsIHBsb3Q9ZmluYWwsIHdpZHRoPTksIGhlaWdodD02LCB1bml0cyA9ICJpbiIsIGRwaSA9ICdzY3JlZW4nKQ0KYGBgDQoNCg0KIyBSZWZlcmVuY2VzDQotIDxodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9BeGVzXyhnZ3Bsb3QyKS8+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy85NTIwODQwL3VzaW5nLXJlZ2V4cC10by1zZWxlY3Qtcm93cy1pbi1yLWRhdGFmcmFtZT4NCi0gPGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzc1OTc1NTkvZ3JlcC11c2luZy1hLWNoYXJhY3Rlci12ZWN0b3Itd2l0aC1tdWx0aXBsZS1wYXR0ZXJucz4NCi0gPGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzYwNjIxMjU0L3Itdmxvb2t1cC1iYXNlZC1vbi1wYXJ0aWFsLXN0cmluZy1tYXRjaGVzLWluLXI+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zMzY4Mzg2Mi9maXJzdC1lbnRyeS1mcm9tLXN0cmluZy1zcGxpdD4NCi0gPGh0dHBzOi8vd3d3LmRhdGFub3ZpYS5jb20vZW4vYmxvZy9ob3ctdG8tY3JlYXRlLWEtYnViYmxlLWNoYXJ0LWluLXItdXNpbmctZ2dwbG90Mi8+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy80Mzc3MDU3OS9ob3ctdG8tY2hhbmdlLXRoZS1jb2xvci1pbi1nZW9tLXBvaW50LW9yLWxpbmVzLWluLWdncGxvdD4NCi0gPGh0dHBzOi8vZ2dwbG90Mi1ib29rLm9yZy9zY2FsZS1wb3NpdGlvbi5odG1sI3NjYWxlLXRyYW5zZm9ybWF0aW9uPg0KLSA8aHR0cDovL3d3dy5jb29rYm9vay1yLmNvbS9HcmFwaHMvQXhlc18oZ2dwbG90MikvPg0KLSA8aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvOTY5MDY0OC9hdm9pZC1jbGlwcGluZy1vZi1wb2ludHMtYWxvbmctYXhpcy1pbi1nZ3Bsb3Q+DQotIDxodHRwczovL3d3dy5zdGF0b2xvZ3kub3JnL2dlb21fcG9pbnQtZmlsbC8+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zNTgzMzMwNy9nZ3Bsb3QyLWF4aXMtbm90LXNob3dpbmctYWZ0ZXItdXNpbmctdGhlbWVheGlzLWxpbmUtZWxlbWVudC1saW5lPg0KLSA8aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTI2MTI4NjcvY3JlYXRlLWEtY29uY2VudHJpYy1jaXJjbGUtbGVnZW5kLWZvci1hLWdncGxvdC1idWJibGUtY2hhcnQ+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8yNTEwNjUwOC9nZ3Bsb3QyLWlzLXRoZXJlLWFuLWVhc3ktd2F5LXRvLXdyYXAtYW5ub3RhdGlvbi10ZXh0Pg0KLSA8aHR0cHM6Ly9yZHJyLmlvL2NyYW4vZ2d0aGVtZXMvbWFuL3RoZW1lX2Vjb25vbWlzdC5odG1sPg0KLSA8aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMTE3MTQ5NTEvcmVtb3ZlLWV4dHJhLWxlZ2VuZHMtaW4tZ2dwbG90Mj4NCi0gPGh0dHBzOi8vZ2l0aHViLmNvbS9qb3NodWEwMzA4L3RyYW5zaWZ5L3RyZWUvbWFzdGVyL2ZvbnQ+DQotIDxodHRwOi8vd3d3LmNvb2tib29rLXIuY29tL0dyYXBocy9MZWdlbmRzXyhnZ3Bsb3QyKS8+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNTM5MjU3MC9zZXQtbGVuZ3RoLW9mLWdlb20taGxpbmUtaW4tZ2VvbS1iYXItcGxvdD4NCi0gPGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzMyNTA1Mjk4L2V4cGxhaW4tZ2dwbG90Mi13YXJuaW5nLXJlbW92ZWQtay1yb3dzLWNvbnRhaW5pbmctbWlzc2luZy12YWx1ZXM+DQotIDxodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xNjA3NDQ0MC9yLWdncGxvdDItY2VudGVyLWFsaWduLWEtbXVsdGktbGluZS10aXRsZT4NCi0gPGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzE0NjA0NDM1L3R1cm5pbmctb2ZmLXNvbWUtbGVnZW5kcy1pbi1hLWdncGxvdD4NCi0gPGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzI2MzY3Mjk2L2hvdy1kby1pLW1ha2UtbXktYXhpcy10aWNrcy1mYWNlLWlud2FyZHMtaW4tZ2dwbG90Mj4NCg==