Table of contents

  1. Download data
  2. Data Preparation
  3. Data analysis 3.1. Out-degree distribution 3.2. In-degree distribution 3.3. In-degree distribution - log scale 3.4. Average number of inbound co-purchase links, the standard deviation, and the maximum 3.5. 10 products with the most inbound co-purchase links

The purpose is the report is to provide the answer and solutions to the Amazon pre-class assignment.

1. Download data

Initially, the required packages are downloaded including the igraph package.


packages <- c("dplyr", "igraph", "readr", "ggplot2", 'readxl', 'readr', 'tidyverse', 'ggthemes', 'knitr', 'extrafont', 'scales', 'lubridate') 
# Checking for package installations on the system and installing if not found.
if (length(setdiff(packages, rownames(installed.packages()))) > 0) {
  install.packages(setdiff(packages, rownames(installed.packages())))  
}
# Packages to use
for(package in packages){
  library(package, character.only = TRUE)
}

Secondly, the required data is downloaded using read.table and read_csv functions.


data1 <- as.data.frame(read.table(file = "graph_subset_rank1000.txt"))
data <- as.data.frame(read.table(file = "graph_subset_rank1000_cc.txt"))
data2 <- as.data.frame(read.table(file = "graph_complete.txt"))
all_books <- read_csv("id_to_titles.csv")

2. Network structure visualization

The graph_subset_rank1000.txt is downloaded in order to generate a visualization of the network structure.


data1 <- as.data.frame(read.table(file = "graph_subset_rank1000.txt"))
routes_igraph <- graph_from_data_frame(data1, directed = FALSE)
plot(routes_igraph, 
     edge.arrow.size = 10, 
     layout= layout_in_circle(routes_igraph),  
     edge.width = 1, 
     vertex.label=NA, 
     vertex.size=3.5,
     edge.color="#808080",
     vertex.color = "#F0E130",
     main = "Network  Salesrank under 1,000 - layout_in_circle"
     )

The initial plot is generated using the layout “layout_in_circle”, however, it is very hard to gather some insights using this layout. For example, it is not possible to see if all the graph is or not connected.

Using other types of layouts such as layout.kamada.kawai provides better visualization, and it is possible to see a disconnected graph, which means it breaks apart naturally into a set of connected groups of nodes named components.


data1 <- as.data.frame(read.table(file = "graph_subset_rank1000.txt"))
routes_igraph <- graph_from_data_frame(data1, directed = FALSE)
plot(routes_igraph, 
     edge.arrow.size = 10, 
     layout= layout.kamada.kawai,  
     edge.width = 1, 
     vertex.label=NA, 
     vertex.size=3.5,
     edge.color="black",
     vertex.color = "#F0E130",
     main = "Network  Salesrank under 1,000 - layout.kamada.kawai"
     )

The graph below uses the layout named “layout_nicely” which allows us to better visualize the components within the network. There is a larger connected component in the center of the circle, this component has a higher number of nodes than the rest of the components in the graph, which could indicate a cluster of amazon products.


data1 <- as.data.frame(read.table(file = "graph_subset_rank1000.txt"))
routes_igraph <- graph_from_data_frame(data1, directed = FALSE)
plot(routes_igraph, 
     edge.arrow.size = 10, 
     layout= layout_nicely(routes_igraph ),  
     edge.width = 1, 
     vertex.label=NA, 
     vertex.size=3.5,
     edge.color="black",
     vertex.color = "#F0E130",
     main = "Network  Salesrank under 1,000 - layout_nicely "
     )

Dividing a graph into its components is of course only a first, global way of describing its structure. Within a given component, there may be a richer internal structure that is important to one’s interpretation of the network. As a result, the data from the largest component in the network of products with salesrank under 1,000 is extracted through the “graph_subset_rank1000_cc.txt” dataset in order to generate a visual to have a better understanding of the network.

The graph below displays the largest component in the network. In this graph every node can reach every other node by a path, which indicates that the graph is connected.


data <- as.data.frame(read.table(file = "graph_subset_rank1000_cc.txt"))
routes_igraph1 <- graph_from_data_frame(data, directed = FALSE)
plot(routes_igraph1, 
     edge.arrow.size = 20, 
     layout= layout.kamada.kawai,
     vertex.label=NA, 
     edge.color="black",
     vertex.color = "#F0E130",
     vertex.size=4,
     main = "The largest component in the network of products with salesrank under 1,000")

Additionally, using the degree function in the graph below allow us to observe the nodes in the component with a larger count of outgoing links to another product page represented by a bigger node size.


data <- as.data.frame(read.table(file = "graph_subset_rank1000_cc.txt"))
routes_igraph1 <- graph_from_data_frame(data, directed = FALSE)
plot.igraph(routes_igraph1, 
            edge.arrow.size = 20, 
            layout= layout.kamada.kawai, 
            edge.color="black",
            vertex.color = "#F0E130",
            vertex.size=degree(routes_igraph, mode="out"),
            main = "The largest component in the network of products with salesrank under 1,000",
            vertex.label=NA, vertex.color = "yellow")

NA
NA

3. Data analysis

3.1 Out-degree distribution

In order to count the number of outgoing links to another product page, the table function was used to summarise the number of outbound links for each product a -> b . However, this summarization does not contain the products with zero outgoing links, so a unique list of products is generated to be merged with the summarization table in order to account for the zero values. The code below describes the process.


