SNA Grad Seminar, Fall 2017 Due: October 24th, 11:59 pm Name of Student:

The purpose of this lab is to develop your familiarity conducting descriptive network analysis using the statistical software package R. This assignment will make use of a data set you collect by defining a search query (a collection of your user-defined search terms) from the New York Times’s Article Search Application Programming Interface. Networks are generated from the co-occurrences between search terms included in the same search query. For example, a link exists between “apple” and “orange” if there are articles in the New York Times that contained these two terms. You will be visualizing and interpreting individual and global network properties of this network.

You will be graded primarily on the completeness and accuracy of your responses, but the clarity of the prepared report will also affect your grade. While students may work together to perform the analysis, each student must submit his or her own report and is responsible for writing the narrative in the report. You must answer all of the bolded questions.

Part 1: Collect Network Data (20 pts)

For this lab, you will search the New York Times, save that data, create networks from that data, compare the differences among networks, and demonstrate your proficiency with basic network descriptive statistics.

Loading and Installing Packages, Set Working Directory

When working with R, you should run each line of code individually, unless it is part of a function definition, so you can see the results. Generally speaking, any line of code that includes ‘{’ (the beginning of a function definition) should be run with all the other lines until you hit ‘}’.

# Lines that start with a hashtag/pound symbol, like this one, are comment lines. Comment lines are ignored by R when it is interpreting code.
# You only need to install packages once. Remove the # in front of each line and then run it to install each package. After successful installation, delete the line of code or replace the #s so the R Notebook doesn't run into problems.
# install.packages('magrittr', repos = "https://cran.rstudio.com")
# install.packages('igraph', repos = "https://cran.rstudio.com")
# install.packages('httr', repos = "https://cran.rstudio.com")
# install.packages('data.table', repos = "https://cran.rstudio.com")
# install.packages('dplyr', repos = "https://cran.rstudio.com")
# install.packages('xml2', repos = "https://cran.rstudio.com")
# You need to load packages every time you run the script or restart R.
library(magrittr)
library(httr)
library(data.table)
library(igraph)
library(dplyr)
library(xml2)
# Set your directory for the project
# You can either enter your filename path within the parentheses below and remove the # creating the comment, or select "Session > Set Working Directory ... Source File Location" in R Studio.
# setwd("Input Directory")

Choose a topic for your search terms

You can decide search terms based on personal interests, research interests, or popular topical areas, among others. You have flexibility in selecting your search term list. For example, you can search for some commercial brands, celebrities, countries, universities, etc. It will be most useful if you choose a collection of words that are not all extremely common. Think about a set of words that might have interesting co-occurrences in articles within the New York Times website. For example, you might be interested in the last names of every Senator involved in a certain political debate, football teams, or cities and their co-occurrence in news articles. Generally speaking, proper nouns are best, but you might have compelling reasons to choose verbs or adjectives. You might want to throw a couple of terms in that aren’t thematically related to make sure you don’t get a totally connected component. The more interesting your network is in terms of differing centrality, distinct components, etc., the easier it will be to do the written analysis. Keep in mind that the Article Search archive is very large; many terms co-occur. You might want to consider two tenuously related subjects. The example file uses four football teams and their home senators, plus a few topical terms.

Create your text input

Create a plain text file with .txt extension in the same directory as the R Markdown Notebook used in this assignment. Make a note of the file name for use in the next code snippet. Place one search term per line, and use 15–20 terms. You’ll also likely want to add quotation marks around your search terms to ensure that you’re only receiving results for the complete term. NOTE: The function will process your terms so that they work in the URL request. You do not need to encode non-alphabetic characters.

The text file cannot include any additional information or characters and it must be a .txt file; Word or RTF documents won’t work.

Analysis

a. Provide a high level overview of the terms you included in the search query.

b. Why did you choose this collection of terms? Were there some specific overarching question—intellectual or extracurricular curiosity—that motivated this collection of terms?

c. How did you decide which terms to use in the search query? Were these terms you intuitively deemed important? Were they culled from a specific source or the result of some separate analysis or search query?

d. What are the insights you hope to glean by looking at the network of terms in terms of individual node metrics, sub-grouping of nodes, overall global network properties?

Working with the API to Collect Your Data

The New York Times controls access to its API by assigning each user a key. Each key has a limited number of calls that can be made within a certain time period. You can read more about the limitations of the API system here.

You will need to create your own API key to complete this assignment. Go to the New York Times developers page and request a key. You will copy that key (received via email) into the api variable below.

# Import your word list
name_of_file <- "NFL.txt" # Creates a variable called name_of_file that you should populate with the name of your text file between quotation marks.
word_list <- read.table(name_of_file, sep = "\n", stringsAsFactors = F) %>% unlist %>% as.vector # Reads the content of your file into a variable.
num_words <- length(word_list) # Creates a variable with the number of words in your list.
url_base <- "https://api.nytimes.com/svc/search/v2/articlesearch.json"
# When you receive the email with your API key, paste it below between the quotation marks.
api <- '76f06c3d16c54280b9233d8f3d76e4bf'

Our first function will gather all of the search terms and their number of hits to be placed in a table. All lines of a function should be run together.

Get_hits_one <- function(keyword1) {
  Sys.sleep(time=3)
  url <- paste0(url_base, "?api-key=", api, "&q=", URLencode(keyword1),"&begin_date=","20160101") # Begin date is in format YYYYMMDD; you can change it if you want only more recent results, for example.
  # The number of results
  print(keyword1)
  hits <- content(GET(url))$response$meta$hits %>% as.numeric
  print(hits)
  # Put results in table
  c(SearchTerm=keyword1,ResultsTotal=hits)
}

Now we will invoke our function to put information from the API into our global environment.

#Create a table of your words and their number of results.
total_table <- t(sapply(word_list,Get_hits_one))
total_table <- as.data.frame(total_table)
total_table$ResultsTotal <- as.numeric(as.character(total_table$ResultsTotal))

If you get zero hits for any of these terms, you should substitute that term for somethign else and rerun the lab up to this point. Next, we will define the function that will collect the article co-occurences network.

Get_hits_two <- function(row_input) {
  keyword1 <- row_input[1]
  keyword2 <- row_input[2]
  url <- paste0(url_base, "?api-key=", api, "&q=", URLencode(keyword1),"+", URLencode(keyword2),"&begin_date=","20160101") #match w/ Begin Date in Get_hits_one.
  # The number of results
  print(paste0(keyword1," ",keyword2)) 
  hits <- content(GET(url))$response$meta$hits %>% as.numeric
  print(hits)
  Sys.sleep(time=3)
  # Put results in table
  c(SearchTerm1=keyword1,SearchTerm2=keyword2,CoOccurrences=hits)
} 

In this next step, we will call the API and collect the co-occurrence network. This may take some time. If you receive “numeric(0)” in any of your resposnes, you’ve likely hit your API key limit and will either need to wait for the calls to reset (24 hours) or request a new key. If you receive the error message “$ operator is invalid for atomic vectors,” you have also hit the API call limit. This could be due to running the script multiple times, or due to hitting too many results based on very common search terms. Request a new API, shorten your word list, and try again. Don’t forget you need to reload your word list from the first part of the Lab in order to get a different set of results! You must also rerun the functions to reassign the API value. If none of your results come back as “0,” you might want to redo your search with the appropriate words.

# Convert the pairs list into a table
pairs_list <- expand.grid(word_list,word_list) %>% filter(Var1 != Var2)
pairs_list <- t(combn(word_list,2))
#Create a network table, run the Get_hits_two function using the pairs lists
network_table <- t(apply(pairs_list,1,Get_hits_two))
#Convert the network table into a dataframe
network_table <- as.data.frame(network_table)
# Read each the content of each item within the $CoOccurreences factor as characters, 
# then force those characters into the "numeric" or "double" type.
network_table$CoOccurrences <- as.numeric(as.character(network_table$CoOccurrences))
# Convert data to data.table type.
total_table <- as.data.table(total_table)
network_table <- as.data.table(network_table)

# Remove zero edges from your network
network_table <- network_table[!CoOccurrences==0] 

# Create a graph object with your data
g_valued <- graph_from_data_frame(d = network_table[,1:3,with=FALSE],directed = FALSE,vertices = total_table)

# If you're having trouble with data collection, you can load the 'NFL Lab Results.RData' file now by clicking the open folder icon on the "Environment"" tab and continue the lab from here. You'll need to figure out what the significance of the terms are yourself, however.
# You should save your data at this point by clicking the floppy disk icon under the "Environment" tab.

Analysis

Is the graph directed or undirected?

How many nodes and links does your network have?

numVertices <- vcount(g_valued)
numVertices
numEdges <- ecount(g_valued)
numEdges

What is the number of possible links in your network?

maxEdges <- numVertices*(numVertices-1)/2
maxEdges

What is the density of your network?

graphDensity <- numEdges/maxEdges # manual calculation
graphDensity
graphDensity1 <- graph.density(g_valued) # using the graph.density function from igraph
graphDensity1

Briefly describe how your choice of dataset may influence your findings. What differences would you expect if you use different search terms? Are the current search terms related to one another? If so, how? Do you think the limitation to one word might skew your answers? (i.e. if you’re interested in Hillary Clinton, but you include “Clinton” as a term, you will get stories that mention Chelsea, Bill, & even P-Funk Allstar George Clinton).

Part 2: Visualize Your Network (20 points)

Let’s start by visualizing the network that we’ve collected from the New York Times Article Search API. We’ll need to choose node colors and set a layout. You can learn more about Fruchterman Reingold layout and other layouts here.

## Learn more about plotting with igraph
?? igraph.plotting
colbar = rainbow(length(word_list)) ## we are selecting different colors to correspond to each word
V(g_valued)$color = colbar
# Set layout here 
L = layout_with_fr(g_valued)  # Fruchterman Reingold
plot(g_valued,vertex.color=V(g_valued)$color, layout = L, vertex.size=6) 

Analysis

In a paragraph, describe the macro-level structure of your graphs based on the Fruchterman Reingold visualization. Is it a giant, connected component, are there distinct sub-components, or are there isolated components? Can you recognize common features of the subcomponents? Does this visualization give you any insight into the co-occurrence patterns of the search-terms? If yes, what? If not, why?

Now we’ll create a second visualization using a different layout.

## You can change the layout by picking one of the other options. Uncomment one of the lines below by erasing the # and running the line. Try to find a layout that gives you different information that Fruchterman Reingold.

 L = layout_with_dh(g_valued) ## Davidson and Harel

# L = layout_with_drl(g_valued) ## Force-directed

# L = layout_with_kk(g_valued) ## Spring
plot(g_valued,vertex.color=V(g_valued)$color, layout = L, vertex.size=6) 

Analysis

In a paragraph, compare and contrast the information given to you by the two different layouts.

Part 3: Community Detection Analysis with R (20 Points)

Identifying subgroups within a network is of great interest to social network researchers, so a variety of algorithms have been developed to identify and measure subgroups. We will use some of R’s built-in tools to identify subgroups and central nodes for visual inspection.

For the remainder of the visualizations we will use the Fruchterman Reingold layout.

L = layout_with_fr(g_valued) 

Cluster the nodes in your network.

