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.
3.4 Average number of inbound co-purchase links, the standard deviation, and the maximum
Measures <- c("Mean", "Median", "standard deviation", "Maximum")
Mean <- mean(merge2$Freq)
Median <- median(merge2$Freq)
sd <- sd(merge2$Freq)
max <- max(merge2$Freq)
statistics <- c(Mean, Median, sd, max)
Table <- data.frame(Measures, statistics)
Table <- Table%>%mutate(statistics = round(statistics,2))
Table
NA
The difference between the median and the mean confirms the positive skewness of the data. The maximum value 549 is highly distant from the mean which evidences the existence of the extreme values in the distribution.
3.5 10 products with the most inbound co-purchase links
The table below displays the top 10 products with the most inbound co-purchase links
all_books <- read_csv("id_to_titles.csv")
colnames(all_books) <- c("b", "title")
merge3 <- merge(x= merge2, y= all_books, by=c("b"), all.x = T)
merge3 <- merge3%>%arrange(desc(Freq))%>%select(title, Freq)
result <- head(merge3, 10)
result
NA
NA
NA
LS0tDQp0aXRsZTogIlByZS1Nb2R1bGUgTmV0d29yayBEYXRhIEFuYWx5c2lzIEluZGl2aWR1YWwgQXNzaWdubWVudCINCmF1dGhvcjogIkpvc2UgQmVsdHJhbiBWaWxhcmR5Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHdvcmRfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQojIyBUYWJsZSBvZiBjb250ZW50cw0KDQoxLiBbRG93bmxvYWQgZGF0YV0oIzEuIERvd25sb2FkIGRhdGEpDQoyLiBbRGF0YSBQcmVwYXJhdGlvbl0oIzIuIERhdGEgUHJlcGFyYXRpb24pDQozLiBbRGF0YSBhbmFseXNpc10oIzMuIERhdGEgYW5hbHlzaXMpDQozLjEuIFtPdXQtZGVncmVlIGRpc3RyaWJ1dGlvbl0oIzMuMSBPdXQtZGVncmVlIGRpc3RyaWJ1dGlvbikNCjMuMi4gW0luLWRlZ3JlZSBkaXN0cmlidXRpb25dKCMzLjIgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbikNCjMuMy4gW0luLWRlZ3JlZSBkaXN0cmlidXRpb24gLSBsb2cgc2NhbGVdKCMzLjMgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiAtIGxvZyBzY2FsZSkNCjMuNC4gW0F2ZXJhZ2UgbnVtYmVyIG9mIGluYm91bmQgY28tcHVyY2hhc2UgbGlua3MsIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24sIGFuZCB0aGUgbWF4aW11bV0oIzMuNCBBdmVyYWdlIG51bWJlciBvZiBpbmJvdW5kIGNvLXB1cmNoYXNlIGxpbmtzLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgdGhlIG1heGltdW0pDQozLjUuIFsxMCBwcm9kdWN0cyB3aXRoIHRoZSBtb3N0IGluYm91bmQgY28tcHVyY2hhc2UgbGlua3NdKCMxMCBwcm9kdWN0cyB3aXRoIHRoZSBtb3N0IGluYm91bmQgY28tcHVyY2hhc2UgbGlua3MpDQoNCg0KDQoNClRoZSBwdXJwb3NlIGlzIHRoZSByZXBvcnQgaXMgdG8gcHJvdmlkZSB0aGUgYW5zd2VyIGFuZCBzb2x1dGlvbnMgdG8gdGhlIEFtYXpvbiBwcmUtY2xhc3MgYXNzaWdubWVudC4NCg0KDQojIDEuIERvd25sb2FkIGRhdGEgPGEgbmFtZT0iMS4gRG93bmxvYWQgZGF0YSI+PC9hPg0KDQoNCkluaXRpYWxseSwgdGhlIHJlcXVpcmVkIHBhY2thZ2VzIGFyZSBkb3dubG9hZGVkIGluY2x1ZGluZyB0aGUgaWdyYXBoIHBhY2thZ2UuDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCg0KcGFja2FnZXMgPC0gYygiZHBseXIiLCAiaWdyYXBoIiwgInJlYWRyIiwgImdncGxvdDIiLCAncmVhZHhsJywgJ3JlYWRyJywgJ3RpZHl2ZXJzZScsICdnZ3RoZW1lcycsICdrbml0cicsICdleHRyYWZvbnQnLCAnc2NhbGVzJywgJ2x1YnJpZGF0ZScpIA0KIyBDaGVja2luZyBmb3IgcGFja2FnZSBpbnN0YWxsYXRpb25zIG9uIHRoZSBzeXN0ZW0gYW5kIGluc3RhbGxpbmcgaWYgbm90IGZvdW5kLg0KaWYgKGxlbmd0aChzZXRkaWZmKHBhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKSA+IDApIHsNCiAgaW5zdGFsbC5wYWNrYWdlcyhzZXRkaWZmKHBhY2thZ2VzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpKSAgDQp9DQojIFBhY2thZ2VzIHRvIHVzZQ0KZm9yKHBhY2thZ2UgaW4gcGFja2FnZXMpew0KICBsaWJyYXJ5KHBhY2thZ2UsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KDQpgYGANCg0KDQpTZWNvbmRseSwgdGhlIHJlcXVpcmVkIGRhdGEgaXMgZG93bmxvYWRlZCB1c2luZyByZWFkLnRhYmxlIGFuZCByZWFkX2NzdiBmdW5jdGlvbnMuDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCmRhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQiKSkNCmRhdGEgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKGZpbGUgPSAiZ3JhcGhfc3Vic2V0X3JhbmsxMDAwX2NjLnR4dCIpKQ0KZGF0YTIgPC0gYXMuZGF0YS5mcmFtZShyZWFkLnRhYmxlKGZpbGUgPSAiZ3JhcGhfY29tcGxldGUudHh0IikpDQphbGxfYm9va3MgPC0gcmVhZF9jc3YoImlkX3RvX3RpdGxlcy5jc3YiKQ0KDQoNCmBgYA0KDQoNCg0KIyAyLiBOZXR3b3JrIHN0cnVjdHVyZSB2aXN1YWxpemF0aW9uIDxhIG5hbWU9IjIuIE5ldHdvcmsgc3RydWN0dXJlIHZpc3VhbGl6YXRpb24iPjwvYT4NCg0KVGhlIGdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQgaXMgZG93bmxvYWRlZCBpbiBvcmRlciB0byBnZW5lcmF0ZSBhIHZpc3VhbGl6YXRpb24gb2YgdGhlIG5ldHdvcmsgc3RydWN0dXJlLiAgDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCmRhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQiKSkNCnJvdXRlc19pZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGRhdGExLCBkaXJlY3RlZCA9IEZBTFNFKQ0KcGxvdChyb3V0ZXNfaWdyYXBoLCANCiAgICAgZWRnZS5hcnJvdy5zaXplID0gMTAsIA0KICAgICBsYXlvdXQ9IGxheW91dF9pbl9jaXJjbGUocm91dGVzX2lncmFwaCksICANCiAgICAgZWRnZS53aWR0aCA9IDEsIA0KICAgICB2ZXJ0ZXgubGFiZWw9TkEsIA0KICAgICB2ZXJ0ZXguc2l6ZT0zLjUsDQogICAgIGVkZ2UuY29sb3I9IiM4MDgwODAiLA0KICAgICB2ZXJ0ZXguY29sb3IgPSAiI0YwRTEzMCIsDQogICAgIG1haW4gPSAiTmV0d29yayAgU2FsZXNyYW5rIHVuZGVyIDEsMDAwIC0gbGF5b3V0X2luX2NpcmNsZSINCiAgICAgKQ0KDQpgYGANCg0KVGhlIGluaXRpYWwgcGxvdCBpcyBnZW5lcmF0ZWQgdXNpbmcgdGhlIGxheW91dCDigJxsYXlvdXRfaW5fY2lyY2xl4oCdLCBob3dldmVyLCBpdCBpcyB2ZXJ5IGhhcmQgdG8gZ2F0aGVyIHNvbWUgaW5zaWdodHMgdXNpbmcgdGhpcyBsYXlvdXQuIEZvciBleGFtcGxlLCBpdCBpcyBub3QgcG9zc2libGUgdG8gc2VlIGlmIGFsbCB0aGUgZ3JhcGggaXMgb3Igbm90IGNvbm5lY3RlZC4gDQoNClVzaW5nIG90aGVyIHR5cGVzIG9mIGxheW91dHMgc3VjaCBhcyBsYXlvdXQua2FtYWRhLmthd2FpIHByb3ZpZGVzIGJldHRlciB2aXN1YWxpemF0aW9uLCBhbmQgaXQgaXMgcG9zc2libGUgdG8gc2VlIGEgZGlzY29ubmVjdGVkIGdyYXBoLCB3aGljaCBtZWFucyBpdCBicmVha3MgYXBhcnQgbmF0dXJhbGx5IGludG8gYSBzZXQgb2YgY29ubmVjdGVkIGdyb3VwcyBvZiBub2RlcyBuYW1lZCBjb21wb25lbnRzLiANCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQ0KDQpkYXRhMSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoZmlsZSA9ICJncmFwaF9zdWJzZXRfcmFuazEwMDAudHh0IikpDQpyb3V0ZXNfaWdyYXBoIDwtIGdyYXBoX2Zyb21fZGF0YV9mcmFtZShkYXRhMSwgZGlyZWN0ZWQgPSBGQUxTRSkNCnBsb3Qocm91dGVzX2lncmFwaCwgDQogICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDEwLCANCiAgICAgbGF5b3V0PSBsYXlvdXQua2FtYWRhLmthd2FpLCAgDQogICAgIGVkZ2Uud2lkdGggPSAxLCANCiAgICAgdmVydGV4LmxhYmVsPU5BLCANCiAgICAgdmVydGV4LnNpemU9My41LA0KICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgIHZlcnRleC5jb2xvciA9ICIjRjBFMTMwIiwNCiAgICAgbWFpbiA9ICJOZXR3b3JrICBTYWxlc3JhbmsgdW5kZXIgMSwwMDAgLSBsYXlvdXQua2FtYWRhLmthd2FpIg0KICAgICApDQoNCmBgYA0KDQpUaGUgZ3JhcGggYmVsb3cgdXNlcyB0aGUgbGF5b3V0IG5hbWVkIOKAnGxheW91dF9uaWNlbHnigJ0gd2hpY2ggYWxsb3dzIHVzIHRvIGJldHRlciB2aXN1YWxpemUgdGhlIGNvbXBvbmVudHMgd2l0aGluIHRoZSBuZXR3b3JrLiBUaGVyZSBpcyBhIGxhcmdlciBjb25uZWN0ZWQgY29tcG9uZW50IGluIHRoZSBjZW50ZXIgb2YgdGhlIGNpcmNsZSwgdGhpcyBjb21wb25lbnQgaGFzIGEgaGlnaGVyIG51bWJlciBvZiBub2RlcyB0aGFuIHRoZSByZXN0IG9mIHRoZSBjb21wb25lbnRzIGluIHRoZSBncmFwaCwgd2hpY2ggY291bGQgaW5kaWNhdGUgYSBjbHVzdGVyIG9mIGFtYXpvbiBwcm9kdWN0cy4gDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQoNCmRhdGExIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX3N1YnNldF9yYW5rMTAwMC50eHQiKSkNCnJvdXRlc19pZ3JhcGggPC0gZ3JhcGhfZnJvbV9kYXRhX2ZyYW1lKGRhdGExLCBkaXJlY3RlZCA9IEZBTFNFKQ0KcGxvdChyb3V0ZXNfaWdyYXBoLCANCiAgICAgZWRnZS5hcnJvdy5zaXplID0gMTAsIA0KICAgICBsYXlvdXQ9IGxheW91dF9uaWNlbHkocm91dGVzX2lncmFwaCApLCAgDQogICAgIGVkZ2Uud2lkdGggPSAxLCANCiAgICAgdmVydGV4LmxhYmVsPU5BLCANCiAgICAgdmVydGV4LnNpemU9My41LA0KICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgIHZlcnRleC5jb2xvciA9ICIjRjBFMTMwIiwNCiAgICAgbWFpbiA9ICJOZXR3b3JrICBTYWxlc3JhbmsgdW5kZXIgMSwwMDAgLSBsYXlvdXRfbmljZWx5ICINCiAgICAgKQ0KDQpgYGANCkRpdmlkaW5nIGEgZ3JhcGggaW50byBpdHMgY29tcG9uZW50cyBpcyBvZiBjb3Vyc2Ugb25seSBhIO+sgXJzdCwgZ2xvYmFsIHdheSBvZiBkZXNjcmliaW5nIGl0cyBzdHJ1Y3R1cmUuIFdpdGhpbiBhIGdpdmVuIGNvbXBvbmVudCwgdGhlcmUgbWF5IGJlIGEgcmljaGVyIGludGVybmFsIHN0cnVjdHVyZSB0aGF0IGlzIGltcG9ydGFudCB0byBvbmXigJlzIGludGVycHJldGF0aW9uIG9mIHRoZSBuZXR3b3JrLiBBcyBhIHJlc3VsdCwgdGhlIGRhdGEgZnJvbSB0aGUgbGFyZ2VzdCBjb21wb25lbnQgaW4gdGhlIG5ldHdvcmsgb2YgcHJvZHVjdHMgd2l0aCBzYWxlc3JhbmsgdW5kZXIgMSwwMDAgaXMgZXh0cmFjdGVkIHRocm91Z2ggdGhlIOKAnGdyYXBoX3N1YnNldF9yYW5rMTAwMF9jYy50eHTigJ0gZGF0YXNldCBpbiBvcmRlciB0byBnZW5lcmF0ZSBhIHZpc3VhbCB0byBoYXZlIGEgYmV0dGVyIHVuZGVyc3RhbmRpbmcgb2YgdGhlIG5ldHdvcmsuIA0KDQoNClRoZSBncmFwaCBiZWxvdyBkaXNwbGF5cyB0aGUgbGFyZ2VzdCBjb21wb25lbnQgaW4gdGhlIG5ldHdvcmsuIEluIHRoaXMgZ3JhcGggZXZlcnkgbm9kZSBjYW4gcmVhY2ggZXZlcnkgb3RoZXIgbm9kZSBieSBhIHBhdGgsIHdoaWNoIGluZGljYXRlcyB0aGF0IHRoZSBncmFwaCBpcyBjb25uZWN0ZWQuICANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0NCg0KZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoZmlsZSA9ICJncmFwaF9zdWJzZXRfcmFuazEwMDBfY2MudHh0IikpDQpyb3V0ZXNfaWdyYXBoMSA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZGF0YSwgZGlyZWN0ZWQgPSBGQUxTRSkNCnBsb3Qocm91dGVzX2lncmFwaDEsIA0KICAgICBlZGdlLmFycm93LnNpemUgPSAyMCwgDQogICAgIGxheW91dD0gbGF5b3V0LmthbWFkYS5rYXdhaSwNCiAgICAgdmVydGV4LmxhYmVsPU5BLCANCiAgICAgZWRnZS5jb2xvcj0iYmxhY2siLA0KICAgICB2ZXJ0ZXguY29sb3IgPSAiI0YwRTEzMCIsDQogICAgIHZlcnRleC5zaXplPTQsDQogICAgIG1haW4gPSAiVGhlIGxhcmdlc3QgY29tcG9uZW50IGluIHRoZSBuZXR3b3JrIG9mIHByb2R1Y3RzIHdpdGggc2FsZXNyYW5rIHVuZGVyIDEsMDAwIikNCg0KYGBgDQoNCkFkZGl0aW9uYWxseSwgdXNpbmcgdGhlIGRlZ3JlZSBmdW5jdGlvbiBpbiB0aGUgZ3JhcGggYmVsb3cgYWxsb3cgdXMgdG8gb2JzZXJ2ZSB0aGUgbm9kZXMgaW4gdGhlIGNvbXBvbmVudCB3aXRoIGEgbGFyZ2VyIGNvdW50IG9mIG91dGdvaW5nIGxpbmtzIHRvIGFub3RoZXIgcHJvZHVjdCBwYWdlIHJlcHJlc2VudGVkIGJ5IGEgYmlnZ2VyIG5vZGUgc2l6ZS4gDQogDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSAxMH0NCg0KZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWQudGFibGUoZmlsZSA9ICJncmFwaF9zdWJzZXRfcmFuazEwMDBfY2MudHh0IikpDQpyb3V0ZXNfaWdyYXBoMSA8LSBncmFwaF9mcm9tX2RhdGFfZnJhbWUoZGF0YSwgZGlyZWN0ZWQgPSBGQUxTRSkNCnBsb3QuaWdyYXBoKHJvdXRlc19pZ3JhcGgxLCANCiAgICAgICAgICAgIGVkZ2UuYXJyb3cuc2l6ZSA9IDIwLCANCiAgICAgICAgICAgIGxheW91dD0gbGF5b3V0LmthbWFkYS5rYXdhaSwgDQogICAgICAgICAgICBlZGdlLmNvbG9yPSJibGFjayIsDQogICAgICAgICAgICB2ZXJ0ZXguY29sb3IgPSAiI0YwRTEzMCIsDQogICAgICAgICAgICB2ZXJ0ZXguc2l6ZT1kZWdyZWUocm91dGVzX2lncmFwaCwgbW9kZT0ib3V0IiksDQogICAgICAgICAgICBtYWluID0gIlRoZSBsYXJnZXN0IGNvbXBvbmVudCBpbiB0aGUgbmV0d29yayBvZiBwcm9kdWN0cyB3aXRoIHNhbGVzcmFuayB1bmRlciAxLDAwMCIsDQogICAgICAgICAgICB2ZXJ0ZXgubGFiZWw9TkEsIHZlcnRleC5jb2xvciA9ICJ5ZWxsb3ciKQ0KDQoNCmBgYA0KDQojIDMuIERhdGEgYW5hbHlzaXMgPGEgbmFtZT0iMy4gRGF0YSBhbmFseXNpcyI+PC9hPg0KDQojIyAzLjEgT3V0LWRlZ3JlZSBkaXN0cmlidXRpb24gPGEgbmFtZT0iMy4xIE91dC1kZWdyZWUgZGlzdHJpYnV0aW9uIj48L2E+DQoNCkluIG9yZGVyIHRvIGNvdW50IHRoZSBudW1iZXIgb2Ygb3V0Z29pbmcgbGlua3MgdG8gYW5vdGhlciBwcm9kdWN0IHBhZ2UsIHRoZSB0YWJsZSBmdW5jdGlvbiB3YXMgdXNlZCB0byBzdW1tYXJpc2UgdGhlIG51bWJlciBvZiBvdXRib3VuZCBsaW5rcyBmb3IgZWFjaCBwcm9kdWN0IGEgLT4gYiAuIEhvd2V2ZXIsIHRoaXMgc3VtbWFyaXphdGlvbiBkb2VzIG5vdCBjb250YWluIHRoZSBwcm9kdWN0cyB3aXRoIHplcm8gb3V0Z29pbmcgbGlua3MsIHNvIGEgdW5pcXVlIGxpc3Qgb2YgcHJvZHVjdHMgaXMgZ2VuZXJhdGVkIHRvIGJlIG1lcmdlZCB3aXRoIHRoZSBzdW1tYXJpemF0aW9uIHRhYmxlIGluIG9yZGVyIHRvIGFjY291bnQgZm9yIHRoZSB6ZXJvIHZhbHVlcy4gVGhlIGNvZGUgYmVsb3cgZGVzY3JpYmVzIHRoZSBwcm9jZXNzLiAgDQoNCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyMH0NCmRhdGEyIDwtIGFzLmRhdGEuZnJhbWUocmVhZC50YWJsZShmaWxlID0gImdyYXBoX2NvbXBsZXRlLnR4dCIpKQ0KY29sbmFtZXMoZGF0YTIpIDwtIGMoImEiLCAiYiIpDQoNCmNvbG5hbWVzKGRhdGEyKSA8LSBjKCJhIiwgImIiKQ0KZGF0YTMgPC0gZGF0YS5mcmFtZSh0YWJsZShkYXRhMiRhKSkNCmNvbG5hbWVzKGRhdGEzKSA8LSBjKCJhIiwgIkZyZXEiKQ0KDQojIEdldHRpbmcgdW5pcXVlIElkIFZhbHVlcw0KYSA8LSBkYXRhMiU+JXNlbGVjdChhKQ0KYTwtIHVuaXF1ZShhKQ0KYiA8LSBkYXRhMiU+JXNlbGVjdChiKQ0KY29sbmFtZXMoYikgPC0gYygiYSIpDQpiIDwtIHVuaXF1ZShiKQ0KYyA8LSByYmluZChhLGIpDQpjIDwtIHVuaXF1ZShjKQ0KDQojIE1lcmdpbmcgdW5pcXVlcyB2YWx1ZXMgdG8gY2FsY3VsYXRlIDAgb3V0Ym91bmQgbGlua3MgDQptZXJnZSA8LSBtZXJnZSh4PSBjLCB5PSBkYXRhMywgYnk9YygiYSIpLCBhbGwueCA9IFQpDQptZXJnZSA8LSBtZXJnZSU+JW11dGF0ZShGcmVxID0gaWZlbHNlKGlzLm5hKEZyZXEpLCAwLCBGcmVxKSkNCmluX2RhdGEgPC0gZGF0YS5mcmFtZSh0YWJsZShtZXJnZSRGcmVxKSkNCmluX2RhdGENCg0KYGBgDQoNCg0KVGhlIGdyYXBoIGJlbG93IGRpc3BsYXlzIHRoZSBvdXQtZGVncmVlIGRpc3RyaWJ1dGlvbiwgaW4gd2hpY2ggdGhlIG1heGltdW0gbnVtYmVyIG9mIG91dGJvdW5kIGxpbmtzIGlzIDUsIGFuZCB0aGUgaGlnaGVzdCBmcmVxdWVuY3kgaXMgNC4gDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNX0NCg0KZ2dwbG90KGRhdGEgPSBtZXJnZSwgYWVzKEZyZXEpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSA2LCBmaWxsPSIjMDBCRUQ4IiwgY29sb3IgPSAiIzAwQkVEOCIpICsgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoDQogICAgICAgIHRleHQ9ZWxlbWVudF90ZXh0KHNpemU9MTAsICBmYW1pbHk9IkNvbWljIFNhbnMgTVMiKSwNCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gIGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXkiKSwNCiAgICAgICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkpICsgbGFicyh0aXRsZSA9ICJPdXQtZGVncmVlIGRpc3RyaWJ1dGlvbiBQbG90IiwgeCA9ICJPdXQtRGVncmVlIiwgeSA9ICJGcmVxdWVuY3kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGUgbWF4aW11bSBudW1iZXIgb2Ygb3V0Ym91bmQgbGlua3MgaXMgNSBhbmQgdGhlIGhpZ2hlc3QgZnJlcXVlbmN5IGlzIDQuIiwgY2FwdGlvbiA9ICJKb3NlIFZpbGFyZHkoMjAxOSkgfCBOZXR3b3JrIEFuYWx5dGljcyIgKSAgKyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQ0KDQpgYGANCg0KDQojIyAzLjIgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiA8YSBuYW1lPSIzLjIgSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiI+PC9hPg0KDQpUaGUgZ3JhcGggYmVsb3cgZGlzcGxheXMgdGhlIGluLWRlZ3JlZSBkaXN0cmlidXRpb24uIEluIHRoaXMgY2FzZSwgdGhlIGRpc3RyaWJ1dGlvbiBpcyBkaWZmZXJlbnQgYXMgc29tZSBmZXcgcHJvZHVjdHMgaGF2ZSBtb3JlIHRoYW4gMjAwIGluY29taW5nIGxpbmtzIHdpdGggYSBtYXhpbXVtIG9mIDU0OSwgYW5kIGFsc28gdGhlcmUgaXMgYSBsYXJnZXIgbnVtYmVyIG9mIHByb2R1Y3RzIHdpdGggMCBhbmQgMSBpbmNvbWluZyBsaW5rcywgd2hpY2ggbWVhbnMgdGhhdCB0aGVyZSBhcmUgb25seSBmZXcgcG9wdWxhciBib29rcyB0aGF0IGFyZSBkcml2aW5nIGNvLXB1cmNoYXNlcyBvZiBzZXZlcmFsIG90aGVyIGJvb2tzLiANCg0KDQpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDV9DQpkYXRhNCA8LSBkYXRhLmZyYW1lKHRhYmxlKGRhdGEyJGIpKQ0KY29sbmFtZXMoZGF0YTQpIDwtIGMoImIiLCAiRnJlcSIpDQoNCmNvbG5hbWVzKGMpIDwtIGMoImIiKQ0KbWVyZ2UyIDwtIG1lcmdlKHg9IGMsIHk9IGRhdGE0LCBieT1jKCJiIiksIGFsbC54ID0gVCkNCm1lcmdlMiA8LSBtZXJnZTIlPiVtdXRhdGUoRnJlcSA9IGlmZWxzZShpcy5uYShGcmVxKSwgMCwgRnJlcSkpDQpvdXRfZGF0YSA8LSBkYXRhLmZyYW1lKHRhYmxlKG1lcmdlMiRGcmVxKSkNCg0KZ2dwbG90KGRhdGEgPSBtZXJnZTIsIGFlcyh4PUZyZXEpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDAsIGZpbGw9IiMwMEJFRDgiLCBjb2xvciA9ICIjMDBCRUQ4IikgKyAgdGhlbWVfYncoKSArIA0KICB0aGVtZSgNCiAgICAgICAgdGV4dD1lbGVtZW50X3RleHQoc2l6ZT0xMCwgIGZhbWlseT0iQ29taWMgU2FucyBNUyIpLA0KICAgICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIHBhbmVsLmdyaWQubWlub3IgPSAgZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JheSIpLA0KICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBsYWJzKHRpdGxlID0gIkluLWRlZ3JlZSBkaXN0cmlidXRpb24gUGxvdCIsIHggPSAiSW4tRGVncmVlIiwgeSA9ICJGcmVxdWVuY3kiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICJUaGVyZSBpcyBhIGxhcmdlciBudW1iZXIgb2YgcHJvZHVjdHMgd2l0aCAwIGFuZCAxIGluY29taW5nIGxpbmtzLlRoZSBtYXhpbXVtIGZyZXF1ZW5jeSBpcyA1NDkgaW4tbGlua3MiLCBjYXB0aW9uID0gIkpvc2UgVmlsYXJkeSgyMDE5KSB8IE5ldHdvcmsgQW5hbHl0aWNzIiApICArIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBjb21tYSkgKyBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpDQoNCmBgYA0KDQpUaGUgaW4tZGVncmVlIGRpc3RyaWJ1dGlvbiBncmFwaCBzaG93Y2FzZXMgdGhlIGxvbmcgdGFpbCBkaXN0cmlidXRpb24gb3IgcG93ZXIgbGF3IHdoZXJlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIGV2ZW50IGlzIHNrZXdlZCBzbyB0aGF0IGEgc21hbGwgbnVtYmVyIG9mIG91dGNvbWVzIGhhdmUgZHJhbWF0aWNhbGx5IGhpZ2hlciB2YWx1ZXMgdGhhbiB0aGUgcmVtYWluaW5nIHBvcHVsYXRpb24sIGRyYXdpbmcgYXMgYSByZXN1bHQsIGEgY3VydmUgd2l0aCBhIGxvbmcgdGFpbCBsb3dlcmluZyBhcyB0aGUgdmFsdWUgaW5jcmVhc2VzLiANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyMH0NCg0KbWVyZ2UubCA8LSBtZXJnZTIlPiVtdXRhdGUoRnJlcSA9IGlmZWxzZShGcmVxID09IDAsIGxvZygwLjEpLCBsb2coRnJlcSkpKQ0Kb3V0X2RhdGEyIDwtIGRhdGEuZnJhbWUodGFibGUobWVyZ2UubCRGcmVxKSkNCm91dF9kYXRhMg0KDQpgYGANCg0KIyMgMy4zIEluLWRlZ3JlZSBkaXN0cmlidXRpb24gLSBsb2cgc2NhbGUgPGEgbmFtZT0iMy4zIEluLWRlZ3JlZSBkaXN0cmlidXRpb24gLSBsb2cgc2NhbGUiPjwvYT4NCg0KSW5pdGlhbGx5LCB0aGUgMCBpbmJvdW5kIGxpbmtzIHdlcmUgcmVwbGFjZWQgd2l0aCAwLjEgdG8gYmUgYWJsZSB0byBhcHBseSB0aGUgbG9nIGZ1bmN0aW9uLiANCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA1fQ0KDQpnZ3Bsb3QoZGF0YSA9IG1lcmdlLmwsIGFlcyh4PUZyZXEpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMCwgZmlsbD0iIzAwQkVEOCIsIGNvbG9yID0gIiMwMEJFRDgiKSArICB0aGVtZV9idygpICsgDQogIHRoZW1lKA0KICAgICAgICB0ZXh0PWVsZW1lbnRfdGV4dChzaXplPTEwLCAgZmFtaWx5PSJDb21pYyBTYW5zIE1TIiksDQogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9ICBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShjb2xvciA9ICJncmF5IiksDQogICAgICAgIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpKSArIGxhYnModGl0bGUgPSAiSW4tZGVncmVlIGRpc3RyaWJ1dGlvbiBQbG90IC0gbG9nIHNjYWxlIiwgeCA9ICJJbi1EZWdyZWUiLCB5ID0gIkZyZXF1ZW5jeSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gIlRoZSBwbG90IGluZGljYXRlcyB0aGF0IHRoZSBtb3N0IGZyZXF1ZW50IG51bWJlciBvZiBpbmNvbWluZyBsaW5rcyBpcyAwIChsb2cgKDAuMSkgPSAtMi4zKSIsIGNhcHRpb24gPSAiSm9zZSBWaWxhcmR5KDIwMTkpIHwgTmV0d29yayBBbmFseXRpY3MiKSAgKyBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gY29tbWEpICsgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKQ0KDQpgYGANCg0KQWZ0ZXIgYXBwbHlpbmcgdGhlIGxvZyBmdW5jdGlvbiB0byB0aGUgaW4tZGVncmVlIGF4aXMsIHRoZSBkaXN0cmlidXRpb24gaXMgc3RpbGwgc2tld2VkLiBUaGUgcGxvdCBpbmRpY2F0ZXMgdGhhdCB0aGUgbW9zdCBmcmVxdWVudCBudW1iZXIgb2YgaW5jb21pbmcgbGlua3MgaXMgMCAobG9nICgwLjEpID0gLTIuMykuIEFsc28sIGl0IGlzIHBvc3NpYmxlIHRvIG9ic2VydmUgYSBoaWdoIG51bWJlciBvZiBib29rcyB0aGF0IGhhdmUgYmV0d2VlbiAxIGFuZCA3IGluLWxpbmtzLiANCg0KDQojIyAzLjQgQXZlcmFnZSBudW1iZXIgb2YgaW5ib3VuZCBjby1wdXJjaGFzZSBsaW5rcywgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIHRoZSBtYXhpbXVtIDxhIG5hbWU9IjMuNCBBdmVyYWdlIG51bWJlciBvZiBpbmJvdW5kIGNvLXB1cmNoYXNlIGxpbmtzLCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLCBhbmQgdGhlIG1heGltdW0iPjwvYT4NCg0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDV9DQoNCk1lYXN1cmVzIDwtIGMoIk1lYW4iLCAiTWVkaWFuIiwgInN0YW5kYXJkIGRldmlhdGlvbiIsICJNYXhpbXVtIikNCk1lYW4gPC0gbWVhbihtZXJnZTIkRnJlcSkNCk1lZGlhbiA8LSBtZWRpYW4obWVyZ2UyJEZyZXEpDQpzZCA8LSBzZChtZXJnZTIkRnJlcSkNCm1heCA8LSBtYXgobWVyZ2UyJEZyZXEpDQoNCnN0YXRpc3RpY3MgPC0gYyhNZWFuLCBNZWRpYW4sIHNkLCBtYXgpDQpUYWJsZSA8LSBkYXRhLmZyYW1lKE1lYXN1cmVzLCBzdGF0aXN0aWNzKQ0KVGFibGUgPC0gVGFibGUlPiVtdXRhdGUoc3RhdGlzdGljcyA9IHJvdW5kKHN0YXRpc3RpY3MsMikpDQpUYWJsZQ0KDQpgYGANClRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIG1lZGlhbiBhbmQgdGhlIG1lYW4gY29uZmlybXMgdGhlIHBvc2l0aXZlIHNrZXduZXNzIG9mIHRoZSBkYXRhLiBUaGUgbWF4aW11bSB2YWx1ZSA1NDkgaXMgaGlnaGx5IGRpc3RhbnQgZnJvbSB0aGUgbWVhbiB3aGljaCBldmlkZW5jZXMgdGhlIGV4aXN0ZW5jZSBvZiB0aGUgZXh0cmVtZSB2YWx1ZXMgaW4gdGhlIGRpc3RyaWJ1dGlvbi4gDQoNCg0KIyMgMy41IDEwIHByb2R1Y3RzIHdpdGggdGhlIG1vc3QgaW5ib3VuZCBjby1wdXJjaGFzZSBsaW5rcyA8YSBuYW1lPSIzLjUgMTAgcHJvZHVjdHMgd2l0aCB0aGUgbW9zdCBpbmJvdW5kIGNvLXB1cmNoYXNlIGxpbmtzIj48L2E+DQoNClRoZSB0YWJsZSBiZWxvdyBkaXNwbGF5cyB0aGUgdG9wIDEwIHByb2R1Y3RzIHdpdGggdGhlIG1vc3QgaW5ib3VuZCBjby1wdXJjaGFzZSBsaW5rcw0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9DQphbGxfYm9va3MgPC0gcmVhZF9jc3YoImlkX3RvX3RpdGxlcy5jc3YiKQ0KY29sbmFtZXMoYWxsX2Jvb2tzKSA8LSBjKCJiIiwgInRpdGxlIikNCg0KbWVyZ2UzIDwtIG1lcmdlKHg9IG1lcmdlMiwgeT0gYWxsX2Jvb2tzLCBieT1jKCJiIiksIGFsbC54ID0gVCkNCm1lcmdlMyA8LSBtZXJnZTMlPiVhcnJhbmdlKGRlc2MoRnJlcSkpJT4lc2VsZWN0KHRpdGxlLCBGcmVxKQ0KcmVzdWx0IDwtIGhlYWQobWVyZ2UzLCAxMCkNCnJlc3VsdA0KDQpgYGA=