colnames(data2) <- c("a", "b")
data3 <- data.frame(table(data2$a))
colnames(data3) <- c("a", "Freq")

a <- data2%>%select(a)
a<- unique(a)
b <- data2%>%select(b)
colnames(b) <- c("a")
b <- unique(b)
c <- rbind(a,b)
c <- unique(c)


merge <- merge(x= c, y= data3, by=c("a"), all.x = T)
merge <- merge%>%mutate(Freq = ifelse(is.na(Freq), 0, Freq))
in_data <- data.frame(table(merge$Freq))
in_data
NA

The graph below displays the out-degree distribution, in which the maximum number of outbound links is 5, and the highest frequency is 4.


ggplot(data = merge, aes(Freq)) +
  geom_histogram(bins = 6, fill="#00BED8", color = "#00BED8") + theme_classic() +
  theme(
        text=element_text(size=10,  family="Comic Sans MS"),
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor =  element_blank(),
        axis.line = element_line(color = "gray"),
        axis.ticks.x = element_blank(),
        axis.ticks.y = element_blank()) + labs(title = "Out-degree distribution Plot", x = "Out-Degree", y = "Frequency",
                                               subtitle = "The maximum number of outbound links is 5 and the highest frequency is 4.", caption = "Jose Vilardy(2019) | Network Analytics" )  + scale_y_continuous(labels = comma) + scale_x_continuous(labels = comma)

3.2 In-degree distribution

The graph below displays the in-degree distribution. In this case, the distribution is different as some few products have more than 200 incoming links with a maximum of 549, and also there is a larger number of products with 0 and 1 incoming links, which means that there are only few popular books that are driving co-purchases of several other books.


ggplot(data = merge2, aes(x=Freq)) +
  geom_histogram(bins = 100, fill="#00BED8", color = "#00BED8") +  theme_bw() + 
  theme(
        text=element_text(size=10,  family="Comic Sans MS"),
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor =  element_blank(),
        axis.line = element_line(color = "gray"),
        axis.ticks.x = element_blank(),
        axis.ticks.y = element_blank()) + labs(title = "In-degree distribution Plot", x = "In-Degree", y = "Frequency",
                                               subtitle = "There is a larger number of products with 0 and 1 incoming links.The maximum frequency is 549 in-links", caption = "Jose Vilardy(2019)| Network Analytics" )  + scale_y_continuous(labels = comma) + scale_x_continuous(labels = comma)

The in-degree distribution graph showcases the long tail distribution or power law where the distribution of the event is skewed so that a small number of outcomes have dramatically higher values than the remaining population, drawing as a result, a curve with a long tail lowering as the value increases.


merge.l <- merge2%>%mutate(Freq = ifelse(Freq == 0, log(0.1), log(Freq)))
out_data2 <- data.frame(table(merge.l$Freq))
out_data2
NA

3.3 In-degree distribution - log scale

Initially, the 0 inbound links were replaced with 0.1 to be able to apply the log function.


ggplot(data = merge.l, aes(x=Freq)) +
  geom_histogram(bins = 10, fill="#00BED8", color = "#00BED8") +  theme_bw() + 
  theme(
        text=element_text(size=10,  family="Comic Sans MS"),
        panel.border = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor =  element_blank(),
        axis.line = element_line(color = "gray"),
        axis.ticks.x = element_blank(),
        axis.ticks.y = element_blank()) + labs(title = "In-degree distribution Plot - log scale", x = "In-Degree", y = "Frequency",
                                               subtitle = "The plot indicates that the most frequent number of incoming links is 0 (log (0.1) = -2.3)", caption = "Jose Vilardy(2019)| Network Analytics")  + scale_y_continuous(labels = comma) + scale_x_continuous(labels = comma)

After applying the log function to the in-degree axis, the distribution is still skewed. The plot indicates that the most frequent number of incoming links is 0 (log (0.1) = -2.3). Also, it is possible to observe a high number of books that have between 1 and 7 in-links.