# Learn more about the clustering algorithm.
?? cluster_walktrap
cluster <- cluster_walktrap(g_valued)
# Find the number of clusters
membership(cluster)   # affiliation list
length(sizes(cluster)) # number of clusters
# Find the size the each cluster 
# Note that communities with one node are isolates, or have only a single tie
sizes(cluster) 

How many communities have been created?

How many nodes are in each community? In networks containing node attribute information, we can often gain insight into a network by looking at the nodes that get placed in the same partition. For your network, what might each cluster of nodes potentially have in common? Describe each cluster, its membership, and the relationship between nodes in the cluster. Next we visualize the clusters by coloring nodes according to their modularity class.

plot(cluster, g_valued, col = V(g_valued)$color, layout = L, vertex.size=6)

What information does this layout convey? Are the clusters well-separated, or is there a great deal of overlap? Is it easier to identify the common themes among clusters in this layout rather than looking only at the graphs?

What differences are there between nodes in the same cluster and across clusters?

Describe the brokers between any components and cliques. What are common features of these brokers? About how many brokers would you have to remove from your network to “shatter” it into two or more disconnected components?

Part 4: Centrality Visualization & Weighted Values (20 Points)

For each network, you will use centrality metrics to improve your visualization. You may need to adjust the size parameter to make your network more easily visible.

Degree Centrality

totalDegree <- degree(g_valued,mode="all")
sort(totalDegree,decreasing=TRUE)[1:5]
g2 <- g_valued
V(g2)$size <- totalDegree*2 #can adjust the number if nodes are too big
plot(g2, layout = L, vertex.label=NA)

Briefly explain degree centrality and why nodes are more or less central in the network.

Weighted Degree Centrality

wd <- graph.strength(g_valued,weights = E(g_valued)$CoOccurrences)
sort(wd,decreasing=TRUE)[1:5]
wg2 <- g_valued
V(wg2)$size <- wd*.1 # adjust the number if nodes are too big
plot(wg2, layout = L, vertex.label=NA, edge.width=sqrt(E(g_valued)$CoOccurrences)) #taking the square root is a good way to make a large range of numbers visible in an edge. Otherwise edges tend to cover up all the other edges and obscure the relationships.

What does the addition of weighted degree and edge information tell you about your graph?

Betweenness Centrality

b <- betweenness(g_valued,directed=TRUE)
sort(b,decreasing=TRUE)[1:5]
g4 <- g_valued
V(g4)$size <- b*1.2#can adjust the number
plot(g4, layout = L, vertex.label=NA)

Briefly explain betweenness centrality and why nodes are more or less central in the network.

Weighted Betweenness Centrality

wbtwn <- betweenness(g_valued,weights = E(g_valued)$CoOccurrences)
sort(wbtwn,decreasing=TRUE)[1:5]
wBtwnG <- g_valued
V(wBtwnG)$size <- wbtwn*.5 # adjust the number if nodes are too big
plot(wBtwnG, layout = L, vertex.label=NA, edge.width=sqrt(E(g_valued)$CoOccurrences)) #taking the square root is a good way to make a large range of numbers visible in an edge.

What does the addition of weighted degree and edge information tell you about your graph?

Closeness Centrality

c <- closeness(g_valued)
sort(c,decreasing=TRUE)[1:5]
g5 <- g_valued
V(g5)$size <- c*500  #can adjust the number
plot(g5, layout = L, vertex.label=NA)

Briefly explain closeness centrality and why nodes are more or less central in the network.

Weighted Closeness Centrality

wClsnss <- closeness(g_valued,weights = E(g_valued)$CoOccurrences)
sort(wClsnss,decreasing=TRUE)[1:5]
wClsnssG <- g_valued
V(wClsnssG)$size <- wClsnss*1000 # adjust the number if nodes are too big
plot(wClsnssG, layout = L, vertex.label=NA, edge.width=sqrt(E(g_valued)$CoOccurrences)) #taking the square root is a good way to make a large range of numbers visible in an edge.

What does the addition of weighted degree and edge information tell you about your graph?

Eigenvector Centrality

eigc <- eigen_centrality(g_valued,directed=TRUE)
sort(eigc$vector,decreasing=TRUE)[1:5]
g6 <- g_valued
V(g6)$size <- eigc$vector*50 #can adjust the number
plot(g6, layout = L, vertex.label=NA)

Briefly explain eigenvector centrality and why nodes are more or less central in the network.

Analysis

Choose the visualization that you think is most interesting and briefly explain what it tells you about a central node in your network. Discuss the type of centrality, and what that node’s centrality score tells you about the search co-occurrence network.

Briefly discuss an interesting difference between types of centrality for your network.

Global Network Metrics with R

Compute the network centralization scores for your network for degree, betweenness, closeness, and eigenvector centrality.

# Degree centralization
centralization.degree(g_valued,normalized = TRUE)

# Betweenness centralization
centralization.betweenness(g_valued,normalized = TRUE)

# Closeness centralization 
centralization.closeness(g_valued,normalized = TRUE)

# Eigenvector centralization 
centralization.evcent(g_valued,normalized = TRUE)

Record the centralization score of each centrality measure.

Briefly explain what the centralization of a network is.

Compare the centralization scores above with the graphs you created where the nodes are scaled by centrality. Describe the appearance of more centralized v. less centralized networks.

Part 5. Power Laws & Small Worlds (20)

Power Laws

Networks often demonstrate power law distributions. Plot the degree distribution of the nodes in your base graph.

# Calculate degree distribution
deg <- degree(g_valued,v=V(g_valued), mode="all")
deg

# Degree distribution is the cumulative frequency of nodes with a given degree
deg_distr <-degree.distribution(g_valued, cumulative=T, mode="all")
deg_distr
plot(deg_distr, ylim=c(.01,10), bg="black",pch=21, xlab="Degree", ylab="Cumulative Frequency") #You may need to adjust the ylim to a larger or smaller number to make the graph show more data.

Test whether it’s approximately a power law, estimate log f (k) = log a − c log k. “This says that if we have a power-law relationship, and we plot log f (k) as a function of log k, then we should see a straight line: −c will be the slope, and log a will be the y-intercept. Such a “log-log” plot thus provides a quick way to see if one’s data exhibits an approximate power-law: it is easy to see if one has an approximately straight line, and one can read off the exponent from the slope.” (E&K, Chapter 18, p.546).

power <- power.law.fit(deg_distr)
power
plot(deg_distr, log="xy", ylim=c(.01,10), bg="black",pch=21, xlab="Degree", ylab="Cumulative Frequency")

Does your network exhibit a power law distribution of degree centrality?

Small Worlds

Networks often demonstrate small world characteristics. Compute the average clustering coefficient (ACC) and the characteristic path length (CPL).

# Average clustering coefficient (ACC)
transitivity(g_valued, type = c("average"))

# Characteristic path length (CPL)
average.path.length(g_valued)

Compute the ACC and CPL for 100 random networks with the same number of nodes and ties as your test network.

accSum <- 0
cplSum <- 0
for (i in 1:100){
  grph <- erdos.renyi.game(numVertices, numEdges, type = "gnm")
  accSum <- accSum + transitivity(grph, type = c("average"))
  cplSum <- cplSum + average.path.length(grph)
}
accSum/100
cplSum/100

Based on these data, would you conclude that the observed network demonstrates small world properties? Why or why not?

Wrapping up

To complete the lab, make sure output/previews have been generated for each block of code. Then click the “Publish” button on the upper right hand corner of this screen and sign up for an RPubs account. Submit the URL of the published, completed lab on Canvas.