LS0tDQp0aXRsZTogIlByZS1Nb2R1bGUgTmV0d29yayBEYXRhIEFuYWx5c2lzIEluZGl2aWR1YWwgQXNzaWdubWVudCINCmF1dGhvcjogIkpvc2UgQmVsdHJhbiBWaWxhcmR5Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojIyBUYWJsZSBvZiBjb250ZW50cw0KDQoxLiBbRG93bmxvYWQgZGF0YV0oIzEuIERvd25sb2FkIGRhdGEpDQoyLiBbRGF0YSBQcmVwYXJhdGlvbl0oIzIuIERhdGEgUHJlcGFyYXRpb24pDQozLiBbRGF0YSBhbmFseXNpc10oIzMuIERhdGEgYW5hbHlzaXMpDQozLjEuIFtPdXQtZGVncmVlIGRpc3RyaWJ1dGlvbl0oIzMuMSBPdXQtZGVncmVlIGRpc3RyaWJ1dGlvbikNCjMuMi4gW0luLWRlZ3JlZSBkaXN0cmlidXRpb25dKCMzLjIgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbikNCjMuMy4gW0luLWRlZ3JlZSBkaXN0cmlidXRpb24gLSBsb2cgc2NhbGVdKCMzLjMgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiAtIGxvZyBzY2FsZSkNCjMuNC4gW0F2ZXJhZ2UgbnVtYmVyIG9mIGluYm91bmQgY28tcHVyY2hhc2UgbGlua3MsIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCB0aGUgbWF4aW11bV0oIzMuNCBBdmVyYWdlIG51bWJlciBvZiBpbmJvdW5kIGNvLXB1cmNoYXNlIGxpbmtzLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgdGhlIG1heGltdW0pDQozLjUuIFsxMCBwcm9kdWN0cyB3aXRoIHRoZSBtb3N0IGluYm91bmQgY28tcHVyY2hhc2UgbGlua3NdKCMxMCBwcm9kdWN0cyB3aXRoIHRoZSBtb3N0IGluYm91bmQgY28tcHVyY2hhc2UgbGlua3MpDQoNCg0KDQoNClRoZSBwdXJwb3NlIGlzIHRoZSByZXBvcnQgaXMgdG8gcHJvdmlkZSB0aGUgYW5zd2VyIGFuZCBzb2x1dGlvbnMgdG8gdGhlIEFtYXpvbiBwcmUtY2xhc3MgYXNzaWdubWVudC4NCg0KDQojIDEuIERvd25sb2FkIGRhdGEgPGEgbmFtZT0iMS4gRG93bmxvYWQgZGF0YSI+PC9hPg0KDQoNCkluaXRpYWxseSwgdGhlIHJlcXVpcmVkIHBhY2thZ2VzIGFyZSBkb3dubG9hZGVkIGluY2x1ZGluZyB0aGUgaWdyYXBoIHBhY2thZ2UuDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCg0KcGFja2FnZXMgPC0gYygiZHBseXIiLCAiaWdyYXBoIiwgInJlYWRyIiwgImdncGxvdDIiLCAncmVhZHhsJywgJ3JlYWRyJywgJ3RpZHl2ZXJzZScsICdnZ3RoZW1lcycsICdrbml0cicsICdleHRyYWZvbnQnLCAnc2NhbGVzJywgJ2x1YnJpZGF0ZScpIA0KIyBDaGVja2luZyBmb3IgcGFja2FnZSBpbnN0YWxsYXRpb25zIG9uIHRoZSBzeXN0ZW0gYW5kIGluc3RhbGxpbmcgaWYgbm90IGZvdW5kLg0KaWYgKGxlbmd0aChzZXRkaWZmKHBhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKSA+IDApIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhzZXRkaWZmKHBhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKSAgDQp9DQojIFBhY2thZ2VzIHRvIHVzZQ0KZm9yKHBhY2thZ2UgaW4gcGFja2FnZXMpew0KICBsaWJyYXJ5KHBhY2thZ2UsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KDQpgYGANCg0KDQpTZWNvbmRseSwgdGhlIHJlcXVpcmVkIGRhdGEgaXMgZG93bmxvYWRlZCB1c2luZyByZWFkLnRhYmxlIGFuZCByZWFkX2NzdiBmdW5jdGlvbnMuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCmRhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQiKSkNCmRhdGEgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKGZpbGUgPSAiZ3JhcGhfc3Vic2V0X3JhbmsxMDAwX2NjLnR4dCIpKQ0KZGF0YTIgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKGZpbGUgPSAiZ3JhcGhfY29tcGxldGUudHh0IikpDQphbGxfYm9va3MgPC0gcmVhZF9jc3YoImlkX3RvX3RpdGxlcy5jc3YiKQ0KDQoNCmBgYA0KDQoNCg0KIyAyLiBOZXR3b3JrIHN0cnVjdHVyZSB2aXN1YWxpemF0aW9uIDxhIG5hbWU9IjIuIE5ldHdvcmsgc3RydWN0dXJlIHZpc3VhbGl6YXRpb24iPjwvYT4NCg0KVGhlIGdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQgaXMgZG93bmxvYWRlZCBpbiBvcmRlciB0byBnZW5lcmF0ZSBhIHZpc3VhbGl6YXRpb24gb2YgdGhlIG5ldHdvcmsgc3RydWN0dXJlLiAgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCmRhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQiKSkNCnJvdXRlc19pZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGRhdGExLCBkaXJlY3RlZCA9IEZBTFNFKQ0KcGxvdChyb3V0ZXNfaWdyYXBoLCANCiAgICAgZWRnZS5hcnJvdy5zaXplID0gMTAsIA0KICAgICBsYXlvdXQ9IGxheW91dF9pbl9jaXJjbGUocm91dGVzX2lncmFwaCksICANCiAgICAgZWRnZS53aWR0aCA9IDEsIA0KICAgICB2ZXJ0ZXgubGFiZWw9TkEsIA0KICAgICB2ZXJ0ZXguc2l6ZT0zLjUsDQogICAgIGVkZ2UuY29sb3I9IiM4MDgwODAiLA0KICAgICB2ZXJ0ZXguY29sb3IgPSAiI0YwRTEzMCIsDQogICAgIG1haW4gPSAiTmV0d29yayAgU2FsZXNyYW5rIHVuZGVyIDEsMDAwIC0gbGF5b3V0X2luX2NpcmNsZSINCiAgICAgKQ0KDQpgYGANCg0KVGhlIGluaXRpYWwgcGxvdCBpcyBnZW5lcmF0ZWQgdXNpbmcgdGhlIGxheW91dCDigJxsYXlvdXRfaW5fY2lyY2xl4oCdLCBob3dldmVyLCBpdCBpcyB2ZXJ5IGhhcmQgdG8gZ2F0aGVyIHNvbWUgaW5zaWdodHMgdXNpbmcgdGhpcyBsYXlvdXQuIEZvciBleGFtcGxlLCBpdCBpcyBub3QgcG9zc2libGUgdG8gc2VlIGlmIGFsbCB0aGUgZ3JhcGggaXMgb3Igbm90IGNvbm5lY3RlZC4gDQoNClVzaW5nIG90aGVyIHR5cGVzIG9mIGxheW91dHMgc3VjaCBhcyBsYXlvdXQua2FtYWRhLmthd2FpIHByb3ZpZGVzIGJldHRlciB2aXN1YWxpemF0aW9uLCBhbmQgaXQgaXMgcG9zc2libGUgdG8gc2VlIGEgZGlzY29ubmVjdGVkIGdyYXBoLCB3aGljaCBtZWFucyBpdCBicmVha3MgYXBhcnQgbmF0dXJhbGx5IGludG8gYSBzZXQgb2YgY29ubmVjdGVkIGdyb3VwcyBvZiBub2RlcyBuYW1lZCBjb21wb25lbnRzLiANCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQ0KDQpkYXRhMSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoZmlsZSA9ICJncmFwaF9zdWJzZXRfcmFuazEwMDAudHh0IikpDQpyb3V0ZXNfaWdyYXBoIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkYXRhMSwgZGlyZWN0ZWQgPSBGQUxTRSkNCnBsb3Qocm91dGVzX2lncmFwaCwgDQogICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDEwLCANCiAgICAgbGF5b3V0PSBsYXlvdXQua2FtYWRhLmthd2FpLCAgDQogICAgIGVkZ2Uud2lkdGggPSAxLCANCiAgICAgdmVydGV4LmxhYmVsPU5BLCANCiAgICAgdmVydGV4LnNpemU9My41LA0KICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgIHZlcnRleC5jb2xvciA9ICIjRjBFMTMwIiwNCiAgICAgbWFpbiA9ICJOZXR3b3JrICBTYWxlc3JhbmsgdW5kZXIgMSwwMDAgLSBsYXlvdXQua2FtYWRhLmthd2FpIg0KICAgICApDQoNCmBgYA0KDQpUaGUgZ3JhcGggYmVsb3cgdXNlcyB0aGUgbGF5b3V0IG5hbWVkIOKAnGxheW91dF9uaWNlbHnigJ0gd2hpY2ggYWxsb3dzIHVzIHRvIGJldHRlciB2aXN1YWxpemUgdGhlIGNvbXBvbmVudHMgd2l0aGluIHRoZSBuZXR3b3JrLiBUaGVyZSBpcyBhIGxhcmdlciBjb25uZWN0ZWQgY29tcG9uZW50IGluIHRoZSBjZW50ZXIgb2YgdGhlIGNpcmNsZSwgdGhpcyBjb21wb25lbnQgaGFzIGEgaGlnaGVyIG51bWJlciBvZiBub2RlcyB0aGFuIHRoZSByZXN0IG9mIHRoZSBjb21wb25lbnRzIGluIHRoZSBncmFwaCwgd2hpY2ggY291bGQgaW5kaWNhdGUgYSBjbHVzdGVyIG9mIGFtYXpvbiBwcm9kdWN0cy4gDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCmRhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQiKSkNCnJvdXRlc19pZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGRhdGExLCBkaXJlY3RlZCA9IEZBTFNFKQ0KcGxvdChyb3V0ZXNfaWdyYXBoLCANCiAgICAgZWRnZS5hcnJvdy5zaXplID0gMTAsIA0KICAgICBsYXlvdXQ9IGxheW91dF9uaWNlbHkocm91dGVzX2lncmFwaCApLCAgDQogICAgIGVkZ2Uud2lkdGggPSAxLCANCiAgICAgdmVydGV4LmxhYmVsPU5BLCANCiAgICAgdmVydGV4LnNpemU9My41LA0KICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgIHZlcnRleC5jb2xvciA9ICIjRjBFMTMwIiwNCiAgICAgbWFpbiA9ICJOZXR3b3JrICBTYWxlc3JhbmsgdW5kZXIgMSwwMDAgLSBsYXlvdXRfbmljZWx5ICINCiAgICAgKQ0KDQpgYGANCkRpdmlkaW5nIGEgZ3JhcGggaW50byBpdHMgY29tcG9uZW50cyBpcyBvZiBjb3Vyc2Ugb25seSBhIO+sgXJzdCwgZ2xvYmFsIHdheSBvZiBkZXNjcmliaW5nIGl0cyBzdHJ1Y3R1cmUuIFdpdGhpbiBhIGdpdmVuIGNvbXBvbmVudCwgdGhlcmUgbWF5IGJlIGEgcmljaGVyIGludGVybmFsIHN0cnVjdHVyZSB0aGF0IGlzIGltcG9ydGFudCB0byBvbmXigJlzIGludGVycHJldGF0aW9uIG9mIHRoZSBuZXR3b3JrLiBBcyBhIHJlc3VsdCwgdGhlIGRhdGEgZnJvbSB0aGUgbGFyZ2VzdCBjb21wb25lbnQgaW4gdGhlIG5ldHdvcmsgb2YgcHJvZHVjdHMgd2l0aCBzYWxlc3JhbmsgdW5kZXIgMSwwMDAgaXMgZXh0cmFjdGVkIHRocm91Z2ggdGhlIOKAnGdyYXBoX3N1YnNldF9yYW5rMTAwMF9jYy50eHTigJ0gZGF0YXNldCBpbiBvcmRlciB0byBnZW5lcmF0ZSBhIHZpc3VhbCB0byBoYXZlIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG5ldHdvcmsuIA0KDQoNClRoZSBncmFwaCBiZWxvdyBkaXNwbGF5cyB0aGUgbGFyZ2VzdCBjb21wb25lbnQgaW4gdGhlIG5ldHdvcmsuIEluIHRoaXMgZ3JhcGggZXZlcnkgbm9kZSBjYW4gcmVhY2ggZXZlcnkgb3RoZXIgbm9kZSBieSBhIHBhdGgsIHdoaWNoIGluZGljYXRlcyB0aGF0IHRoZSBncmFwaCBpcyBjb25uZWN0ZWQuICANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0NCg0KZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoZmlsZSA9ICJncmFwaF9zdWJzZXRfcmFuazEwMDBfY2MudHh0IikpDQpyb3V0ZXNfaWdyYXBoMSA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZGF0YSwgZGlyZWN0ZWQgPSBGQUxTRSkNCnBsb3Qocm91dGVzX2lncmFwaDEsIA0KICAgICBlZGdlLmFycm93LnNpemUgPSAyMCwgDQogICAgIGxheW91dD0gbGF5b3V0LmthbWFkYS5rYXdhaSwNCiAgICAgdmVydGV4LmxhYmVsPU5BLCANCiAgICAgZWRnZS5jb2xvcj0iYmxhY2siLA0KICAgICB2ZXJ0ZXguY29sb3IgPSAiI0YwRTEzMCIsDQogICAgIHZlcnRleC5zaXplPTQsDQogICAgIG1haW4gPSAiVGhlIGxhcmdlc3QgY29tcG9uZW50IGluIHRoZSBuZXR3b3JrIG9mIHByb2R1Y3RzIHdpdGggc2FsZXNyYW5rIHVuZGVyIDEsMDAwIikNCg0KYGBgDQoNCkFkZGl0aW9uYWxseSwgdXNpbmcgdGhlIGRlZ3JlZSBmdW5jdGlvbiBpbiB0aGUgZ3JhcGggYmVsb3cgYWxsb3cgdXMgdG8gb2JzZXJ2ZSB0aGUgbm9kZXMgaW4gdGhlIGNvbXBvbmVudCB3aXRoIGEgbGFyZ2VyIGNvdW50IG9mIG91dGdvaW5nIGxpbmtzIHRvIGFub3RoZXIgcHJvZHVjdCBwYWdlIHJlcHJlc2VudGVkIGJ5IGEgYmlnZ2VyIG5vZGUgc2l6ZS4gDQogDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0NCg0KZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoZmlsZSA9ICJncmFwaF9zdWJzZXRfcmFuazEwMDBfY2MudHh0IikpDQpyb3V0ZXNfaWdyYXBoMSA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZGF0YSwgZGlyZWN0ZWQgPSBGQUxTRSkNCnBsb3QuaWdyYXBoKHJvdXRlc19pZ3JhcGgxLCANCiAgICAgICAgICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDIwLCANCiAgICAgICAgICAgIGxheW91dD0gbGF5b3V0LmthbWFkYS5rYXdhaSwgDQogICAgICAgICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgICAgICAgICB2ZXJ0ZXguY29sb3IgPSAiI0YwRTEzMCIsDQogICAgICAgICAgICB2ZXJ0ZXguc2l6ZT1kZWdyZWUocm91dGVzX2lncmFwaCwgbW9kZT0ib3V0IiksDQogICAgICAgICAgICBtYWluID0gIlRoZSBsYXJnZXN0IGNvbXBvbmVudCBpbiB0aGUgbmV0d29yayBvZiBwcm9kdWN0cyB3aXRoIHNhbGVzcmFuayB1bmRlciAxLDAwMCIsDQogICAgICAgICAgICB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5jb2xvciA9ICJ5ZWxsb3ciKQ0KDQoNCmBgYA0KDQojIDMuIERhdGEgYW5hbHlzaXMgPGEgbmFtZT0iMy4gRGF0YSBhbmFseXNpcyI+PC9hPg0KDQojIyAzLjEgT3V0LWRlZ3JlZSBkaXN0cmlidXRpb24gPGEgbmFtZT0iMy4xIE91dC1kZWdyZWUgZGlzdHJpYnV0aW9uIj48L2E+DQoNCkluIG9yZGVyIHRvIGNvdW50IHRoZSBudW1iZXIgb2Ygb3V0Z29pbmcgbGlua3MgdG8gYW5vdGhlciBwcm9kdWN0IHBhZ2UsIHRoZSB0YWJsZSBmdW5jdGlvbiB3YXMgdXNlZCB0byBzdW1tYXJpc2UgdGhlIG51bWJlciBvZiBvdXRib3VuZCBsaW5rcyBmb3IgZWFjaCBwcm9kdWN0IGEgLT4gYiAuIEhvd2V2ZXIsIHRoaXMgc3VtbWFyaXphdGlvbiBkb2VzIG5vdCBjb250YWluIHRoZSBwcm9kdWN0cyB3aXRoIHplcm8gb3V0Z29pbmcgbGlua3MsIHNvIGEgdW5pcXVlIGxpc3Qgb2YgcHJvZHVjdHMgaXMgZ2VuZXJhdGVkIHRvIGJlIG1lcmdlZCB3aXRoIHRoZSBzdW1tYXJpemF0aW9uIHRhYmxlIGluIG9yZGVyIHRvIGFjY291bnQgZm9yIHRoZSB6ZXJvIHZhbHVlcy4gVGhlIGNvZGUgYmVsb3cgZGVzY3JpYmVzIHRoZSBwcm9jZXNzLiAgDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyMH0NCmRhdGEyIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX2NvbXBsZXRlLnR4dCIpKQ0KY29sbmFtZXMoZGF0YTIpIDwtIGMoImEiLCAiYiIpDQoNCmNvbG5hbWVzKGRhdGEyKSA8LSBjKCJhIiwgImIiKQ0KZGF0YTMgPC0gZGF0YS5mcmFtZSh0YWJsZShkYXRhMiRhKSkNCmNvbG5hbWVzKGRhdGEzKSA8LSBjKCJhIiwgIkZyZXEiKQ0KDQojIEdldHRpbmcgdW5pcXVlIElkIFZhbHVlcw0KYSA8LSBkYXRhMiU+JXNlbGVjdChhKQ0KYTwtIHVuaXF1ZShhKQ0KYiA8LSBkYXRhMiU+JXNlbGVjdChiKQ0KY29sbmFtZXMoYikgPC0gYygiYSIpDQpiIDwtIHVuaXF1ZShiKQ0KYyA8LSByYmluZChhLGIpDQpjIDwtIHVuaXF1ZShjKQ0KDQojIE1lcmdpbmcgdW5pcXVlcyB2YWx1ZXMgdG8gY2FsY3VsYXRlIDAgb3V0Ym91bmQgbGlua3MgDQptZXJnZSA8LSBtZXJnZSh4PSBjLCB5PSBkYXRhMywgYnk9YygiYSIpLCBhbGwueCA9IFQpDQptZXJnZSA8LSBtZXJnZSU+JW11dGF0ZShGcmVxID0gaWZlbHNlKGlzLm5hKEZyZXEpLCAwLCBGcmVxKSkNCmluX2RhdGEgPC0gZGF0YS5mcmFtZSh0YWJsZShtZXJnZSRGcmVxKSkNCmluX2RhdGENCg0KYGBgDQoNCg0KVGhlIGdyYXBoIGJlbG93IGRpc3BsYXlzIHRoZSBvdXQtZGVncmVlIGRpc3RyaWJ1dGlvbiwgaW4gd2hpY2ggdGhlIG1heGltdW0gbnVtYmVyIG9mIG91dGJvdW5kIGxpbmtzIGlzIDUsIGFuZCB0aGUgaGlnaGVzdCBmcmVxdWVuY3kgaXMgNC4gDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0NCg0KZ2dwbG90KGRhdGEgPSBtZXJnZSwgYWVzKEZyZXEpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA2LCBmaWxsPSIjMDBCRUQ4IiwgY29sb3IgPSAiIzAwQkVEOCIpICsgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoDQogICAgICAgIHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTAsICBmYW1pbHk9IkNvbWljIFNhbnMgTVMiKSwNCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gIGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXkiKSwNCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkpICsgbGFicyh0aXRsZSA9ICJPdXQtZGVncmVlIGRpc3RyaWJ1dGlvbiBQbG90IiwgeCA9ICJPdXQtRGVncmVlIiwgeSA9ICJGcmVxdWVuY3kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGUgbWF4aW11bSBudW1iZXIgb2Ygb3V0Ym91bmQgbGlua3MgaXMgNSBhbmQgdGhlIGhpZ2hlc3QgZnJlcXVlbmN5IGlzIDQuIiwgY2FwdGlvbiA9ICJKb3NlIFZpbGFyZHkoMjAxOSkgfCBOZXR3b3JrIEFuYWx5dGljcyIgKSAgKyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQ0KDQpgYGANCg0KDQojIyAzLjIgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiA8YSBuYW1lPSIzLjIgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiI+PC9hPg0KDQpUaGUgZ3JhcGggYmVsb3cgZGlzcGxheXMgdGhlIGluLWRlZ3JlZSBkaXN0cmlidXRpb24uIEluIHRoaXMgY2FzZSwgdGhlIGRpc3RyaWJ1dGlvbiBpcyBkaWZmZXJlbnQgYXMgc29tZSBmZXcgcHJvZHVjdHMgaGF2ZSBtb3JlIHRoYW4gMjAwIGluY29taW5nIGxpbmtzIHdpdGggYSBtYXhpbXVtIG9mIDU0OSwgYW5kIGFsc28gdGhlcmUgaXMgYSBsYXJnZXIgbnVtYmVyIG9mIHByb2R1Y3RzIHdpdGggMCBhbmQgMSBpbmNvbWluZyBsaW5rcywgd2hpY2ggbWVhbnMgdGhhdCB0aGVyZSBhcmUgb25seSBmZXcgcG9wdWxhciBib29rcyB0aGF0IGFyZSBkcml2aW5nIGNvLXB1cmNoYXNlcyBvZiBzZXZlcmFsIG90aGVyIGJvb2tzLiANCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9DQpkYXRhNCA8LSBkYXRhLmZyYW1lKHRhYmxlKGRhdGEyJGIpKQ0KY29sbmFtZXMoZGF0YTQpIDwtIGMoImIiLCAiRnJlcSIpDQoNCmNvbG5hbWVzKGMpIDwtIGMoImIiKQ0KbWVyZ2UyIDwtIG1lcmdlKHg9IGMsIHk9IGRhdGE0LCBieT1jKCJiIiksIGFsbC54ID0gVCkNCm1lcmdlMiA8LSBtZXJnZTIlPiVtdXRhdGUoRnJlcSA9IGlmZWxzZShpcy5uYShGcmVxKSwgMCwgRnJlcSkpDQpvdXRfZGF0YSA8LSBkYXRhLmZyYW1lKHRhYmxlKG1lcmdlMiRGcmVxKSkNCg0KZ2dwbG90KGRhdGEgPSBtZXJnZTIsIGFlcyh4PUZyZXEpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDAsIGZpbGw9IiMwMEJFRDgiLCBjb2xvciA9ICIjMDBCRUQ4IikgKyAgdGhlbWVfYncoKSArIA0KICB0aGVtZSgNCiAgICAgICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCwgIGZhbWlseT0iQ29taWMgU2FucyBNUyIpLA0KICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSAgZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheSIpLA0KICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBsYWJzKHRpdGxlID0gIkluLWRlZ3JlZSBkaXN0cmlidXRpb24gUGxvdCIsIHggPSAiSW4tRGVncmVlIiwgeSA9ICJGcmVxdWVuY3kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGVyZSBpcyBhIGxhcmdlciBudW1iZXIgb2YgcHJvZHVjdHMgd2l0aCAwIGFuZCAxIGluY29taW5nIGxpbmtzLlRoZSBtYXhpbXVtIGZyZXF1ZW5jeSBpcyA1NDkgaW4tbGlua3MiLCBjYXB0aW9uID0gIkpvc2UgVmlsYXJkeSgyMDE5KSB8IE5ldHdvcmsgQW5hbHl0aWNzIiApICArIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpDQoNCmBgYA0KDQpUaGUgaW4tZGVncmVlIGRpc3RyaWJ1dGlvbiBncmFwaCBzaG93Y2FzZXMgdGhlIGxvbmcgdGFpbCBkaXN0cmlidXRpb24gb3IgcG93ZXIgbGF3IHdoZXJlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGV2ZW50IGlzIHNrZXdlZCBzbyB0aGF0IGEgc21hbGwgbnVtYmVyIG9mIG91dGNvbWVzIGhhdmUgZHJhbWF0aWNhbGx5IGhpZ2hlciB2YWx1ZXMgdGhhbiB0aGUgcmVtYWluaW5nIHBvcHVsYXRpb24sIGRyYXdpbmcgYXMgYSByZXN1bHQsIGEgY3VydmUgd2l0aCBhIGxvbmcgdGFpbCBsb3dlcmluZyBhcyB0aGUgdmFsdWUgaW5jcmVhc2VzLiANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyMH0NCg0KbWVyZ2UubCA8LSBtZXJnZTIlPiVtdXRhdGUoRnJlcSA9IGlmZWxzZShGcmVxID09IDAsIGxvZygwLjEpLCBsb2coRnJlcSkpKQ0Kb3V0X2RhdGEyIDwtIGRhdGEuZnJhbWUodGFibGUobWVyZ2UubCRGcmVxKSkNCm91dF9kYXRhMg0KDQpgYGANCg0KIyMgMy4zIEluLWRlZ3JlZSBkaXN0cmlidXRpb24gLSBsb2cgc2NhbGUgPGEgbmFtZT0iMy4zIEluLWRlZ3JlZSBkaXN0cmlidXRpb24gLSBsb2cgc2NhbGUiPjwvYT4NCg0KSW5pdGlhbGx5LCB0aGUgMCBpbmJvdW5kIGxpbmtzIHdlcmUgcmVwbGFjZWQgd2l0aCAwLjEgdG8gYmUgYWJsZSB0byBhcHBseSB0aGUgbG9nIGZ1bmN0aW9uLiANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1fQ0KDQpnZ3Bsb3QoZGF0YSA9IG1lcmdlLmwsIGFlcyh4PUZyZXEpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMCwgZmlsbD0iIzAwQkVEOCIsIGNvbG9yID0gIiMwMEJFRDgiKSArICB0aGVtZV9idygpICsgDQogIHRoZW1lKA0KICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwLCAgZmFtaWx5PSJDb21pYyBTYW5zIE1TIiksDQogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9ICBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5IiksDQogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpKSArIGxhYnModGl0bGUgPSAiSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiBQbG90IC0gbG9nIHNjYWxlIiwgeCA9ICJJbi1EZWdyZWUiLCB5ID0gIkZyZXF1ZW5jeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIlRoZSBwbG90IGluZGljYXRlcyB0aGF0IHRoZSBtb3N0IGZyZXF1ZW50IG51bWJlciBvZiBpbmNvbWluZyBsaW5rcyBpcyAwIChsb2cgKDAuMSkgPSAtMi4zKSIsIGNhcHRpb24gPSAiSm9zZSBWaWxhcmR5KDIwMTkpIHwgTmV0d29yayBBbmFseXRpY3MiKSAgKyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQ0KDQpgYGANCg0KQWZ0ZXIgYXBwbHlpbmcgdGhlIGxvZyBmdW5jdGlvbiB0byB0aGUgaW4tZGVncmVlIGF4aXMsIHRoZSBkaXN0cmlidXRpb24gaXMgc3RpbGwgc2tld2VkLiBUaGUgcGxvdCBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9zdCBmcmVxdWVudCBudW1iZXIgb2YgaW5jb21pbmcgbGlua3MgaXMgMCAobG9nICgwLjEpID0gLTIuMykuIEFsc28sIGl0IGlzIHBvc3NpYmxlIHRvIG9ic2VydmUgYSBoaWdoIG51bWJlciBvZiBib29rcyB0aGF0IGhhdmUgYmV0d2VlbiAxIGFuZCA3IGluLWxpbmtzLiANCg0KDQojIyAzLjQgQXZlcmFnZSBudW1iZXIgb2YgaW5ib3VuZCBjby1wdXJjaGFzZSBsaW5rcywgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHRoZSBtYXhpbXVtIDxhIG5hbWU9IjMuNCBBdmVyYWdlIG51bWJlciBvZiBpbmJvdW5kIGNvLXB1cmNoYXNlIGxpbmtzLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgdGhlIG1heGltdW0iPjwvYT4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDV9DQoNCk1lYXN1cmVzIDwtIGMoIk1lYW4iLCAiTWVkaWFuIiwgInN0YW5kYXJkIGRldmlhdGlvbiIsICJNYXhpbXVtIikNCk1lYW4gPC0gbWVhbihtZXJnZTIkRnJlcSkNCk1lZGlhbiA8LSBtZWRpYW4obWVyZ2UyJEZyZXEpDQpzZCA8LSBzZChtZXJnZTIkRnJlcSkNCm1heCA8LSBtYXgobWVyZ2UyJEZyZXEpDQoNCnN0YXRpc3RpY3MgPC0gYyhNZWFuLCBNZWRpYW4sIHNkLCBtYXgpDQpUYWJsZSA8LSBkYXRhLmZyYW1lKE1lYXN1cmVzLCBzdGF0aXN0aWNzKQ0KVGFibGUgPC0gVGFibGUlPiVtdXRhdGUoc3RhdGlzdGljcyA9IHJvdW5kKHN0YXRpc3RpY3MsMikpDQpUYWJsZQ0KDQpgYGANClRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lZGlhbiBhbmQgdGhlIG1lYW4gY29uZmlybXMgdGhlIHBvc2l0aXZlIHNrZXduZXNzIG9mIHRoZSBkYXRhLiBUaGUgbWF4aW11bSB2YWx1ZSA1NDkgaXMgaGlnaGx5IGRpc3RhbnQgZnJvbSB0aGUgbWVhbiB3aGljaCBldmlkZW5jZXMgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgZXh0cmVtZSB2YWx1ZXMgaW4gdGhlIGRpc3RyaWJ1dGlvbi4gDQoNCg0KIyMgMy41IDEwIHByb2R1Y3RzIHdpdGggdGhlIG1vc3QgaW5ib3VuZCBjby1wdXJjaGFzZSBsaW5rcyA8YSBuYW1lPSIzLjUgMTAgcHJvZHVjdHMgd2l0aCB0aGUgbW9zdCBpbmJvdW5kIGNvLXB1cmNoYXNlIGxpbmtzIj48L2E+DQoNClRoZSB0YWJsZSBiZWxvdyBkaXNwbGF5cyB0aGUgdG9wIDEwIHByb2R1Y3RzIHdpdGggdGhlIG1vc3QgaW5ib3VuZCBjby1wdXJjaGFzZSBsaW5rcw0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQphbGxfYm9va3MgPC0gcmVhZF9jc3YoImlkX3RvX3RpdGxlcy5jc3YiKQ0KY29sbmFtZXMoYWxsX2Jvb2tzKSA8LSBjKCJiIiwgInRpdGxlIikNCg0KbWVyZ2UzIDwtIG1lcmdlKHg9IG1lcmdlMiwgeT0gYWxsX2Jvb2tzLCBieT1jKCJiIiksIGFsbC54ID0gVCkNCm1lcmdlMyA8LSBtZXJnZTMlPiVhcnJhbmdlKGRlc2MoRnJlcSkpJT4lc2VsZWN0KHRpdGxlLCBGcmVxKQ0KcmVzdWx0IDwtIGhlYWQobWVyZ2UzLCAxMCkNCnJlc3VsdA0KDQpgYGA=