LS0tDQp0aXRsZTogJ0Rlc2NyaXB0aXZlIEFuYWx5dGljIEV4ZXJjaXNlIDE6IFZpc3VhbGl6aW5nIGFuZCBJbnRlcnByZXRpbmcgTmV0d29ya3MnDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgd29yZF9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQoqKlNOQSBHcmFkIFNlbWluYXIsIEZhbGwgMjAxNyoqDQoqKkR1ZToqKiBPY3RvYmVyIDI0dGgsIDExOjU5IHBtDQoqKk5hbWUgb2YgU3R1ZGVudCoqOiANCg0KVGhlIHB1cnBvc2Ugb2YgdGhpcyBsYWIgaXMgdG8gZGV2ZWxvcCB5b3VyIGZhbWlsaWFyaXR5IGNvbmR1Y3RpbmcgZGVzY3JpcHRpdmUgbmV0d29yayBhbmFseXNpcyB1c2luZyB0aGUgc3RhdGlzdGljYWwgc29mdHdhcmUgcGFja2FnZSBSLiBUaGlzIGFzc2lnbm1lbnQgd2lsbCBtYWtlIHVzZSBvZiBhIGRhdGEgc2V0IHlvdSBjb2xsZWN0IGJ5IGRlZmluaW5nIGEgc2VhcmNoIHF1ZXJ5IChhIGNvbGxlY3Rpb24gb2YgeW91ciB1c2VyLWRlZmluZWQgc2VhcmNoIHRlcm1zKSBmcm9tIHRoZSAqW05ldyBZb3JrIFRpbWVzXSh3d3cubnl0aW1lcy5jb20pKidzIEFydGljbGUgU2VhcmNoIFtBcHBsaWNhdGlvbiBQcm9ncmFtbWluZyBJbnRlcmZhY2VdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0FwcGxpY2F0aW9uX3Byb2dyYW1taW5nX2ludGVyZmFjZSkuIE5ldHdvcmtzIGFyZSBnZW5lcmF0ZWQgZnJvbSB0aGUgY28tb2NjdXJyZW5jZXMgYmV0d2VlbiBzZWFyY2ggdGVybXMgaW5jbHVkZWQgaW4gdGhlIHNhbWUgc2VhcmNoIHF1ZXJ5LiBGb3IgZXhhbXBsZSwgYSBsaW5rIGV4aXN0cyBiZXR3ZWVuIOKAnGFwcGxl4oCdIGFuZCDigJxvcmFuZ2XigJ0gaWYgdGhlcmUgYXJlIGFydGljbGVzIGluIHRoZSAqTmV3IFlvcmsgVGltZXMqIHRoYXQgY29udGFpbmVkIHRoZXNlIHR3byB0ZXJtcy4gIFlvdSB3aWxsIGJlIHZpc3VhbGl6aW5nIGFuZCBpbnRlcnByZXRpbmcgaW5kaXZpZHVhbCBhbmQgZ2xvYmFsIG5ldHdvcmsgcHJvcGVydGllcyBvZiB0aGlzIG5ldHdvcmsuDQoNCllvdSB3aWxsIGJlIGdyYWRlZCBwcmltYXJpbHkgb24gdGhlIGNvbXBsZXRlbmVzcyBhbmQgYWNjdXJhY3kgb2YgeW91ciByZXNwb25zZXMsIGJ1dCB0aGUgY2xhcml0eSBvZiB0aGUgcHJlcGFyZWQgcmVwb3J0IHdpbGwgYWxzbyBhZmZlY3QgeW91ciBncmFkZS4gIFdoaWxlIHN0dWRlbnRzIG1heSB3b3JrIHRvZ2V0aGVyIHRvIHBlcmZvcm0gdGhlIGFuYWx5c2lzLCBlYWNoIHN0dWRlbnQgbXVzdCBzdWJtaXQgaGlzIG9yIGhlciBvd24gcmVwb3J0IGFuZCBpcyByZXNwb25zaWJsZSBmb3Igd3JpdGluZyB0aGUgbmFycmF0aXZlIGluIHRoZSByZXBvcnQuIFlvdSBtdXN0IGFuc3dlciBhbGwgb2YgdGhlIGJvbGRlZCBxdWVzdGlvbnMuDQoNCiMgUGFydCAxOiBDb2xsZWN0IE5ldHdvcmsgRGF0YSAoMjAgcHRzKQ0KDQpGb3IgdGhpcyBsYWIsIHlvdSB3aWxsIHNlYXJjaCB0aGUgKk5ldyBZb3JrIFRpbWVzKiwgc2F2ZSB0aGF0IGRhdGEsIGNyZWF0ZSBuZXR3b3JrcyBmcm9tIHRoYXQgZGF0YSwgY29tcGFyZSB0aGUgZGlmZmVyZW5jZXMgYW1vbmcgbmV0d29ya3MsIGFuZCBkZW1vbnN0cmF0ZSB5b3VyIHByb2ZpY2llbmN5IHdpdGggYmFzaWMgbmV0d29yayBkZXNjcmlwdGl2ZSBzdGF0aXN0aWNzLg0KDQojIyBMb2FkaW5nIGFuZCBJbnN0YWxsaW5nIFBhY2thZ2VzLCBTZXQgV29ya2luZyBEaXJlY3RvcnkNCg0KV2hlbiB3b3JraW5nIHdpdGggUiwgeW91IHNob3VsZCBydW4gZWFjaCBsaW5lIG9mIGNvZGUgaW5kaXZpZHVhbGx5LCB1bmxlc3MgaXQgaXMgcGFydCBvZiBhIGZ1bmN0aW9uIGRlZmluaXRpb24sIHNvIHlvdSBjYW4gc2VlIHRoZSByZXN1bHRzLiBHZW5lcmFsbHkgc3BlYWtpbmcsIGFueSBsaW5lIG9mIGNvZGUgdGhhdCBpbmNsdWRlcyAneycgKHRoZSBiZWdpbm5pbmcgb2YgYSBmdW5jdGlvbiBkZWZpbml0aW9uKSBzaG91bGQgYmUgcnVuIHdpdGggYWxsIHRoZSBvdGhlciBsaW5lcyB1bnRpbCB5b3UgaGl0ICd9Jy4NCg0KYGBge3J9DQojIExpbmVzIHRoYXQgc3RhcnQgd2l0aCBhIGhhc2h0YWcvcG91bmQgc3ltYm9sLCBsaWtlIHRoaXMgb25lLCBhcmUgY29tbWVudCBsaW5lcy4gQ29tbWVudCBsaW5lcyBhcmUgaWdub3JlZCBieSBSIHdoZW4gaXQgaXMgaW50ZXJwcmV0aW5nIGNvZGUuDQojIFlvdSBvbmx5IG5lZWQgdG8gaW5zdGFsbCBwYWNrYWdlcyBvbmNlLiBSZW1vdmUgdGhlICMgaW4gZnJvbnQgb2YgZWFjaCBsaW5lIGFuZCB0aGVuIHJ1biBpdCB0byBpbnN0YWxsIGVhY2ggcGFja2FnZS4gQWZ0ZXIgc3VjY2Vzc2Z1bCBpbnN0YWxsYXRpb24sIGRlbGV0ZSB0aGUgbGluZSBvZiBjb2RlIG9yIHJlcGxhY2UgdGhlICNzIHNvIHRoZSBSIE5vdGVib29rIGRvZXNuJ3QgcnVuIGludG8gcHJvYmxlbXMuDQojIGluc3RhbGwucGFja2FnZXMoJ21hZ3JpdHRyJywgcmVwb3MgPSAiaHR0cHM6Ly9jcmFuLnJzdHVkaW8uY29tIikNCiMgaW5zdGFsbC5wYWNrYWdlcygnaWdyYXBoJywgcmVwb3MgPSAiaHR0cHM6Ly9jcmFuLnJzdHVkaW8uY29tIikNCiMgaW5zdGFsbC5wYWNrYWdlcygnaHR0cicsIHJlcG9zID0gImh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbSIpDQojIGluc3RhbGwucGFja2FnZXMoJ2RhdGEudGFibGUnLCByZXBvcyA9ICJodHRwczovL2NyYW4ucnN0dWRpby5jb20iKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCdkcGx5cicsIHJlcG9zID0gImh0dHBzOi8vY3Jhbi5yc3R1ZGlvLmNvbSIpDQojIGluc3RhbGwucGFja2FnZXMoJ3htbDInLCByZXBvcyA9ICJodHRwczovL2NyYW4ucnN0dWRpby5jb20iKQ0KIyBZb3UgbmVlZCB0byBsb2FkIHBhY2thZ2VzIGV2ZXJ5IHRpbWUgeW91IHJ1biB0aGUgc2NyaXB0IG9yIHJlc3RhcnQgUi4NCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KGh0dHIpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KGlncmFwaCkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHhtbDIpDQojIFNldCB5b3VyIGRpcmVjdG9yeSBmb3IgdGhlIHByb2plY3QNCiMgWW91IGNhbiBlaXRoZXIgZW50ZXIgeW91ciBmaWxlbmFtZSBwYXRoIHdpdGhpbiB0aGUgcGFyZW50aGVzZXMgYmVsb3cgYW5kIHJlbW92ZSB0aGUgIyBjcmVhdGluZyB0aGUgY29tbWVudCwgb3Igc2VsZWN0ICJTZXNzaW9uID4gU2V0IFdvcmtpbmcgRGlyZWN0b3J5IC4uLiBTb3VyY2UgRmlsZSBMb2NhdGlvbiIgaW4gUiBTdHVkaW8uDQojIHNldHdkKCJJbnB1dCBEaXJlY3RvcnkiKQ0KYGBgDQoNCiMjIENob29zZSBhIHRvcGljIGZvciB5b3VyIHNlYXJjaCB0ZXJtcw0KDQpZb3UgY2FuIGRlY2lkZSBzZWFyY2ggdGVybXMgYmFzZWQgb24gcGVyc29uYWwgaW50ZXJlc3RzLCByZXNlYXJjaCBpbnRlcmVzdHMsIG9yIHBvcHVsYXIgdG9waWNhbCBhcmVhcywgYW1vbmcgb3RoZXJzLiBZb3UgaGF2ZSBmbGV4aWJpbGl0eSBpbiBzZWxlY3RpbmcgeW91ciBzZWFyY2ggdGVybSBsaXN0LiBGb3IgZXhhbXBsZSwgeW91IGNhbiBzZWFyY2ggZm9yIHNvbWUgY29tbWVyY2lhbCBicmFuZHMsIGNlbGVicml0aWVzLCBjb3VudHJpZXMsIHVuaXZlcnNpdGllcywgZXRjLiBJdCB3aWxsIGJlIG1vc3QgdXNlZnVsIGlmIHlvdSBjaG9vc2UgYSBjb2xsZWN0aW9uIG9mIHdvcmRzIHRoYXQgYXJlIG5vdCBhbGwgZXh0cmVtZWx5IGNvbW1vbi4gVGhpbmsgYWJvdXQgYSBzZXQgb2Ygd29yZHMgdGhhdCBtaWdodCBoYXZlIGludGVyZXN0aW5nIGNvLW9jY3VycmVuY2VzIGluIGFydGljbGVzIHdpdGhpbiB0aGUgKk5ldyBZb3JrIFRpbWVzKiB3ZWJzaXRlLiBGb3IgZXhhbXBsZSwgeW91IG1pZ2h0IGJlIGludGVyZXN0ZWQgaW4gdGhlIGxhc3QgbmFtZXMgb2YgZXZlcnkgU2VuYXRvciBpbnZvbHZlZCBpbiBhIGNlcnRhaW4gcG9saXRpY2FsIGRlYmF0ZSwgZm9vdGJhbGwgdGVhbXMsIG9yIGNpdGllcyBhbmQgdGhlaXIgY28tb2NjdXJyZW5jZSBpbiBuZXdzIGFydGljbGVzLiBHZW5lcmFsbHkgc3BlYWtpbmcsIHByb3BlciBub3VucyBhcmUgYmVzdCwgYnV0IHlvdSBtaWdodCBoYXZlIGNvbXBlbGxpbmcgcmVhc29ucyB0byBjaG9vc2UgdmVyYnMgb3IgYWRqZWN0aXZlcy4gWW91IG1pZ2h0IHdhbnQgdG8gdGhyb3cgYSBjb3VwbGUgb2YgdGVybXMgaW4gdGhhdCBhcmVuJ3QgdGhlbWF0aWNhbGx5IHJlbGF0ZWQgdG8gbWFrZSBzdXJlIHlvdSBkb24ndCBnZXQgYSB0b3RhbGx5IGNvbm5lY3RlZCBjb21wb25lbnQuIFRoZSBtb3JlIGludGVyZXN0aW5nIHlvdXIgbmV0d29yayBpcyBpbiB0ZXJtcyBvZiBkaWZmZXJpbmcgY2VudHJhbGl0eSwgZGlzdGluY3QgY29tcG9uZW50cywgZXRjLiwgdGhlIGVhc2llciBpdCB3aWxsIGJlIHRvIGRvIHRoZSB3cml0dGVuIGFuYWx5c2lzLiBLZWVwIGluIG1pbmQgdGhhdCB0aGUgQXJ0aWNsZSBTZWFyY2ggYXJjaGl2ZSBpcyB2ZXJ5IGxhcmdlOyBtYW55IHRlcm1zIGNvLW9jY3VyLiBZb3UgbWlnaHQgd2FudCB0byBjb25zaWRlciB0d28gdGVudW91c2x5IHJlbGF0ZWQgc3ViamVjdHMuIFRoZSBleGFtcGxlIGZpbGUgdXNlcyBmb3VyIGZvb3RiYWxsIHRlYW1zIGFuZCB0aGVpciBob21lIHNlbmF0b3JzLCBwbHVzIGEgZmV3IHRvcGljYWwgdGVybXMuDQoNCiMjIENyZWF0ZSB5b3VyIHRleHQgaW5wdXQNCg0KQ3JlYXRlIGEgcGxhaW4gdGV4dCBmaWxlIHdpdGggLnR4dCBleHRlbnNpb24gaW4gdGhlIHNhbWUgZGlyZWN0b3J5IGFzIHRoZSBSIE1hcmtkb3duIE5vdGVib29rIHVzZWQgaW4gdGhpcyBhc3NpZ25tZW50LiBNYWtlIGEgbm90ZSBvZiB0aGUgZmlsZSBuYW1lIGZvciB1c2UgaW4gdGhlIG5leHQgY29kZSBzbmlwcGV0LiBQbGFjZSBvbmUgc2VhcmNoIHRlcm0gcGVyIGxpbmUsIGFuZCB1c2UgMTXigJMyMCB0ZXJtcy4gIFlvdSdsbCBhbHNvIGxpa2VseSB3YW50IHRvIGFkZCBxdW90YXRpb24gbWFya3MgYXJvdW5kIHlvdXIgc2VhcmNoIHRlcm1zIHRvIGVuc3VyZSB0aGF0IHlvdSdyZSBvbmx5IHJlY2VpdmluZyByZXN1bHRzIGZvciB0aGUgY29tcGxldGUgdGVybS4gTk9URTogVGhlIGZ1bmN0aW9uIHdpbGwgcHJvY2VzcyB5b3VyIHRlcm1zIHNvIHRoYXQgdGhleSB3b3JrIGluIHRoZSBVUkwgcmVxdWVzdC4gWW91IGRvIG5vdCBuZWVkIHRvIGVuY29kZSBub24tYWxwaGFiZXRpYyBjaGFyYWN0ZXJzLg0KDQpUaGUgdGV4dCBmaWxlIGNhbm5vdCBpbmNsdWRlIGFueSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIG9yIGNoYXJhY3RlcnMgYW5kIGl0IG11c3QgYmUgYSAudHh0IGZpbGU7IFdvcmQgb3IgUlRGIGRvY3VtZW50cyB3b27igJl0IHdvcmsuDQoNCiMjIEFuYWx5c2lzDQoNCioqYS4JUHJvdmlkZSBhIGhpZ2ggbGV2ZWwgb3ZlcnZpZXcgb2YgdGhlIHRlcm1zIHlvdSBpbmNsdWRlZCBpbiB0aGUgc2VhcmNoIHF1ZXJ5LioqDQoNCioqYi4JV2h5IGRpZCB5b3UgY2hvb3NlIHRoaXMgY29sbGVjdGlvbiBvZiB0ZXJtcz8gIFdlcmUgdGhlcmUgc29tZSBzcGVjaWZpYyBvdmVyYXJjaGluZyBxdWVzdGlvbuKAlGludGVsbGVjdHVhbCBvciBleHRyYWN1cnJpY3VsYXIgY3VyaW9zaXR54oCUdGhhdCBtb3RpdmF0ZWQgdGhpcyBjb2xsZWN0aW9uIG9mIHRlcm1zPyoqDQoNCioqYy4JSG93IGRpZCB5b3UgZGVjaWRlIHdoaWNoIHRlcm1zIHRvIHVzZSBpbiB0aGUgc2VhcmNoIHF1ZXJ5PyBXZXJlIHRoZXNlIHRlcm1zIHlvdSBpbnR1aXRpdmVseSBkZWVtZWQgaW1wb3J0YW50PyBXZXJlIHRoZXkgY3VsbGVkIGZyb20gYSBzcGVjaWZpYyBzb3VyY2Ugb3IgdGhlIHJlc3VsdCBvZiBzb21lIHNlcGFyYXRlIGFuYWx5c2lzIG9yIHNlYXJjaCBxdWVyeT8qKg0KDQoqKmQuCVdoYXQgYXJlIHRoZSBpbnNpZ2h0cyB5b3UgaG9wZSB0byBnbGVhbiBieSBsb29raW5nIGF0IHRoZSBuZXR3b3JrIG9mIHRlcm1zIGluIHRlcm1zIG9mIGluZGl2aWR1YWwgbm9kZSBtZXRyaWNzLCBzdWItZ3JvdXBpbmcgb2Ygbm9kZXMsIG92ZXJhbGwgZ2xvYmFsIG5ldHdvcmsgcHJvcGVydGllcz8qKg0KDQojIyBXb3JraW5nIHdpdGggdGhlIEFQSSB0byBDb2xsZWN0IFlvdXIgRGF0YQ0KVGhlICpOZXcgWW9yayBUaW1lcyogY29udHJvbHMgYWNjZXNzIHRvIGl0cyBBUEkgYnkgYXNzaWduaW5nIGVhY2ggdXNlciBhIGtleS4gRWFjaCBrZXkgaGFzIGEgbGltaXRlZCBudW1iZXIgb2YgY2FsbHMgdGhhdCBjYW4gYmUgbWFkZSB3aXRoaW4gYSBjZXJ0YWluIHRpbWUgcGVyaW9kLiBZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCB0aGUgbGltaXRhdGlvbnMgb2YgdGhlIEFQSSBzeXN0ZW0gW2hlcmVdKGh0dHA6Ly9kZXZlbG9wZXIubnl0aW1lcy5jb20vYXJ0aWNsZV9zZWFyY2hfdjIuanNvbiMpLg0KDQpZb3Ugd2lsbCBuZWVkIHRvIGNyZWF0ZSB5b3VyIG93biBBUEkga2V5IHRvIGNvbXBsZXRlIHRoaXMgYXNzaWdubWVudC4gR28gdG8gdGhlICpOZXcgWW9yayBUaW1lcyogW2RldmVsb3BlcnMgcGFnZV0oaHR0cHM6Ly9kZXZlbG9wZXIubnl0aW1lcy5jb20vc2lnbnVwKSBhbmQgcmVxdWVzdCBhIGtleS4gWW91IHdpbGwgY29weSB0aGF0IGtleSAocmVjZWl2ZWQgdmlhIGVtYWlsKSBpbnRvIHRoZSBhcGkgdmFyaWFibGUgYmVsb3cuDQoNCmBgYHtyfQ0KIyBJbXBvcnQgeW91ciB3b3JkIGxpc3QNCm5hbWVfb2ZfZmlsZSA8LSAiTkZMLnR4dCIgIyBDcmVhdGVzIGEgdmFyaWFibGUgY2FsbGVkIG5hbWVfb2ZfZmlsZSB0aGF0IHlvdSBzaG91bGQgcG9wdWxhdGUgd2l0aCB0aGUgbmFtZSBvZiB5b3VyIHRleHQgZmlsZSBiZXR3ZWVuIHF1b3RhdGlvbiBtYXJrcy4NCndvcmRfbGlzdCA8LSByZWFkLnRhYmxlKG5hbWVfb2ZfZmlsZSwgc2VwID0gIlxuIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpICU+JSB1bmxpc3QgJT4lIGFzLnZlY3RvciAjIFJlYWRzIHRoZSBjb250ZW50IG9mIHlvdXIgZmlsZSBpbnRvIGEgdmFyaWFibGUuDQpudW1fd29yZHMgPC0gbGVuZ3RoKHdvcmRfbGlzdCkgIyBDcmVhdGVzIGEgdmFyaWFibGUgd2l0aCB0aGUgbnVtYmVyIG9mIHdvcmRzIGluIHlvdXIgbGlzdC4NCnVybF9iYXNlIDwtICJodHRwczovL2FwaS5ueXRpbWVzLmNvbS9zdmMvc2VhcmNoL3YyL2FydGljbGVzZWFyY2guanNvbiINCiMgV2hlbiB5b3UgcmVjZWl2ZSB0aGUgZW1haWwgd2l0aCB5b3VyIEFQSSBrZXksIHBhc3RlIGl0IGJlbG93IGJldHdlZW4gdGhlIHF1b3RhdGlvbiBtYXJrcy4NCmFwaSA8LSAnNzZmMDZjM2QxNmM1NDI4MGI5MjMzZDhmM2Q3NmU0YmYnDQpgYGANCg0KT3VyIGZpcnN0IGZ1bmN0aW9uIHdpbGwgZ2F0aGVyIGFsbCBvZiB0aGUgc2VhcmNoIHRlcm1zIGFuZCB0aGVpciBudW1iZXIgb2YgaGl0cyB0byBiZSBwbGFjZWQgaW4gYSB0YWJsZS4gQWxsIGxpbmVzIG9mIGEgZnVuY3Rpb24gc2hvdWxkIGJlIHJ1biB0b2dldGhlci4NCg0KYGBge3J9DQpHZXRfaGl0c19vbmUgPC0gZnVuY3Rpb24oa2V5d29yZDEpIHsNCiAgU3lzLnNsZWVwKHRpbWU9MykNCiAgdXJsIDwtIHBhc3RlMCh1cmxfYmFzZSwgIj9hcGkta2V5PSIsIGFwaSwgIiZxPSIsIFVSTGVuY29kZShrZXl3b3JkMSksIiZiZWdpbl9kYXRlPSIsIjIwMTYwMTAxIikgIyBCZWdpbiBkYXRlIGlzIGluIGZvcm1hdCBZWVlZTU1ERDsgeW91IGNhbiBjaGFuZ2UgaXQgaWYgeW91IHdhbnQgb25seSBtb3JlIHJlY2VudCByZXN1bHRzLCBmb3IgZXhhbXBsZS4NCiAgIyBUaGUgbnVtYmVyIG9mIHJlc3VsdHMNCiAgcHJpbnQoa2V5d29yZDEpDQogIGhpdHMgPC0gY29udGVudChHRVQodXJsKSkkcmVzcG9uc2UkbWV0YSRoaXRzICU+JSBhcy5udW1lcmljDQogIHByaW50KGhpdHMpDQogICMgUHV0IHJlc3VsdHMgaW4gdGFibGUNCiAgYyhTZWFyY2hUZXJtPWtleXdvcmQxLFJlc3VsdHNUb3RhbD1oaXRzKQ0KfQ0KYGBgDQoNCk5vdyB3ZSB3aWxsIGludm9rZSBvdXIgZnVuY3Rpb24gdG8gcHV0IGluZm9ybWF0aW9uIGZyb20gdGhlIEFQSSBpbnRvIG91ciBnbG9iYWwgZW52aXJvbm1lbnQuDQoNCmBgYHtyfQ0KI0NyZWF0ZSBhIHRhYmxlIG9mIHlvdXIgd29yZHMgYW5kIHRoZWlyIG51bWJlciBvZiByZXN1bHRzLg0KdG90YWxfdGFibGUgPC0gdChzYXBwbHkod29yZF9saXN0LEdldF9oaXRzX29uZSkpDQp0b3RhbF90YWJsZSA8LSBhcy5kYXRhLmZyYW1lKHRvdGFsX3RhYmxlKQ0KdG90YWxfdGFibGUkUmVzdWx0c1RvdGFsIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHRvdGFsX3RhYmxlJFJlc3VsdHNUb3RhbCkpDQpgYGANCklmIHlvdSBnZXQgemVybyBoaXRzIGZvciBhbnkgb2YgdGhlc2UgdGVybXMsIHlvdSBzaG91bGQgc3Vic3RpdHV0ZSB0aGF0IHRlcm0gZm9yIHNvbWV0aGlnbiBlbHNlIGFuZCByZXJ1biB0aGUgbGFiIHVwIHRvIHRoaXMgcG9pbnQuDQpOZXh0LCB3ZSB3aWxsIGRlZmluZSB0aGUgZnVuY3Rpb24gdGhhdCB3aWxsIGNvbGxlY3QgdGhlIGFydGljbGUgY28tb2NjdXJlbmNlcyBuZXR3b3JrLg0KYGBge3J9DQpHZXRfaGl0c190d28gPC0gZnVuY3Rpb24ocm93X2lucHV0KSB7DQogIGtleXdvcmQxIDwtIHJvd19pbnB1dFsxXQ0KICBrZXl3b3JkMiA8LSByb3dfaW5wdXRbMl0NCiAgdXJsIDwtIHBhc3RlMCh1cmxfYmFzZSwgIj9hcGkta2V5PSIsIGFwaSwgIiZxPSIsIFVSTGVuY29kZShrZXl3b3JkMSksIisiLCBVUkxlbmNvZGUoa2V5d29yZDIpLCImYmVnaW5fZGF0ZT0iLCIyMDE2MDEwMSIpICNtYXRjaCB3LyBCZWdpbiBEYXRlIGluIEdldF9oaXRzX29uZS4NCiAgIyBUaGUgbnVtYmVyIG9mIHJlc3VsdHMNCiAgcHJpbnQocGFzdGUwKGtleXdvcmQxLCIgIixrZXl3b3JkMikpIA0KICBoaXRzIDwtIGNvbnRlbnQoR0VUKHVybCkpJHJlc3BvbnNlJG1ldGEkaGl0cyAlPiUgYXMubnVtZXJpYw0KICBwcmludChoaXRzKQ0KICBTeXMuc2xlZXAodGltZT0zKQ0KICAjIFB1dCByZXN1bHRzIGluIHRhYmxlDQogIGMoU2VhcmNoVGVybTE9a2V5d29yZDEsU2VhcmNoVGVybTI9a2V5d29yZDIsQ29PY2N1cnJlbmNlcz1oaXRzKQ0KfSANCmBgYA0KDQpJbiB0aGlzIG5leHQgc3RlcCwgd2Ugd2lsbCBjYWxsIHRoZSBBUEkgYW5kIGNvbGxlY3QgdGhlIGNvLW9jY3VycmVuY2UgbmV0d29yay4gVGhpcyBtYXkgdGFrZSBzb21lIHRpbWUuIElmIHlvdSByZWNlaXZlICJudW1lcmljKDApIiBpbiBhbnkgb2YgeW91ciByZXNwb3NuZXMsIHlvdSd2ZSBsaWtlbHkgaGl0IHlvdXIgQVBJIGtleSBsaW1pdCBhbmQgd2lsbCBlaXRoZXIgbmVlZCB0byB3YWl0IGZvciB0aGUgY2FsbHMgdG8gcmVzZXQgKDI0IGhvdXJzKSBvciByZXF1ZXN0IGEgbmV3IGtleS4gSWYgeW91IHJlY2VpdmUgdGhlIGVycm9yIG1lc3NhZ2UgIiQgb3BlcmF0b3IgaXMgaW52YWxpZCBmb3IgYXRvbWljIHZlY3RvcnMsIiB5b3UgaGF2ZSBhbHNvIGhpdCB0aGUgQVBJIGNhbGwgbGltaXQuIFRoaXMgY291bGQgYmUgZHVlIHRvIHJ1bm5pbmcgdGhlIHNjcmlwdCBtdWx0aXBsZSB0aW1lcywgb3IgZHVlIHRvIGhpdHRpbmcgdG9vIG1hbnkgcmVzdWx0cyBiYXNlZCBvbiB2ZXJ5IGNvbW1vbiBzZWFyY2ggdGVybXMuIFJlcXVlc3QgYSBuZXcgQVBJLCBzaG9ydGVuIHlvdXIgd29yZCBsaXN0LCBhbmQgdHJ5IGFnYWluLiBEb24ndCBmb3JnZXQgeW91IG5lZWQgdG8gcmVsb2FkIHlvdXIgd29yZCBsaXN0IGZyb20gdGhlIGZpcnN0IHBhcnQgb2YgdGhlIExhYiBpbiBvcmRlciB0byBnZXQgYSBkaWZmZXJlbnQgc2V0IG9mIHJlc3VsdHMhIFlvdSBtdXN0IGFsc28gcmVydW4gdGhlIGZ1bmN0aW9ucyB0byByZWFzc2lnbiB0aGUgQVBJIHZhbHVlLiBJZiBub25lIG9mIHlvdXIgcmVzdWx0cyBjb21lIGJhY2sgYXMgIjAsIiB5b3UgbWlnaHQgd2FudCB0byByZWRvIHlvdXIgc2VhcmNoIHdpdGggdGhlIGFwcHJvcHJpYXRlIHdvcmRzLg0KDQpgYGB7cn0NCiMgQ29udmVydCB0aGUgcGFpcnMgbGlzdCBpbnRvIGEgdGFibGUNCnBhaXJzX2xpc3QgPC0gZXhwYW5kLmdyaWQod29yZF9saXN0LHdvcmRfbGlzdCkgJT4lIGZpbHRlcihWYXIxICE9IFZhcjIpDQpwYWlyc19saXN0IDwtIHQoY29tYm4od29yZF9saXN0LDIpKQ0KI0NyZWF0ZSBhIG5ldHdvcmsgdGFibGUsIHJ1biB0aGUgR2V0X2hpdHNfdHdvIGZ1bmN0aW9uIHVzaW5nIHRoZSBwYWlycyBsaXN0cw0KbmV0d29ya190YWJsZSA8LSB0KGFwcGx5KHBhaXJzX2xpc3QsMSxHZXRfaGl0c190d28pKQ0KI0NvbnZlcnQgdGhlIG5ldHdvcmsgdGFibGUgaW50byBhIGRhdGFmcmFtZQ0KbmV0d29ya190YWJsZSA8LSBhcy5kYXRhLmZyYW1lKG5ldHdvcmtfdGFibGUpDQojIFJlYWQgZWFjaCB0aGUgY29udGVudCBvZiBlYWNoIGl0ZW0gd2l0aGluIHRoZSAkQ29PY2N1cnJlZW5jZXMgZmFjdG9yIGFzIGNoYXJhY3RlcnMsIA0KIyB0aGVuIGZvcmNlIHRob3NlIGNoYXJhY3RlcnMgaW50byB0aGUgIm51bWVyaWMiIG9yICJkb3VibGUiIHR5cGUuDQpuZXR3b3JrX3RhYmxlJENvT2NjdXJyZW5jZXMgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobmV0d29ya190YWJsZSRDb09jY3VycmVuY2VzKSkNCiMgQ29udmVydCBkYXRhIHRvIGRhdGEudGFibGUgdHlwZS4NCnRvdGFsX3RhYmxlIDwtIGFzLmRhdGEudGFibGUodG90YWxfdGFibGUpDQpuZXR3b3JrX3RhYmxlIDwtIGFzLmRhdGEudGFibGUobmV0d29ya190YWJsZSkNCg0KIyBSZW1vdmUgemVybyBlZGdlcyBmcm9tIHlvdXIgbmV0d29yaw0KbmV0d29ya190YWJsZSA8LSBuZXR3b3JrX3RhYmxlWyFDb09jY3VycmVuY2VzPT0wXSANCg0KIyBDcmVhdGUgYSBncmFwaCBvYmplY3Qgd2l0aCB5b3VyIGRhdGENCmdfdmFsdWVkIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkID0gbmV0d29ya190YWJsZVssMTozLHdpdGg9RkFMU0VdLGRpcmVjdGVkID0gRkFMU0UsdmVydGljZXMgPSB0b3RhbF90YWJsZSkNCg0KIyBJZiB5b3UncmUgaGF2aW5nIHRyb3VibGUgd2l0aCBkYXRhIGNvbGxlY3Rpb24sIHlvdSBjYW4gbG9hZCB0aGUgJ05GTCBMYWIgUmVzdWx0cy5SRGF0YScgZmlsZSBub3cgYnkgY2xpY2tpbmcgdGhlIG9wZW4gZm9sZGVyIGljb24gb24gdGhlICJFbnZpcm9ubWVudCIiIHRhYiBhbmQgY29udGludWUgdGhlIGxhYiBmcm9tIGhlcmUuIFlvdSdsbCBuZWVkIHRvIGZpZ3VyZSBvdXQgd2hhdCB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSB0ZXJtcyBhcmUgeW91cnNlbGYsIGhvd2V2ZXIuDQojIFlvdSBzaG91bGQgc2F2ZSB5b3VyIGRhdGEgYXQgdGhpcyBwb2ludCBieSBjbGlja2luZyB0aGUgZmxvcHB5IGRpc2sgaWNvbiB1bmRlciB0aGUgIkVudmlyb25tZW50IiB0YWIuDQpgYGANCg0KIyMgQW5hbHlzaXMNCg0KKipJcyB0aGUgZ3JhcGggZGlyZWN0ZWQgb3IgdW5kaXJlY3RlZD8qKiANCg0KKipIb3cgbWFueSBub2RlcyBhbmQgbGlua3MgZG9lcyB5b3VyIG5ldHdvcmsgaGF2ZT8gKioNCmBgYHtyfQ0KbnVtVmVydGljZXMgPC0gdmNvdW50KGdfdmFsdWVkKQ0KbnVtVmVydGljZXMNCm51bUVkZ2VzIDwtIGVjb3VudChnX3ZhbHVlZCkNCm51bUVkZ2VzDQpgYGANCg0KKipXaGF0IGlzIHRoZSBudW1iZXIgb2YgcG9zc2libGUgbGlua3MgaW4geW91ciBuZXR3b3JrPyAqKg0KYGBge3J9DQptYXhFZGdlcyA8LSBudW1WZXJ0aWNlcyoobnVtVmVydGljZXMtMSkvMg0KbWF4RWRnZXMNCmBgYA0KDQoqKldoYXQgaXMgdGhlIGRlbnNpdHkgb2YgeW91ciBuZXR3b3JrPyoqIA0KYGBge3J9DQpncmFwaERlbnNpdHkgPC0gbnVtRWRnZXMvbWF4RWRnZXMgIyBtYW51YWwgY2FsY3VsYXRpb24NCmdyYXBoRGVuc2l0eQ0KZ3JhcGhEZW5zaXR5MSA8LSBncmFwaC5kZW5zaXR5KGdfdmFsdWVkKSAjIHVzaW5nIHRoZSBncmFwaC5kZW5zaXR5IGZ1bmN0aW9uIGZyb20gaWdyYXBoDQpncmFwaERlbnNpdHkxDQpgYGANCg0KKipCcmllZmx5IGRlc2NyaWJlIGhvdyB5b3VyIGNob2ljZSBvZiBkYXRhc2V0IG1heSBpbmZsdWVuY2UgeW91ciBmaW5kaW5ncy4qKiAgV2hhdCBkaWZmZXJlbmNlcyB3b3VsZCB5b3UgZXhwZWN0IGlmIHlvdSB1c2UgZGlmZmVyZW50IHNlYXJjaCB0ZXJtcz8gQXJlIHRoZSBjdXJyZW50IHNlYXJjaCB0ZXJtcyByZWxhdGVkIHRvIG9uZSBhbm90aGVyPyBJZiBzbywgaG93PyBEbyB5b3UgdGhpbmsgdGhlIGxpbWl0YXRpb24gdG8gb25lIHdvcmQgbWlnaHQgc2tldyB5b3VyIGFuc3dlcnM/IChpLmUuIGlmIHlvdeKAmXJlIGludGVyZXN0ZWQgaW4gSGlsbGFyeSBDbGludG9uLCBidXQgeW91IGluY2x1ZGUg4oCcQ2xpbnRvbuKAnSBhcyBhIHRlcm0sIHlvdSB3aWxsIGdldCBzdG9yaWVzIHRoYXQgbWVudGlvbiBDaGVsc2VhLCBCaWxsLCAmIGV2ZW4gUC1GdW5rIEFsbHN0YXIgR2VvcmdlIENsaW50b24pLg0KDQojIFBhcnQgMjogVmlzdWFsaXplIFlvdXIgTmV0d29yayAoMjAgcG9pbnRzKQ0KDQpMZXQncyBzdGFydCBieSB2aXN1YWxpemluZyB0aGUgbmV0d29yayB0aGF0IHdlJ3ZlIGNvbGxlY3RlZCBmcm9tIHRoZSAqTmV3IFlvcmsgVGltZXMqIEFydGljbGUgU2VhcmNoIEFQSS4gV2UnbGwgbmVlZCB0byBjaG9vc2Ugbm9kZSBjb2xvcnMgYW5kIHNldCBhIGxheW91dC4gWW91IGNhbiBsZWFybiBtb3JlIGFib3V0IEZydWNodGVybWFuIFJlaW5nb2xkIGxheW91dCBhbmQgb3RoZXIgbGF5b3V0cyBbaGVyZV0oaHR0cDovL2lncmFwaC5vcmcvci9kb2MvbGF5b3V0X3dpdGhfZnIuaHRtbCkuDQoNCmBgYHtyfQ0KIyMgTGVhcm4gbW9yZSBhYm91dCBwbG90dGluZyB3aXRoIGlncmFwaA0KPz8gaWdyYXBoLnBsb3R0aW5nDQpjb2xiYXIgPSByYWluYm93KGxlbmd0aCh3b3JkX2xpc3QpKSAjIyB3ZSBhcmUgc2VsZWN0aW5nIGRpZmZlcmVudCBjb2xvcnMgdG8gY29ycmVzcG9uZCB0byBlYWNoIHdvcmQNClYoZ192YWx1ZWQpJGNvbG9yID0gY29sYmFyDQojIFNldCBsYXlvdXQgaGVyZSANCkwgPSBsYXlvdXRfd2l0aF9mcihnX3ZhbHVlZCkgICMgRnJ1Y2h0ZXJtYW4gUmVpbmdvbGQNCnBsb3QoZ192YWx1ZWQsdmVydGV4LmNvbG9yPVYoZ192YWx1ZWQpJGNvbG9yLCBsYXlvdXQgPSBMLCB2ZXJ0ZXguc2l6ZT02KSANCg0KYGBgDQojIyBBbmFseXNpcw0KKipJbiBhIHBhcmFncmFwaCwgZGVzY3JpYmUgdGhlIG1hY3JvLWxldmVsIHN0cnVjdHVyZSBvZiB5b3VyIGdyYXBocyBiYXNlZCBvbiB0aGUgRnJ1Y2h0ZXJtYW4gUmVpbmdvbGQgdmlzdWFsaXphdGlvbi4qKg0KSXMgaXQgYSBnaWFudCwgY29ubmVjdGVkIGNvbXBvbmVudCwgYXJlIHRoZXJlIGRpc3RpbmN0IHN1Yi1jb21wb25lbnRzLCBvciBhcmUgdGhlcmUgaXNvbGF0ZWQgY29tcG9uZW50cz8gIENhbiB5b3UgcmVjb2duaXplIGNvbW1vbiBmZWF0dXJlcyBvZiB0aGUgc3ViY29tcG9uZW50cz8gIERvZXMgdGhpcyB2aXN1YWxpemF0aW9uIGdpdmUgeW91IGFueSBpbnNpZ2h0IGludG8gdGhlIGNvLW9jY3VycmVuY2UgcGF0dGVybnMgb2YgdGhlIHNlYXJjaC10ZXJtcz8gIElmIHllcywgd2hhdD8gSWYgbm90LCB3aHk/DQoNCk5vdyB3ZSdsbCBjcmVhdGUgYSBzZWNvbmQgdmlzdWFsaXphdGlvbiB1c2luZyBhIGRpZmZlcmVudCBsYXlvdXQuDQpgYGB7cn0NCiMjIFlvdSBjYW4gY2hhbmdlIHRoZSBsYXlvdXQgYnkgcGlja2luZyBvbmUgb2YgdGhlIG90aGVyIG9wdGlvbnMuIFVuY29tbWVudCBvbmUgb2YgdGhlIGxpbmVzIGJlbG93IGJ5IGVyYXNpbmcgdGhlICMgYW5kIHJ1bm5pbmcgdGhlIGxpbmUuIFRyeSB0byBmaW5kIGEgbGF5b3V0IHRoYXQgZ2l2ZXMgeW91IGRpZmZlcmVudCBpbmZvcm1hdGlvbiB0aGF0IEZydWNodGVybWFuIFJlaW5nb2xkLg0KDQogTCA9IGxheW91dF93aXRoX2RoKGdfdmFsdWVkKSAjIyBEYXZpZHNvbiBhbmQgSGFyZWwNCg0KIyBMID0gbGF5b3V0X3dpdGhfZHJsKGdfdmFsdWVkKSAjIyBGb3JjZS1kaXJlY3RlZA0KDQojIEwgPSBsYXlvdXRfd2l0aF9rayhnX3ZhbHVlZCkgIyMgU3ByaW5nDQpwbG90KGdfdmFsdWVkLHZlcnRleC5jb2xvcj1WKGdfdmFsdWVkKSRjb2xvciwgbGF5b3V0ID0gTCwgdmVydGV4LnNpemU9NikgDQpgYGANCiMjIEFuYWx5c2lzDQoNCioqSW4gYSBwYXJhZ3JhcGgsIGNvbXBhcmUgYW5kIGNvbnRyYXN0IHRoZSBpbmZvcm1hdGlvbiBnaXZlbiB0byB5b3UgYnkgdGhlIHR3byBkaWZmZXJlbnQgbGF5b3V0cy4qKg0KDQojIFBhcnQgMzogQ29tbXVuaXR5IERldGVjdGlvbiBBbmFseXNpcyB3aXRoIFIgKDIwIFBvaW50cykNCg0KSWRlbnRpZnlpbmcgc3ViZ3JvdXBzIHdpdGhpbiBhIG5ldHdvcmsgaXMgb2YgZ3JlYXQgaW50ZXJlc3QgdG8gc29jaWFsIG5ldHdvcmsgcmVzZWFyY2hlcnMsIHNvIGEgdmFyaWV0eSBvZiBhbGdvcml0aG1zIGhhdmUgYmVlbiBkZXZlbG9wZWQgdG8gaWRlbnRpZnkgYW5kIG1lYXN1cmUgc3ViZ3JvdXBzLiAgV2Ugd2lsbCB1c2Ugc29tZSBvZiBS4oCZcyBidWlsdC1pbiB0b29scyB0byBpZGVudGlmeSBzdWJncm91cHMgYW5kIGNlbnRyYWwgbm9kZXMgZm9yIHZpc3VhbCBpbnNwZWN0aW9uLg0KDQpGb3IgdGhlIHJlbWFpbmRlciBvZiB0aGUgdmlzdWFsaXphdGlvbnMgd2Ugd2lsbCB1c2UgdGhlIEZydWNodGVybWFuIFJlaW5nb2xkIGxheW91dC4NCmBgYHtyfQ0KTCA9IGxheW91dF93aXRoX2ZyKGdfdmFsdWVkKSANCmBgYA0KDQpDbHVzdGVyIHRoZSBub2RlcyBpbiB5b3VyIG5ldHdvcmsuDQpgYGB7cn0NCiMgTGVhcm4gbW9yZSBhYm91dCB0aGUgY2x1c3RlcmluZyBhbGdvcml0aG0uDQo/PyBjbHVzdGVyX3dhbGt0cmFwDQpjbHVzdGVyIDwtIGNsdXN0ZXJfd2Fsa3RyYXAoZ192YWx1ZWQpDQojIEZpbmQgdGhlIG51bWJlciBvZiBjbHVzdGVycw0KbWVtYmVyc2hpcChjbHVzdGVyKSAgICMgYWZmaWxpYXRpb24gbGlzdA0KbGVuZ3RoKHNpemVzKGNsdXN0ZXIpKSAjIG51bWJlciBvZiBjbHVzdGVycw0KIyBGaW5kIHRoZSBzaXplIHRoZSBlYWNoIGNsdXN0ZXIgDQojIE5vdGUgdGhhdCBjb21tdW5pdGllcyB3aXRoIG9uZSBub2RlIGFyZSBpc29sYXRlcywgb3IgaGF2ZSBvbmx5IGEgc2luZ2xlIHRpZQ0Kc2l6ZXMoY2x1c3RlcikgDQpgYGANCg0KKipIb3cgbWFueSBjb21tdW5pdGllcyBoYXZlIGJlZW4gY3JlYXRlZD8qKg0KDQoqKkhvdyBtYW55IG5vZGVzIGFyZSBpbiBlYWNoIGNvbW11bml0eT8qKg0KSW4gbmV0d29ya3MgY29udGFpbmluZyBub2RlIGF0dHJpYnV0ZSBpbmZvcm1hdGlvbiwgd2UgY2FuIG9mdGVuIGdhaW4gaW5zaWdodCBpbnRvIGEgbmV0d29yayBieSBsb29raW5nIGF0IHRoZSBub2RlcyB0aGF0IGdldCBwbGFjZWQgaW4gdGhlIHNhbWUgcGFydGl0aW9uLiANCioqRm9yIHlvdXIgbmV0d29yaywgd2hhdCBtaWdodCBlYWNoIGNsdXN0ZXIgb2Ygbm9kZXMgcG90ZW50aWFsbHkgaGF2ZSBpbiBjb21tb24/IERlc2NyaWJlIGVhY2ggY2x1c3RlciwgaXRzIG1lbWJlcnNoaXAsIGFuZCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gbm9kZXMgaW4gdGhlIGNsdXN0ZXIuKioNCk5leHQgd2UgdmlzdWFsaXplIHRoZSBjbHVzdGVycyBieSBjb2xvcmluZyBub2RlcyBhY2NvcmRpbmcgdG8gdGhlaXIgbW9kdWxhcml0eSBjbGFzcy4gDQpgYGB7cn0NCnBsb3QoY2x1c3RlciwgZ192YWx1ZWQsIGNvbCA9IFYoZ192YWx1ZWQpJGNvbG9yLCBsYXlvdXQgPSBMLCB2ZXJ0ZXguc2l6ZT02KQ0KYGBgDQoNCioqV2hhdCBpbmZvcm1hdGlvbiBkb2VzIHRoaXMgbGF5b3V0IGNvbnZleT8gIEFyZSB0aGUgY2x1c3RlcnMgd2VsbC1zZXBhcmF0ZWQsIG9yIGlzIHRoZXJlIGEgZ3JlYXQgZGVhbCBvZiBvdmVybGFwPyBJcyBpdCBlYXNpZXIgdG8gaWRlbnRpZnkgdGhlIGNvbW1vbiB0aGVtZXMgYW1vbmcgY2x1c3RlcnMgaW4gdGhpcyBsYXlvdXQgcmF0aGVyIHRoYW4gbG9va2luZyBvbmx5IGF0IHRoZSBncmFwaHM/KioNCg0KKipXaGF0IGRpZmZlcmVuY2VzIGFyZSB0aGVyZSBiZXR3ZWVuIG5vZGVzIGluIHRoZSBzYW1lIGNsdXN0ZXIgYW5kIGFjcm9zcyBjbHVzdGVycz8qKg0KDQoqKkRlc2NyaWJlIHRoZSBicm9rZXJzIGJldHdlZW4gYW55IGNvbXBvbmVudHMgYW5kIGNsaXF1ZXMuICBXaGF0IGFyZSBjb21tb24gZmVhdHVyZXMgb2YgdGhlc2UgYnJva2Vycz8gIEFib3V0IGhvdyBtYW55IGJyb2tlcnMgd291bGQgeW91IGhhdmUgdG8gcmVtb3ZlIGZyb20geW91ciBuZXR3b3JrIHRvICJzaGF0dGVyIiBpdCBpbnRvIHR3byBvciBtb3JlIGRpc2Nvbm5lY3RlZCBjb21wb25lbnRzPyoqDQoNCiMgUGFydCA0OiBDZW50cmFsaXR5IFZpc3VhbGl6YXRpb24gJiBXZWlnaHRlZCBWYWx1ZXMgKDIwIFBvaW50cykNCg0KRm9yIGVhY2ggbmV0d29yaywgeW91IHdpbGwgdXNlIGNlbnRyYWxpdHkgbWV0cmljcyB0byBpbXByb3ZlIHlvdXIgdmlzdWFsaXphdGlvbi4gWW91IG1heSBuZWVkIHRvIGFkanVzdCB0aGUgc2l6ZSBwYXJhbWV0ZXIgdG8gbWFrZSB5b3VyIG5ldHdvcmsgbW9yZSBlYXNpbHkgdmlzaWJsZS4NCg0KIyMgRGVncmVlIENlbnRyYWxpdHkNCmBgYHtyfQ0KdG90YWxEZWdyZWUgPC0gZGVncmVlKGdfdmFsdWVkLG1vZGU9ImFsbCIpDQpzb3J0KHRvdGFsRGVncmVlLGRlY3JlYXNpbmc9VFJVRSlbMTo1XQ0KZzIgPC0gZ192YWx1ZWQNClYoZzIpJHNpemUgPC0gdG90YWxEZWdyZWUqMiAjY2FuIGFkanVzdCB0aGUgbnVtYmVyIGlmIG5vZGVzIGFyZSB0b28gYmlnDQpwbG90KGcyLCBsYXlvdXQgPSBMLCB2ZXJ0ZXgubGFiZWw9TkEpDQpgYGANCioqQnJpZWZseSBleHBsYWluIGRlZ3JlZSBjZW50cmFsaXR5IGFuZCB3aHkgbm9kZXMgYXJlIG1vcmUgb3IgbGVzcyBjZW50cmFsIGluIHRoZSBuZXR3b3JrLioqDQoNCiMjIFdlaWdodGVkIERlZ3JlZSBDZW50cmFsaXR5DQpgYGB7cn0NCndkIDwtIGdyYXBoLnN0cmVuZ3RoKGdfdmFsdWVkLHdlaWdodHMgPSBFKGdfdmFsdWVkKSRDb09jY3VycmVuY2VzKQ0Kc29ydCh3ZCxkZWNyZWFzaW5nPVRSVUUpWzE6NV0NCndnMiA8LSBnX3ZhbHVlZA0KVih3ZzIpJHNpemUgPC0gd2QqLjEgIyBhZGp1c3QgdGhlIG51bWJlciBpZiBub2RlcyBhcmUgdG9vIGJpZw0KcGxvdCh3ZzIsIGxheW91dCA9IEwsIHZlcnRleC5sYWJlbD1OQSwgZWRnZS53aWR0aD1zcXJ0KEUoZ192YWx1ZWQpJENvT2NjdXJyZW5jZXMpKSAjdGFraW5nIHRoZSBzcXVhcmUgcm9vdCBpcyBhIGdvb2Qgd2F5IHRvIG1ha2UgYSBsYXJnZSByYW5nZSBvZiBudW1iZXJzIHZpc2libGUgaW4gYW4gZWRnZS4gT3RoZXJ3aXNlIGVkZ2VzIHRlbmQgdG8gY292ZXIgdXAgYWxsIHRoZSBvdGhlciBlZGdlcyBhbmQgb2JzY3VyZSB0aGUgcmVsYXRpb25zaGlwcy4NCmBgYA0KKipXaGF0IGRvZXMgdGhlIGFkZGl0aW9uIG9mIHdlaWdodGVkIGRlZ3JlZSBhbmQgZWRnZSBpbmZvcm1hdGlvbiB0ZWxsIHlvdSBhYm91dCB5b3VyIGdyYXBoPyoqDQoNCiMjIEJldHdlZW5uZXNzIENlbnRyYWxpdHkNCmBgYHtyfQ0KYiA8LSBiZXR3ZWVubmVzcyhnX3ZhbHVlZCxkaXJlY3RlZD1UUlVFKQ0Kc29ydChiLGRlY3JlYXNpbmc9VFJVRSlbMTo1XQ0KZzQgPC0gZ192YWx1ZWQNClYoZzQpJHNpemUgPC0gYioxLjIjY2FuIGFkanVzdCB0aGUgbnVtYmVyDQpwbG90KGc0LCBsYXlvdXQgPSBMLCB2ZXJ0ZXgubGFiZWw9TkEpDQpgYGANCioqQnJpZWZseSBleHBsYWluIGJldHdlZW5uZXNzIGNlbnRyYWxpdHkgYW5kIHdoeSBub2RlcyBhcmUgbW9yZSBvciBsZXNzIGNlbnRyYWwgaW4gdGhlIG5ldHdvcmsuKioNCg0KIyMjIFdlaWdodGVkIEJldHdlZW5uZXNzIENlbnRyYWxpdHkNCmBgYHtyfQ0Kd2J0d24gPC0gYmV0d2Vlbm5lc3MoZ192YWx1ZWQsd2VpZ2h0cyA9IEUoZ192YWx1ZWQpJENvT2NjdXJyZW5jZXMpDQpzb3J0KHdidHduLGRlY3JlYXNpbmc9VFJVRSlbMTo1XQ0Kd0J0d25HIDwtIGdfdmFsdWVkDQpWKHdCdHduRykkc2l6ZSA8LSB3YnR3biouNSAjIGFkanVzdCB0aGUgbnVtYmVyIGlmIG5vZGVzIGFyZSB0b28gYmlnDQpwbG90KHdCdHduRywgbGF5b3V0ID0gTCwgdmVydGV4LmxhYmVsPU5BLCBlZGdlLndpZHRoPXNxcnQoRShnX3ZhbHVlZCkkQ29PY2N1cnJlbmNlcykpICN0YWtpbmcgdGhlIHNxdWFyZSByb290IGlzIGEgZ29vZCB3YXkgdG8gbWFrZSBhIGxhcmdlIHJhbmdlIG9mIG51bWJlcnMgdmlzaWJsZSBpbiBhbiBlZGdlLg0KYGBgDQoqKldoYXQgZG9lcyB0aGUgYWRkaXRpb24gb2Ygd2VpZ2h0ZWQgZGVncmVlIGFuZCBlZGdlIGluZm9ybWF0aW9uIHRlbGwgeW91IGFib3V0IHlvdXIgZ3JhcGg/KioNCg0KIyMgQ2xvc2VuZXNzIENlbnRyYWxpdHkNCmBgYHtyfQ0KYyA8LSBjbG9zZW5lc3MoZ192YWx1ZWQpDQpzb3J0KGMsZGVjcmVhc2luZz1UUlVFKVsxOjVdDQpnNSA8LSBnX3ZhbHVlZA0KVihnNSkkc2l6ZSA8LSBjKjUwMCAgI2NhbiBhZGp1c3QgdGhlIG51bWJlcg0KcGxvdChnNSwgbGF5b3V0ID0gTCwgdmVydGV4LmxhYmVsPU5BKQ0KYGBgDQoqKkJyaWVmbHkgZXhwbGFpbiBjbG9zZW5lc3MgY2VudHJhbGl0eSBhbmQgd2h5IG5vZGVzIGFyZSBtb3JlIG9yIGxlc3MgY2VudHJhbCBpbiB0aGUgbmV0d29yay4qKg0KDQojIyMgV2VpZ2h0ZWQgQ2xvc2VuZXNzIENlbnRyYWxpdHkNCg0KYGBge3J9DQp3Q2xzbnNzIDwtIGNsb3NlbmVzcyhnX3ZhbHVlZCx3ZWlnaHRzID0gRShnX3ZhbHVlZCkkQ29PY2N1cnJlbmNlcykNCnNvcnQod0Nsc25zcyxkZWNyZWFzaW5nPVRSVUUpWzE6NV0NCndDbHNuc3NHIDwtIGdfdmFsdWVkDQpWKHdDbHNuc3NHKSRzaXplIDwtIHdDbHNuc3MqMTAwMCAjIGFkanVzdCB0aGUgbnVtYmVyIGlmIG5vZGVzIGFyZSB0b28gYmlnDQpwbG90KHdDbHNuc3NHLCBsYXlvdXQgPSBMLCB2ZXJ0ZXgubGFiZWw9TkEsIGVkZ2Uud2lkdGg9c3FydChFKGdfdmFsdWVkKSRDb09jY3VycmVuY2VzKSkgI3Rha2luZyB0aGUgc3F1YXJlIHJvb3QgaXMgYSBnb29kIHdheSB0byBtYWtlIGEgbGFyZ2UgcmFuZ2Ugb2YgbnVtYmVycyB2aXNpYmxlIGluIGFuIGVkZ2UuDQpgYGANCioqV2hhdCBkb2VzIHRoZSBhZGRpdGlvbiBvZiB3ZWlnaHRlZCBkZWdyZWUgYW5kIGVkZ2UgaW5mb3JtYXRpb24gdGVsbCB5b3UgYWJvdXQgeW91ciBncmFwaD8qKg0KDQojIyBFaWdlbnZlY3RvciBDZW50cmFsaXR5DQpgYGB7cn0NCmVpZ2MgPC0gZWlnZW5fY2VudHJhbGl0eShnX3ZhbHVlZCxkaXJlY3RlZD1UUlVFKQ0Kc29ydChlaWdjJHZlY3RvcixkZWNyZWFzaW5nPVRSVUUpWzE6NV0NCmc2IDwtIGdfdmFsdWVkDQpWKGc2KSRzaXplIDwtIGVpZ2MkdmVjdG9yKjUwICNjYW4gYWRqdXN0IHRoZSBudW1iZXINCnBsb3QoZzYsIGxheW91dCA9IEwsIHZlcnRleC5sYWJlbD1OQSkNCmBgYA0KDQoqKkJyaWVmbHkgZXhwbGFpbiBlaWdlbnZlY3RvciBjZW50cmFsaXR5IGFuZCB3aHkgbm9kZXMgYXJlIG1vcmUgb3IgbGVzcyBjZW50cmFsIGluIHRoZSBuZXR3b3JrLioqDQoNCiMjIEFuYWx5c2lzDQoqKkNob29zZSB0aGUgdmlzdWFsaXphdGlvbiB0aGF0IHlvdSB0aGluayBpcyBtb3N0IGludGVyZXN0aW5nIGFuZCBicmllZmx5IGV4cGxhaW4gd2hhdCBpdCB0ZWxscyB5b3UgYWJvdXQgYSBjZW50cmFsIG5vZGUgaW4geW91ciBuZXR3b3JrLiBEaXNjdXNzIHRoZSB0eXBlIG9mIGNlbnRyYWxpdHksIGFuZCB3aGF0IHRoYXQgbm9kZeKAmXMgY2VudHJhbGl0eSBzY29yZSB0ZWxscyB5b3UgYWJvdXQgdGhlIHNlYXJjaCBjby1vY2N1cnJlbmNlIG5ldHdvcmsuKioNCg0KKipCcmllZmx5IGRpc2N1c3MgYW4gaW50ZXJlc3RpbmcgZGlmZmVyZW5jZSBiZXR3ZWVuIHR5cGVzIG9mIGNlbnRyYWxpdHkgZm9yIHlvdXIgbmV0d29yay4qKg0KDQojIyBHbG9iYWwgTmV0d29yayBNZXRyaWNzIHdpdGggUg0KDQpDb21wdXRlIHRoZSBuZXR3b3JrIGNlbnRyYWxpemF0aW9uIHNjb3JlcyBmb3IgeW91ciBuZXR3b3JrIGZvciBkZWdyZWUsIGJldHdlZW5uZXNzLCBjbG9zZW5lc3MsIGFuZCBlaWdlbnZlY3RvciBjZW50cmFsaXR5Lg0KDQpgYGB7cn0NCiMgRGVncmVlIGNlbnRyYWxpemF0aW9uDQpjZW50cmFsaXphdGlvbi5kZWdyZWUoZ192YWx1ZWQsbm9ybWFsaXplZCA9IFRSVUUpDQoNCiMgQmV0d2Vlbm5lc3MgY2VudHJhbGl6YXRpb24NCmNlbnRyYWxpemF0aW9uLmJldHdlZW5uZXNzKGdfdmFsdWVkLG5vcm1hbGl6ZWQgPSBUUlVFKQ0KDQojIENsb3NlbmVzcyBjZW50cmFsaXphdGlvbiANCmNlbnRyYWxpemF0aW9uLmNsb3NlbmVzcyhnX3ZhbHVlZCxub3JtYWxpemVkID0gVFJVRSkNCg0KIyBFaWdlbnZlY3RvciBjZW50cmFsaXphdGlvbiANCmNlbnRyYWxpemF0aW9uLmV2Y2VudChnX3ZhbHVlZCxub3JtYWxpemVkID0gVFJVRSkNCg0KYGBgDQoqKlJlY29yZCB0aGUgY2VudHJhbGl6YXRpb24gc2NvcmUgb2YgZWFjaCBjZW50cmFsaXR5IG1lYXN1cmUuKioNCg0KKipCcmllZmx5IGV4cGxhaW4gd2hhdCB0aGUgY2VudHJhbGl6YXRpb24gb2YgYSBuZXR3b3JrIGlzLioqDQoNCioqQ29tcGFyZSB0aGUgY2VudHJhbGl6YXRpb24gc2NvcmVzIGFib3ZlIHdpdGggdGhlIGdyYXBocyB5b3UgY3JlYXRlZCB3aGVyZSB0aGUgbm9kZXMgYXJlIHNjYWxlZCBieSBjZW50cmFsaXR5LiBEZXNjcmliZSB0aGUgYXBwZWFyYW5jZSBvZiBtb3JlIGNlbnRyYWxpemVkIHYuIGxlc3MgY2VudHJhbGl6ZWQgbmV0d29ya3MuKioNCg0KIyMgUGFydCA1LiBQb3dlciBMYXdzICYgU21hbGwgV29ybGRzICgyMCkNCg0KIyMgUG93ZXIgTGF3cw0KTmV0d29ya3Mgb2Z0ZW4gZGVtb25zdHJhdGUgcG93ZXIgbGF3IGRpc3RyaWJ1dGlvbnMuIFBsb3QgdGhlIGRlZ3JlZSBkaXN0cmlidXRpb24gb2YgdGhlIG5vZGVzIGluIHlvdXIgYmFzZSBncmFwaC4gDQpgYGB7cn0NCiMgQ2FsY3VsYXRlIGRlZ3JlZSBkaXN0cmlidXRpb24NCmRlZyA8LSBkZWdyZWUoZ192YWx1ZWQsdj1WKGdfdmFsdWVkKSwgbW9kZT0iYWxsIikNCmRlZw0KDQojIERlZ3JlZSBkaXN0cmlidXRpb24gaXMgdGhlIGN1bXVsYXRpdmUgZnJlcXVlbmN5IG9mIG5vZGVzIHdpdGggYSBnaXZlbiBkZWdyZWUNCmRlZ19kaXN0ciA8LWRlZ3JlZS5kaXN0cmlidXRpb24oZ192YWx1ZWQsIGN1bXVsYXRpdmU9VCwgbW9kZT0iYWxsIikNCmRlZ19kaXN0cg0KcGxvdChkZWdfZGlzdHIsIHlsaW09YyguMDEsMTApLCBiZz0iYmxhY2siLHBjaD0yMSwgeGxhYj0iRGVncmVlIiwgeWxhYj0iQ3VtdWxhdGl2ZSBGcmVxdWVuY3kiKSAjWW91IG1heSBuZWVkIHRvIGFkanVzdCB0aGUgeWxpbSB0byBhIGxhcmdlciBvciBzbWFsbGVyIG51bWJlciB0byBtYWtlIHRoZSBncmFwaCBzaG93IG1vcmUgZGF0YS4NCmBgYA0KDQpUZXN0IHdoZXRoZXIgaXTigJlzIGFwcHJveGltYXRlbHkgYSBwb3dlciBsYXcsIGVzdGltYXRlIGxvZyBmIChrKSA9IGxvZyBhIOKIkiBjIGxvZyBrLiDigJxUaGlzIHNheXMgdGhhdCBpZiB3ZSBoYXZlIGEgcG93ZXItbGF3IHJlbGF0aW9uc2hpcCwgYW5kIHdlIHBsb3QgbG9nIGYgKGspIGFzIGEgZnVuY3Rpb24gb2YgbG9nIGssIHRoZW4gd2Ugc2hvdWxkIHNlZSBhIHN0cmFpZ2h0IGxpbmU6IOKIkmMgd2lsbCBiZSB0aGUgc2xvcGUsIGFuZCBsb2cgYSB3aWxsIGJlIHRoZSB5LWludGVyY2VwdC4gU3VjaCBhIOKAnGxvZy1sb2figJ0gcGxvdCB0aHVzIHByb3ZpZGVzIGEgcXVpY2sgd2F5IHRvIHNlZSBpZiBvbmXigJlzIGRhdGEgZXhoaWJpdHMgYW4gYXBwcm94aW1hdGUgcG93ZXItbGF3OiBpdCBpcyBlYXN5IHRvIHNlZSBpZiBvbmUgaGFzIGFuIGFwcHJveGltYXRlbHkgc3RyYWlnaHQgbGluZSwgYW5kIG9uZSBjYW4gcmVhZCBvZmYgdGhlIGV4cG9uZW50IGZyb20gdGhlIHNsb3BlLuKAnSAoRSZLLCBDaGFwdGVyIDE4LCBwLjU0NikuDQoNCmBgYHtyfQ0KcG93ZXIgPC0gcG93ZXIubGF3LmZpdChkZWdfZGlzdHIpDQpwb3dlcg0KcGxvdChkZWdfZGlzdHIsIGxvZz0ieHkiLCB5bGltPWMoLjAxLDEwKSwgYmc9ImJsYWNrIixwY2g9MjEsIHhsYWI9IkRlZ3JlZSIsIHlsYWI9IkN1bXVsYXRpdmUgRnJlcXVlbmN5IikNCmBgYA0KDQoqKkRvZXMgeW91ciBuZXR3b3JrIGV4aGliaXQgYSBwb3dlciBsYXcgZGlzdHJpYnV0aW9uIG9mIGRlZ3JlZSBjZW50cmFsaXR5PyoqDQoNCiMjIFNtYWxsIFdvcmxkcw0KDQpOZXR3b3JrcyBvZnRlbiBkZW1vbnN0cmF0ZSBzbWFsbCB3b3JsZCBjaGFyYWN0ZXJpc3RpY3MuIENvbXB1dGUgdGhlIGF2ZXJhZ2UgY2x1c3RlcmluZyBjb2VmZmljaWVudCAoQUNDKSBhbmQgdGhlIGNoYXJhY3RlcmlzdGljIHBhdGggbGVuZ3RoIChDUEwpLg0KYGBge3J9DQojIEF2ZXJhZ2UgY2x1c3RlcmluZyBjb2VmZmljaWVudCAoQUNDKQ0KdHJhbnNpdGl2aXR5KGdfdmFsdWVkLCB0eXBlID0gYygiYXZlcmFnZSIpKQ0KDQojIENoYXJhY3RlcmlzdGljIHBhdGggbGVuZ3RoIChDUEwpDQphdmVyYWdlLnBhdGgubGVuZ3RoKGdfdmFsdWVkKQ0KYGBgDQoNCkNvbXB1dGUgdGhlIEFDQyBhbmQgQ1BMIGZvciAxMDAgcmFuZG9tIG5ldHdvcmtzIHdpdGggdGhlIHNhbWUgbnVtYmVyIG9mIG5vZGVzIGFuZCB0aWVzIGFzIHlvdXIgdGVzdCBuZXR3b3JrLiANCg0KYGBge3J9DQphY2NTdW0gPC0gMA0KY3BsU3VtIDwtIDANCmZvciAoaSBpbiAxOjEwMCl7DQogIGdycGggPC0gZXJkb3MucmVueWkuZ2FtZShudW1WZXJ0aWNlcywgbnVtRWRnZXMsIHR5cGUgPSAiZ25tIikNCiAgYWNjU3VtIDwtIGFjY1N1bSArIHRyYW5zaXRpdml0eShncnBoLCB0eXBlID0gYygiYXZlcmFnZSIpKQ0KICBjcGxTdW0gPC0gY3BsU3VtICsgYXZlcmFnZS5wYXRoLmxlbmd0aChncnBoKQ0KfQ0KYWNjU3VtLzEwMA0KY3BsU3VtLzEwMA0KYGBgDQoNCioqQmFzZWQgb24gdGhlc2UgZGF0YSwgd291bGQgeW91IGNvbmNsdWRlIHRoYXQgdGhlIG9ic2VydmVkIG5ldHdvcmsgZGVtb25zdHJhdGVzIHNtYWxsIHdvcmxkIHByb3BlcnRpZXM/IFdoeSBvciB3aHkgbm90PyoqDQoNCiMjIFdyYXBwaW5nIHVwDQpUbyBjb21wbGV0ZSB0aGUgbGFiLCBtYWtlIHN1cmUgb3V0cHV0L3ByZXZpZXdzIGhhdmUgYmVlbiBnZW5lcmF0ZWQgZm9yIGVhY2ggYmxvY2sgb2YgY29kZS4gVGhlbiBjbGljayB0aGUgIlB1Ymxpc2giIGJ1dHRvbiBvbiB0aGUgdXBwZXIgcmlnaHQgaGFuZCBjb3JuZXIgb2YgdGhpcyBzY3JlZW4gYW5kIHNpZ24gdXAgZm9yIGFuIFJQdWJzIGFjY291bnQuIFN1Ym1pdCB0aGUgVVJMIG9mIHRoZSBwdWJsaXNoZWQsIGNvbXBsZXRlZCBsYWIgb24gQ2FudmFzLg==