Background

Amazon sellers that are part of the company’s Brand Registry program have access to Amazon’s Brand Analytics, a feature which provides valuable data to Amazon sellers on the larger Amazon product catalog, demographic data on a seller’s customers, and other useful information. One of the reports provided by Brand Analytics is the Amazon Search Terms report, which includes a list of the most popular search terms on Amazon for a selected time frame, ranked. In addition to providing the search frequency rank, the report also provides data on the top three clicked products for each search term, and the share that those products have of all the clicks and sales made when a customer searches that term. While this is useful for a seller tracking the position of their own products on the Amazon platform, it can also be used to observe trends concerning broader customer interests and their interaction with the Amazon product catalog. The full report, when downloaded, includes records on the top 1,000,000 search terms for a select time period. The short project below, done in R, explores this data for the week-long period of 5/22/22 - 5/28/22 and provides visualization of some of the trends that can be ascertained from this large dataset.

For a description of the Amazon report in question, refer here.

Loading libraries:

library(tidyverse)
library(ggplot2)
library(tidyr)
library(janitor)
library(ggwordcloud)
library(wordcloud)
library(tm)
library(NLP)

reading CSV file:

search_data <- read_csv("/Users/YourName/Desktop/File.csv", skip=1)

str(search_data)

search_data <- clean_names(search_data)

Preview the dataset:

head(search_data,10)

Let’s remove the % symbol and make the values numeric for all columns where a % symbol appears:

search_data$number_1_click_share <- as.numeric(sub("%", "", search_data$number_1_click_share))

search_data$number_2_click_share <- as.numeric(sub("%", "", search_data$number_2_click_share))

search_data$number_3_click_share <- as.numeric(sub("%", "", search_data$number_3_click_share))

search_data$number_1_conversion_share <- as.numeric(sub("%", "", search_data$number_1_conversion_share))

search_data$number_2_conversion_share <- as.numeric(sub("%", "", search_data$number_2_conversion_share))

search_data$number_3_conversion_share <- as.numeric(sub("%", "", search_data$number_3_conversion_share))

Let’s create a wordcloud to see what people are searching the most for in this time period. To make this more manageable, lets work with the top 30,000 searches, and words mentioned more than 50 times.

top_30k <- search_data %>% filter(search_frequency_rank<=30000)

wordcloud(top_30k$search_term, min.freq=50)

Products with a search rank of less than or equal to 30,000 represent some of the most searched (and purchased) Amazon products, and our list above only includes those search terms that were repeated more than 50 times in these twop 30,000 entries. The wordcloud above therefore gives us a quick glimpse into Amazon customers’ top search behavior during the time period looked at at the end of May. Given well-known sales trends, we unsurpisingly see “women” as our top searched term. We see other sales demographics noted as key search terms as well, including men, baby, and dog (i.e. pets). Given the summer time frame looked at in this search terms report, it is unsurprising to see terms such as “outdoor,” “sunglasses,” “summer,” “sunscreen” and “swimming” appear in our wordcloud.

Our data currently gives total click share and conversion share relative to all those products clicked for a certain search term by a customer, and gives this separately for each of the top 3 clicked products. Let’s create two columns to capture the summed numbers from all of these three products:

Create total_click_share column and total_conversion_share column:

search_data <- mutate(search_data, total_click_share = number_1_click_share + number_2_click_share + number_3_click_share)

search_data <- mutate(search_data, total_conversion_share = number_1_conversion_share + number_2_conversion_share + number_3_conversion_share)

See the right most columns for these added columns:

head(search_data,10)

Explore if there is any missing data in our new total_click_share column:

which(is.na(search_data$total_click_share))
integer(0)

There is no missing data in this column.

Explore if there is any missing data in our new total_conversion_share column:

str(which(is.na(search_data$total_conversion_share)))
 int [1:2709] 39125 56329 73720 75440 88342 117020 129896 140058 159939 167344 ...

There are close to ~3000 missing values.

Looks like total_conversion_share has missing values. Let’s explore further what the reason is:

na_total_conversion_share <- search_data %>% filter(is.na(search_data$total_conversion_share))

head(na_total_conversion_share,50)

Upon exploring the examples of the NA values above, they appear to be examples where a single product garners 100% of the clickshare for a search term, and zero sales are made. This is believable when we consider that practically all of the items in this list have a poor search frequency rank. Nothing in the top 30,000 appears. In many but not all the cases, the term that is searched is also not one that is done by a normal user. They are searches for specific Amazon identification numbers, also known as the Amazon “ASIN”, indicating that the customer was only looking for 1 possible search result (e.g., a specific make/brand for a product). This may also reflect the search of a software gathering data on Amazon products, or an attempt at artificially boosting a product search rank by a seller.

In some of the cases of a real search term, e.g., “silk pillowcase queen size”, it is possible that there was a flaw in the data creation, as these search terms, when independently searched on Amazon, give several relevant results where one would expect more than one product would be clicked. Thankfully the number of these examples is few relative to the dataset of 1,000,000 serach terms, and they will be automatically removed from graph we will plot below because they are null.

Let’s explore some of these outlier cases where a single product garnered 100% of the clickshare for a particular search term in the broader dataset:

outlier_no_1_click_share <- search_data %>% filter(search_data$number_1_click_share == 100)
head(outlier_no_1_click_share,80)
str(outlier_no_1_click_share)
tibble [9,063 × 17] (S3: tbl_df/tbl/data.frame)
 $ department               : chr [1:9063] "Amazon.com" "Amazon.com" "Amazon.com" "Amazon.com" ...
 $ search_term              : chr [1:9063] "silk pillowcase queen size" "campling light" "campling lights" "a. b. c." ...
 $ search_frequency_rank    : num [1:9063] 39125 56329 73720 74403 75440 ...
 $ number_1_clicked_asin    : chr [1:9063] "B07Y4QMHMQ" "B09YDP9989" "B09YDP9989" "B06Y2NHTH5" ...
 $ number_1_product_title   : chr [1:9063] "THXSILK Silk Pillowcase 22 Momme 100% Pure Mulberry Natural Soft Breathable Pillow Cover with Hidden Zipper Clo"| __truncated__ "4Pcs/ Set LED Camping Light ,Lotus Blue Portable LED Tent Lantern 4 Modes for Backpacking Camping Hiking Fishin"| __truncated__ "4Pcs/ Set LED Camping Light ,Lotus Blue Portable LED Tent Lantern 4 Modes for Backpacking Camping Hiking Fishin"| __truncated__ "Alphabet Train - Learn ABCs with Vehicles and Animals for Kids" ...
 $ number_1_click_share     : num [1:9063] 100 100 100 100 100 100 100 100 100 100 ...
 $ number_1_conversion_share: num [1:9063] NA NA NA 100 NA NA NA NA NA 100 ...
 $ number_2_clicked_asin    : chr [1:9063] "null" "null" "null" "null" ...
 $ number_2_product_title   : chr [1:9063] "null" "null" "null" "null" ...
 $ number_2_click_share     : num [1:9063] 0 0 0 0 0 0 0 0 0 0 ...
 $ number_2_conversion_share: num [1:9063] 0 0 0 0 0 0 0 0 0 0 ...
 $ number_3_clicked_asin    : chr [1:9063] "null" "null" "null" "null" ...
 $ number_3_product_title   : chr [1:9063] "null" "null" "null" "null" ...
 $ number_3_click_share     : num [1:9063] 0 0 0 0 0 0 0 0 0 0 ...
 $ number_3_conversion_share: num [1:9063] 0 0 0 0 0 0 0 0 0 0 ...
 $ total_click_share        : num [1:9063] 100 100 100 100 100 100 100 100 100 100 ...
 $ total_conversion_share   : num [1:9063] NA NA NA 100 NA NA NA NA NA 100 ...

There are only ~ 9000 of such cases (~0.9% of the data) in the larger data set, and they seem to fit the types of searches we just described: either they are poor ranked terms where purchases are infrequent and we could expect a single product gathering the entirety of a term’s clickshare, or the search terms appear to be unnatural attempts at zeroing in on a single product through an ASIN.

We can visually see where the ranking concentration of these cases where only a single product contains 100% of the click share in the scatter plot below. It is clear that examples of these cases are primarily found where search terms are ranked lower where this type of case would makes sense.

Here I create a scatter plot showing the concentration of these outlier cases in different keyword search rankings. I will set alpha for our points to 0.01 because we are dealing with several thousand data points:

ggplot(data=outlier_no_1_click_share) + geom_point(mapping=aes(x=search_frequency_rank, y=total_click_share),alpha=0.05) +
  labs(title="Click Share % of Top Clicked Product vs. Keyword Search Rank", 
       caption="A visual representation showing the outlier cases of one product with 100% clickshare for a keyword", x="Search Frequency Rank for Keyword (Bigger # Means Lower Search Volume)",
       y="% of Click Share by Top Clicked Product") + coord_cartesian(xlim =c(0, 1000000), ylim = c(0, 100))

We see how these cases are infrequent where search frequency rank is greater than 250,000. This makes sense with expected customer behavior. We expect that a search term that is more frequently seached by customers will have more relevant product offerings, and therefore a greater likelihood that more than one product will be clicked by a customer.

What about cases where the top clicked product controls 100% of conversion/sales for that search term?

outlier_no_1_conversion_share <- search_data %>% filter(search_data$number_1_conversion_share == 100)
head(outlier_no_1_conversion_share, 80)
str(outlier_no_1_conversion_share)
tibble [90,768 × 17] (S3: tbl_df/tbl/data.frame)
 $ department               : chr [1:90768] "Amazon.com" "Amazon.com" "Amazon.com" "Amazon.com" ...
 $ search_term              : chr [1:90768] "amazon.co.uk" "bosch legacy season 1" "bulletproof bag" "mens sunglasses duco" ...
 $ search_frequency_rank    : num [1:90768] 4263 7123 10466 12806 13569 ...
 $ number_1_clicked_asin    : chr [1:90768] "B01FIS88SY" "B09PQSJYZM" "B07VVRZ7ZB" "B00SMRN2DU" ...
 $ number_1_product_title   : chr [1:90768] "Amazon eGift Card - Happy Birthday Balloons" "Bosch: Legacy Season 1" "Lenovo Legion 17 Armored Backpack II, Gaming Laptop Bag, Double-Layered Protection, Dedicated Storage Pockets, "| __truncated__ "Duco Mens Sports Polarized Sunglasses UV Protection Sunglasses for Men 8177s(Black Frame Gray Lens)" ...
 $ number_1_click_share     : num [1:90768] 28.6 28.6 36.4 13 15.4 ...
 $ number_1_conversion_share: num [1:90768] 100 100 100 100 100 100 100 100 100 100 ...
 $ number_2_clicked_asin    : chr [1:90768] "B01EAG3VZA" "B09PQDFV3L" "B07GGPH3CZ" "B081L8NYW1" ...
 $ number_2_product_title   : chr [1:90768] "Frito-Lay Ultimate Snack Care Package, Variety Assortment of Chips, Cookies, Crackers & More, 40 Count" "Bosch: Legacy Season 1" "ProCase Tactical Backpack Bag 40L Large 3 Day Military Army Outdoor Assault Pack Rucksacks Carry Bag Backpacks -Black" "Duco Men's Sports Polarized Driving Carbon Fiber Sunglasses for Men UV400 Protection DC8277 (Gunmetal Frame Blue Lens)" ...
 $ number_2_click_share     : num [1:90768] 8.88 25.25 10.54 11.06 13.14 ...
 $ number_2_conversion_share: num [1:90768] 0 0 0 0 0 0 0 0 0 0 ...
 $ number_3_clicked_asin    : chr [1:90768] "B09Q6DYDTD" "B09SGLBS2L" "B075SZQN9Q" "B07DNXGYC2" ...
 $ number_3_product_title   : chr [1:90768] "Clarifion - DSTx Portable Air Purifier - Plug In Air Ionizer HEPA Air Filter, Mini Personal Air Purifiers For, "| __truncated__ "The Wrong Side of Goodbye" "Tzowla Travel Laptop Backpack Water Resistant Anti-Theft Bag with USB Charging Port and Lock 15.6 Inch Computer"| __truncated__ "Duco Men's Luxury Carbon Fiber Temple Polarized Sunglasses for Men Sports UV400 DC8206" ...
 $ number_3_click_share     : num [1:90768] 6.52 16.76 4.93 10.27 11.44 ...
 $ number_3_conversion_share: num [1:90768] 0 0 0 0 0 0 0 0 0 0 ...
 $ total_click_share        : num [1:90768] 44 70.6 51.9 34.3 40 ...
 $ total_conversion_share   : num [1:90768] 100 100 100 100 100 100 100 100 100 100 ...

There are over 90000 such cases or about 9% of our data where a single product garners 100% of the sales for a search term! While this is believable in cases where customers search for an exact product, a review of the data above suggests some inaccuracy in the provided numbers. E.g., independently searching “mens sunglasses duco” (where “DUCO” is a brand) provides several well-selling mens sunglasses made by DUCO, yet the data suggests only 1 of those products has 100% of the conversion for that search term. The search is also ranked at 12806, which means it is a relatively highly searched term, which means it is more competitive and a case where this would not occur. This may reflect errant data. I will make a scatterplot to see where these types of outliers exist, i.e. where the most clicked product for a keyword controls 100% of the conversions for that keyword. Alpha is set to 0.005 because of large # of points:

ggplot(data=outlier_no_1_conversion_share) + geom_point(mapping=aes(x=search_frequency_rank, y=total_conversion_share),alpha=0.005) +
  labs(title="Conversion Share % of Top Clicked Product vs. Keyword Search Rank", 
       caption="A visual representation showing the outlier cases of one product with 100% conversion for a keyword", x="Search Frequency Rank for Keyword (Bigger # Means Lower Search Volume)",
       y="% of Click Share by Top Clicked Product") + coord_cartesian(xlim =c(0, 1000000), ylim = c(0, 100))

We see that these cases appear more for lesser searched keywords (i.e. keywords with a lower search frequency rank), but that they are also well represented in the higher ranked keywords as well. We will keep this data for now as it is hard to determine genuine cases here from non-genuine.

Let’s explore cases where the top 3 clicked products result in 0% of conversion share for a keyword:

outlier_zero_conversion_share <- search_data %>% filter(search_data$total_conversion_share == 0)
head(outlier_zero_conversion_share, 80)
str(outlier_zero_conversion_share)
tibble [175,645 × 17] (S3: tbl_df/tbl/data.frame)
 $ department               : chr [1:175645] "Amazon.com" "Amazon.com" "Amazon.com" "Amazon.com" ...
 $ search_term              : chr [1:175645] "documentaries" "kgf chapter 2" "bosch" "pime tv" ...
 $ search_frequency_rank    : num [1:175645] 140 226 237 552 605 ...
 $ number_1_clicked_asin    : chr [1:175645] "B09VFRTZLY" "B08KWQLPFF" "B089Y776DM" "B09M7RCXBB" ...
 $ number_1_product_title   : chr [1:175645] "The Catch" "K.G.F: Chapter 1 (Telugu)" "Chapter One: 'Tis the Season" "The Marvelous Mrs. Maisel - Season 4" ...
 $ number_1_click_share     : num [1:175645] 5.77 16.49 15.94 9.58 7.34 ...
 $ number_1_conversion_share: num [1:175645] 0 0 0 0 0 0 0 0 0 0 ...
 $ number_2_clicked_asin    : chr [1:175645] "B00CSCY54A" "B08KWQ5BBK" "B089XVT9D6" "B09PVRLSLB" ...
 $ number_2_product_title   : chr [1:175645] "Girl 27" "K.G.F: Chapter 1 (Hindi)" "Chapter One: 'Tis the Season" "Outer Range - Season 1" ...
 $ number_2_click_share     : num [1:175645] 3.12 11.52 9.25 6.28 5.94 ...
 $ number_2_conversion_share: num [1:175645] 0 0 0 0 0 0 0 0 0 0 ...
 $ number_3_clicked_asin    : chr [1:175645] "B09KJ9B4HL" "B08KWQ4QMH" "B09SGLBS2L" "B08X7JSCHQ" ...
 $ number_3_product_title   : chr [1:175645] "Eating Our Way To Extinction" "K.G.F: Chapter 1 (Telugu)" "The Wrong Side of Goodbye" "Insignia 32-inch Class F20 Series Smart HD 720p Fire TV (NS-32F201NA22, 2021 Model)" ...
 $ number_3_click_share     : num [1:175645] 2.39 5.74 8.79 6.11 5.24 ...
 $ number_3_conversion_share: num [1:175645] 0 0 0 0 0 0 0 0 0 0 ...
 $ total_click_share        : num [1:175645] 11.3 33.8 34 22 18.5 ...
 $ total_conversion_share   : num [1:175645] 0 0 0 0 0 0 0 0 0 0 ...

There are over 175,000 such cases. A review of some examples shows that they include very open ended search terms including “stuff” “hindi movies”, “documentaries” “moves & tv,” along with nonmeaningful search words like “them”. Many of these are instances where 0 conversion is believable, as the customer is merely doing an open search to see what is available.

Let us determine if click share and conversion share for the top 3 products varies according to search frequency rank:

Because of the extremely large number of points in our scatterplot (near 1 million), we opt for a very low alpha on each point to detect density of points.

The scatter plot below is a visual representation showing how control of click share by the top three clicked products for a keyword becomes easier to obtain for lower ranked words that are less competitive. Note that the x axis is the Search Frequency Rank for Keyword, where a larger # means lower search volume.

ggplot(data=search_data) + geom_point(mapping=aes(x=as.numeric(search_frequency_rank), y=as.numeric(total_click_share)),alpha=0.005)

What is our correlation coefficient for search frequency rank explaining the total click share of the top 3 clicked products for a search term:

cor(search_data$search_frequency_rank, search_data$total_click_share)
[1] 0.6009246

There appears to be a notable correlation, as visible in the scatterplot above as well.

Now let’s explore how the conversion share of the top 3 clicked products for a search term varies with the search frequency rank of a search keyword. The scatter plot below is a visual representation showing how control of conversion share by the top three clicked products for a keyword similarly becomes easier as the search rank of a search term goes down (i.e. it’s less competitive). Note that the x axis is the Search Frequency Rank for Keyword, where a larger # means lower search volume.

ggplot(data=search_data) + geom_point(mapping=aes(x=as.numeric(search_frequency_rank), y=as.numeric(total_conversion_share)),alpha=0.005)
Warning: Removed 2709 rows containing missing values (geom_point).

What is our correlation coefficient for search frequency rank explaining the total conversion share of the top 3 clicked products for a search term?

Note that we have some na values that need to be cleaned out first.

no_na_total_conversion_share <- search_data %>% drop_na(total_conversion_share)
cor(no_na_total_conversion_share$search_frequency_rank, no_na_total_conversion_share$total_conversion_share)
[1] 0.1430613

While the correlation coefficient is not very meaningful here, we do see a trajectory in our plotted data indicating that as a search term becomes less competitive (it has a lower search frequency rank), the top 3 products are more able to garner a larger percentage of the conversion share, though this is not as strong as we saw for click share.

Note that in the scatter plot we see the existence of ‘lines’ of points at specific % conversion intervals (0%, 33%, 50%, 66%, 75%, 100%), which makes sense given our data. The data is for 1, 2, or 3 products, and if 1,2, or 3 garner a certain % of the conversion share, we can see these lines appearing at these given intervals.

LS0tCnRpdGxlOiAiVXNpbmcgQW1hem9uIFNlYXJjaCBEYXRhIHRvIEV4cGxvcmUgQ3VzdG9tZXIgQmVoYXZpb3IgKEEgUHJvamVjdCBpbiBSKSIKYXV0aG9yOiAiRiBBYmR1bGxhaCIKZGF0ZTogJzIwMjItMDYtMDYnCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIEJhY2tncm91bmQKQW1hem9uIHNlbGxlcnMgdGhhdCBhcmUgcGFydCBvZiB0aGUgY29tcGFueSdzIEJyYW5kIFJlZ2lzdHJ5IHByb2dyYW0gaGF2ZSBhY2Nlc3MgdG8gQW1hem9uJ3MgQnJhbmQgQW5hbHl0aWNzLCBhIGZlYXR1cmUgd2hpY2ggcHJvdmlkZXMgdmFsdWFibGUgZGF0YSB0byBBbWF6b24gc2VsbGVycyBvbiB0aGUgbGFyZ2VyIEFtYXpvbiBwcm9kdWN0IGNhdGFsb2csIGRlbW9ncmFwaGljIGRhdGEgb24gYSBzZWxsZXIncyBjdXN0b21lcnMsIGFuZCBvdGhlciB1c2VmdWwgaW5mb3JtYXRpb24uIE9uZSBvZiB0aGUgcmVwb3J0cyBwcm92aWRlZCBieSBCcmFuZCBBbmFseXRpY3MgaXMgdGhlIEFtYXpvbiBTZWFyY2ggVGVybXMgcmVwb3J0LCB3aGljaCBpbmNsdWRlcyBhIGxpc3Qgb2YgdGhlIG1vc3QgcG9wdWxhciBzZWFyY2ggdGVybXMgb24gQW1hem9uIGZvciBhIHNlbGVjdGVkIHRpbWUgZnJhbWUsIHJhbmtlZC4gSW4gYWRkaXRpb24gdG8gcHJvdmlkaW5nIHRoZSBzZWFyY2ggZnJlcXVlbmN5IHJhbmssIHRoZSByZXBvcnQgYWxzbyBwcm92aWRlcyBkYXRhIG9uIHRoZSB0b3AgdGhyZWUgY2xpY2tlZCBwcm9kdWN0cyBmb3IgZWFjaCBzZWFyY2ggdGVybSwgYW5kIHRoZSBzaGFyZSB0aGF0IHRob3NlIHByb2R1Y3RzIGhhdmUgb2YgYWxsIHRoZSBjbGlja3MgYW5kIHNhbGVzIG1hZGUgd2hlbiBhIGN1c3RvbWVyIHNlYXJjaGVzIHRoYXQgdGVybS4gV2hpbGUgdGhpcyBpcyB1c2VmdWwgZm9yIGEgc2VsbGVyIHRyYWNraW5nIHRoZSBwb3NpdGlvbiBvZiB0aGVpciBvd24gcHJvZHVjdHMgb24gdGhlIEFtYXpvbiBwbGF0Zm9ybSwgaXQgY2FuIGFsc28gYmUgdXNlZCB0byBvYnNlcnZlIHRyZW5kcyBjb25jZXJuaW5nIGJyb2FkZXIgY3VzdG9tZXIgaW50ZXJlc3RzIGFuZCB0aGVpciBpbnRlcmFjdGlvbiB3aXRoIHRoZSBBbWF6b24gcHJvZHVjdCBjYXRhbG9nLiBUaGUgZnVsbCByZXBvcnQsIHdoZW4gZG93bmxvYWRlZCwgaW5jbHVkZXMgcmVjb3JkcyBvbiB0aGUgdG9wIDEsMDAwLDAwMCBzZWFyY2ggdGVybXMgZm9yIGEgc2VsZWN0IHRpbWUgcGVyaW9kLiBUaGUgc2hvcnQgcHJvamVjdCBiZWxvdywgZG9uZSBpbiBSLCBleHBsb3JlcyB0aGlzIGRhdGEgZm9yIHRoZSB3ZWVrLWxvbmcgcGVyaW9kIG9mIDUvMjIvMjIgLSA1LzI4LzIyIGFuZCBwcm92aWRlcyB2aXN1YWxpemF0aW9uIG9mIHNvbWUgb2YgdGhlIHRyZW5kcyB0aGF0IGNhbiBiZSBhc2NlcnRhaW5lZCBmcm9tIHRoaXMgbGFyZ2UgZGF0YXNldC4KCkZvciBhIGRlc2NyaXB0aW9uIG9mIHRoZSBBbWF6b24gcmVwb3J0IGluIHF1ZXN0aW9uLCByZWZlciBbaGVyZV0oaHR0cHM6Ly9zZWxsZXJjZW50cmFsLmFtYXpvbi5jb20vZ3AvaGVscC9leHRlcm5hbC9oZWxwLmh0bWw/aXRlbUlEPTVOWFdOWThIVUQzVkRDVykuCgoKIyMjIyBMb2FkaW5nIGxpYnJhcmllczoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShnZ3dvcmRjbG91ZCkKbGlicmFyeSh3b3JkY2xvdWQpCmxpYnJhcnkodG0pCmxpYnJhcnkoTkxQKQpgYGAKCgojIyMjIHJlYWRpbmcgQ1NWIGZpbGU6CmBgYHtyfQpzZWFyY2hfZGF0YSA8LSByZWFkX2NzdigiL1VzZXJzL1lvdXJOYW1lL0Rlc2t0b3AvRmlsZS5jc3YiLCBza2lwPTEpCgpzdHIoc2VhcmNoX2RhdGEpCgpzZWFyY2hfZGF0YSA8LSBjbGVhbl9uYW1lcyhzZWFyY2hfZGF0YSkKYGBgCgojIyMjIFByZXZpZXcgdGhlIGRhdGFzZXQ6CmBgYHtyfQpoZWFkKHNlYXJjaF9kYXRhLDEwKQpgYGAKCiMjIyMgTGV0J3MgcmVtb3ZlIHRoZSAlIHN5bWJvbCBhbmQgbWFrZSB0aGUgdmFsdWVzIG51bWVyaWMgZm9yIGFsbCBjb2x1bW5zIHdoZXJlIGEgJSBzeW1ib2wgYXBwZWFyczoKYGBge3Igd2FybmluZz1GQUxTRX0Kc2VhcmNoX2RhdGEkbnVtYmVyXzFfY2xpY2tfc2hhcmUgPC0gYXMubnVtZXJpYyhzdWIoIiUiLCAiIiwgc2VhcmNoX2RhdGEkbnVtYmVyXzFfY2xpY2tfc2hhcmUpKQoKc2VhcmNoX2RhdGEkbnVtYmVyXzJfY2xpY2tfc2hhcmUgPC0gYXMubnVtZXJpYyhzdWIoIiUiLCAiIiwgc2VhcmNoX2RhdGEkbnVtYmVyXzJfY2xpY2tfc2hhcmUpKQoKc2VhcmNoX2RhdGEkbnVtYmVyXzNfY2xpY2tfc2hhcmUgPC0gYXMubnVtZXJpYyhzdWIoIiUiLCAiIiwgc2VhcmNoX2RhdGEkbnVtYmVyXzNfY2xpY2tfc2hhcmUpKQoKc2VhcmNoX2RhdGEkbnVtYmVyXzFfY29udmVyc2lvbl9zaGFyZSA8LSBhcy5udW1lcmljKHN1YigiJSIsICIiLCBzZWFyY2hfZGF0YSRudW1iZXJfMV9jb252ZXJzaW9uX3NoYXJlKSkKCnNlYXJjaF9kYXRhJG51bWJlcl8yX2NvbnZlcnNpb25fc2hhcmUgPC0gYXMubnVtZXJpYyhzdWIoIiUiLCAiIiwgc2VhcmNoX2RhdGEkbnVtYmVyXzJfY29udmVyc2lvbl9zaGFyZSkpCgpzZWFyY2hfZGF0YSRudW1iZXJfM19jb252ZXJzaW9uX3NoYXJlIDwtIGFzLm51bWVyaWMoc3ViKCIlIiwgIiIsIHNlYXJjaF9kYXRhJG51bWJlcl8zX2NvbnZlcnNpb25fc2hhcmUpKQpgYGAKCgojIyMjIExldCdzIGNyZWF0ZSBhIHdvcmRjbG91ZCB0byBzZWUgd2hhdCBwZW9wbGUgYXJlIHNlYXJjaGluZyB0aGUgbW9zdCBmb3IgaW4gdGhpcyB0aW1lIHBlcmlvZC4gVG8gbWFrZSB0aGlzIG1vcmUgbWFuYWdlYWJsZSwgbGV0cyB3b3JrIHdpdGggdGhlIHRvcCAzMCwwMDAgc2VhcmNoZXMsIGFuZCB3b3JkcyBtZW50aW9uZWQgbW9yZSB0aGFuIDUwIHRpbWVzLgpgYGB7ciB3YXJuaW5nPUZBTFNFfQp0b3BfMzBrIDwtIHNlYXJjaF9kYXRhICU+JSBmaWx0ZXIoc2VhcmNoX2ZyZXF1ZW5jeV9yYW5rPD0zMDAwMCkKCndvcmRjbG91ZCh0b3BfMzBrJHNlYXJjaF90ZXJtLCBtaW4uZnJlcT01MCkKYGBgClByb2R1Y3RzIHdpdGggYSBzZWFyY2ggcmFuayBvZiBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gMzAsMDAwIHJlcHJlc2VudCBzb21lIG9mIHRoZSBtb3N0IHNlYXJjaGVkIChhbmQgcHVyY2hhc2VkKSBBbWF6b24gcHJvZHVjdHMsIGFuZCBvdXIgbGlzdCBhYm92ZSBvbmx5IGluY2x1ZGVzIHRob3NlIHNlYXJjaCB0ZXJtcyB0aGF0IHdlcmUgcmVwZWF0ZWQgbW9yZSB0aGFuIDUwIHRpbWVzIGluIHRoZXNlIHR3b3AgMzAsMDAwIGVudHJpZXMuIFRoZSB3b3JkY2xvdWQgYWJvdmUgdGhlcmVmb3JlIGdpdmVzIHVzIGEgcXVpY2sgZ2xpbXBzZSBpbnRvIEFtYXpvbiBjdXN0b21lcnMnIHRvcCBzZWFyY2ggYmVoYXZpb3IgZHVyaW5nIHRoZSB0aW1lIHBlcmlvZCBsb29rZWQgYXQgYXQgdGhlIGVuZCBvZiBNYXkuICBHaXZlbiB3ZWxsLWtub3duIHNhbGVzIHRyZW5kcywgd2UgdW5zdXJwaXNpbmdseSBzZWUgIndvbWVuIiBhcyBvdXIgdG9wIHNlYXJjaGVkIHRlcm0uIFdlIHNlZSBvdGhlciBzYWxlcyBkZW1vZ3JhcGhpY3Mgbm90ZWQgYXMga2V5IHNlYXJjaCB0ZXJtcyBhcyB3ZWxsLCBpbmNsdWRpbmcgbWVuLCBiYWJ5LCBhbmQgZG9nIChpLmUuIHBldHMpLiBHaXZlbiB0aGUgc3VtbWVyIHRpbWUgZnJhbWUgbG9va2VkIGF0IGluIHRoaXMgc2VhcmNoIHRlcm1zIHJlcG9ydCwgaXQgaXMgdW5zdXJwcmlzaW5nIHRvIHNlZSB0ZXJtcyBzdWNoIGFzICJvdXRkb29yLCIgInN1bmdsYXNzZXMsIiAic3VtbWVyLCIgInN1bnNjcmVlbiIgYW5kICJzd2ltbWluZyIgYXBwZWFyIGluIG91ciB3b3JkY2xvdWQuIAoKT3VyIGRhdGEgY3VycmVudGx5IGdpdmVzIHRvdGFsIGNsaWNrIHNoYXJlIGFuZCBjb252ZXJzaW9uIHNoYXJlIHJlbGF0aXZlIHRvIGFsbCB0aG9zZSBwcm9kdWN0cyBjbGlja2VkIGZvciBhIGNlcnRhaW4gc2VhcmNoIHRlcm0gYnkgYSBjdXN0b21lciwgYW5kIGdpdmVzIHRoaXMgc2VwYXJhdGVseSBmb3IgZWFjaCBvZiB0aGUgdG9wIDMgY2xpY2tlZCBwcm9kdWN0cy4gTGV0J3MgY3JlYXRlIHR3byBjb2x1bW5zIHRvIGNhcHR1cmUgdGhlIHN1bW1lZCBudW1iZXJzIGZyb20gYWxsIG9mIHRoZXNlIHRocmVlIHByb2R1Y3RzOgoKIyMjIyBDcmVhdGUgdG90YWxfY2xpY2tfc2hhcmUgY29sdW1uIGFuZCB0b3RhbF9jb252ZXJzaW9uX3NoYXJlIGNvbHVtbjoKYGBge3J9CnNlYXJjaF9kYXRhIDwtIG11dGF0ZShzZWFyY2hfZGF0YSwgdG90YWxfY2xpY2tfc2hhcmUgPSBudW1iZXJfMV9jbGlja19zaGFyZSArIG51bWJlcl8yX2NsaWNrX3NoYXJlICsgbnVtYmVyXzNfY2xpY2tfc2hhcmUpCgpzZWFyY2hfZGF0YSA8LSBtdXRhdGUoc2VhcmNoX2RhdGEsIHRvdGFsX2NvbnZlcnNpb25fc2hhcmUgPSBudW1iZXJfMV9jb252ZXJzaW9uX3NoYXJlICsgbnVtYmVyXzJfY29udmVyc2lvbl9zaGFyZSArIG51bWJlcl8zX2NvbnZlcnNpb25fc2hhcmUpCmBgYAoKIyMjIyBTZWUgdGhlIHJpZ2h0IG1vc3QgY29sdW1ucyBmb3IgdGhlc2UgYWRkZWQgY29sdW1uczoKYGBge3J9CmhlYWQoc2VhcmNoX2RhdGEsMTApCmBgYAoKIyMjIyBFeHBsb3JlIGlmIHRoZXJlIGlzIGFueSBtaXNzaW5nIGRhdGEgaW4gb3VyIG5ldyB0b3RhbF9jbGlja19zaGFyZSBjb2x1bW46CmBgYHtyfQp3aGljaChpcy5uYShzZWFyY2hfZGF0YSR0b3RhbF9jbGlja19zaGFyZSkpCmBgYAoKVGhlcmUgaXMgbm8gbWlzc2luZyBkYXRhIGluIHRoaXMgY29sdW1uLgoKIyMjIyBFeHBsb3JlIGlmIHRoZXJlIGlzIGFueSBtaXNzaW5nIGRhdGEgaW4gb3VyIG5ldyB0b3RhbF9jb252ZXJzaW9uX3NoYXJlIGNvbHVtbjoKYGBge3J9CnN0cih3aGljaChpcy5uYShzZWFyY2hfZGF0YSR0b3RhbF9jb252ZXJzaW9uX3NoYXJlKSkpCmBgYAoKVGhlcmUgYXJlIGNsb3NlIHRvIH4zMDAwIG1pc3NpbmcgdmFsdWVzLgoKCiMjIyMgTG9va3MgbGlrZSB0b3RhbF9jb252ZXJzaW9uX3NoYXJlIGhhcyBtaXNzaW5nIHZhbHVlcy4gTGV0J3MgZXhwbG9yZSBmdXJ0aGVyIHdoYXQgdGhlIHJlYXNvbiBpczoKYGBge3J9Cm5hX3RvdGFsX2NvbnZlcnNpb25fc2hhcmUgPC0gc2VhcmNoX2RhdGEgJT4lIGZpbHRlcihpcy5uYShzZWFyY2hfZGF0YSR0b3RhbF9jb252ZXJzaW9uX3NoYXJlKSkKCmhlYWQobmFfdG90YWxfY29udmVyc2lvbl9zaGFyZSw1MCkKYGBgCgpVcG9uIGV4cGxvcmluZyB0aGUgZXhhbXBsZXMgb2YgdGhlIE5BIHZhbHVlcyBhYm92ZSwgdGhleSBhcHBlYXIgdG8gYmUgZXhhbXBsZXMgd2hlcmUgYSBzaW5nbGUgcHJvZHVjdCBnYXJuZXJzIDEwMCUgb2YgdGhlIGNsaWNrc2hhcmUgZm9yIGEgc2VhcmNoIHRlcm0sIGFuZCB6ZXJvIHNhbGVzIGFyZSBtYWRlLiBUaGlzIGlzIGJlbGlldmFibGUgd2hlbiB3ZSBjb25zaWRlciB0aGF0IHByYWN0aWNhbGx5IGFsbCBvZiB0aGUgaXRlbXMgaW4gdGhpcyBsaXN0IGhhdmUgYSBwb29yIHNlYXJjaCBmcmVxdWVuY3kgcmFuay4gTm90aGluZyBpbiB0aGUgdG9wIDMwLDAwMCBhcHBlYXJzLiBJbiBtYW55IGJ1dCBub3QgYWxsIHRoZSBjYXNlcywgdGhlIHRlcm0gdGhhdCBpcyBzZWFyY2hlZCBpcyBhbHNvIG5vdCBvbmUgdGhhdCBpcyBkb25lIGJ5IGEgbm9ybWFsIHVzZXIuIFRoZXkgYXJlIHNlYXJjaGVzIGZvciBzcGVjaWZpYyBBbWF6b24gaWRlbnRpZmljYXRpb24gbnVtYmVycywgYWxzbyBrbm93biBhcyB0aGUgQW1hem9uICJBU0lOIiwgaW5kaWNhdGluZyB0aGF0IHRoZSBjdXN0b21lciB3YXMgb25seSBsb29raW5nIGZvciAxIHBvc3NpYmxlIHNlYXJjaCByZXN1bHQgKGUuZy4sIGEgc3BlY2lmaWMgbWFrZS9icmFuZCBmb3IgYSBwcm9kdWN0KS4gVGhpcyBtYXkgYWxzbyByZWZsZWN0IHRoZSBzZWFyY2ggb2YgYSBzb2Z0d2FyZSBnYXRoZXJpbmcgZGF0YSBvbiBBbWF6b24gcHJvZHVjdHMsIG9yIGFuIGF0dGVtcHQgYXQgYXJ0aWZpY2lhbGx5IGJvb3N0aW5nIGEgcHJvZHVjdCBzZWFyY2ggcmFuayBieSBhIHNlbGxlci4KCkluIHNvbWUgb2YgdGhlIGNhc2VzIG9mIGEgcmVhbCBzZWFyY2ggdGVybSwgZS5nLiwgInNpbGsgcGlsbG93Y2FzZSBxdWVlbiBzaXplIiwgaXQgaXMgcG9zc2libGUgdGhhdCB0aGVyZSB3YXMgYSBmbGF3IGluIHRoZSBkYXRhIGNyZWF0aW9uLCBhcyB0aGVzZSBzZWFyY2ggdGVybXMsIHdoZW4gaW5kZXBlbmRlbnRseSBzZWFyY2hlZCBvbiBBbWF6b24sIGdpdmUgc2V2ZXJhbCByZWxldmFudCByZXN1bHRzIHdoZXJlIG9uZSB3b3VsZCBleHBlY3QgbW9yZSB0aGFuIG9uZSBwcm9kdWN0IHdvdWxkIGJlIGNsaWNrZWQuIFRoYW5rZnVsbHkgdGhlIG51bWJlciBvZiB0aGVzZSBleGFtcGxlcyBpcyBmZXcgcmVsYXRpdmUgdG8gdGhlIGRhdGFzZXQgb2YgMSwwMDAsMDAwIHNlcmFjaCB0ZXJtcywgYW5kIHRoZXkgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IHJlbW92ZWQgZnJvbSBncmFwaCB3ZSB3aWxsIHBsb3QgYmVsb3cgYmVjYXVzZSB0aGV5IGFyZSBudWxsLgoKTGV0J3MgZXhwbG9yZSBzb21lIG9mIHRoZXNlIG91dGxpZXIgY2FzZXMgd2hlcmUgYSBzaW5nbGUgcHJvZHVjdCBnYXJuZXJlZCAxMDAlIG9mIHRoZSBjbGlja3NoYXJlIGZvciBhIHBhcnRpY3VsYXIgc2VhcmNoIHRlcm0gaW4gdGhlIGJyb2FkZXIgZGF0YXNldDoKCmBgYHtyfQpvdXRsaWVyX25vXzFfY2xpY2tfc2hhcmUgPC0gc2VhcmNoX2RhdGEgJT4lIGZpbHRlcihzZWFyY2hfZGF0YSRudW1iZXJfMV9jbGlja19zaGFyZSA9PSAxMDApCmhlYWQob3V0bGllcl9ub18xX2NsaWNrX3NoYXJlLDgwKQpzdHIob3V0bGllcl9ub18xX2NsaWNrX3NoYXJlKQpgYGAKClRoZXJlIGFyZSBvbmx5IH4gOTAwMCBvZiBzdWNoIGNhc2VzICh+MC45JSBvZiB0aGUgZGF0YSkgaW4gdGhlIGxhcmdlciBkYXRhIHNldCwgYW5kIHRoZXkgc2VlbSB0byBmaXQgdGhlIHR5cGVzIG9mIHNlYXJjaGVzIHdlIGp1c3QgZGVzY3JpYmVkOiBlaXRoZXIgdGhleSBhcmUgcG9vciByYW5rZWQgdGVybXMgd2hlcmUgcHVyY2hhc2VzIGFyZSBpbmZyZXF1ZW50IGFuZCB3ZSBjb3VsZCBleHBlY3QgYSBzaW5nbGUgcHJvZHVjdCBnYXRoZXJpbmcgdGhlIGVudGlyZXR5IG9mIGEgdGVybSdzIGNsaWNrc2hhcmUsIG9yIHRoZSBzZWFyY2ggdGVybXMgYXBwZWFyIHRvIGJlIHVubmF0dXJhbCBhdHRlbXB0cyBhdCB6ZXJvaW5nIGluIG9uIGEgc2luZ2xlIHByb2R1Y3QgdGhyb3VnaCBhbiBBU0lOLgoKV2UgY2FuIHZpc3VhbGx5IHNlZSB3aGVyZSB0aGUgcmFua2luZyBjb25jZW50cmF0aW9uIG9mIHRoZXNlIGNhc2VzIHdoZXJlIG9ubHkgYSBzaW5nbGUgcHJvZHVjdCBjb250YWlucyAxMDAlIG9mIHRoZSBjbGljayBzaGFyZSBpbiB0aGUgc2NhdHRlciBwbG90IGJlbG93LiBJdCBpcyBjbGVhciB0aGF0IGV4YW1wbGVzIG9mIHRoZXNlIGNhc2VzIGFyZSBwcmltYXJpbHkgZm91bmQgd2hlcmUgc2VhcmNoIHRlcm1zIGFyZSByYW5rZWQgbG93ZXIgd2hlcmUgdGhpcyB0eXBlIG9mIGNhc2Ugd291bGQgbWFrZXMgc2Vuc2UuCgpIZXJlIEkgY3JlYXRlIGEgc2NhdHRlciBwbG90IHNob3dpbmcgdGhlIGNvbmNlbnRyYXRpb24gb2YgdGhlc2Ugb3V0bGllciBjYXNlcyBpbiBkaWZmZXJlbnQga2V5d29yZCBzZWFyY2ggcmFua2luZ3MuIEkgd2lsbCBzZXQgYWxwaGEgZm9yIG91ciBwb2ludHMgdG8gMC4wMSBiZWNhdXNlIHdlIGFyZSBkZWFsaW5nIHdpdGggc2V2ZXJhbCB0aG91c2FuZCBkYXRhIHBvaW50czoKCmBgYHtyfQpnZ3Bsb3QoZGF0YT1vdXRsaWVyX25vXzFfY2xpY2tfc2hhcmUpICsgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PXNlYXJjaF9mcmVxdWVuY3lfcmFuaywgeT10b3RhbF9jbGlja19zaGFyZSksYWxwaGE9MC4wNSkgKwogIGxhYnModGl0bGU9IkNsaWNrIFNoYXJlICUgb2YgVG9wIENsaWNrZWQgUHJvZHVjdCB2cy4gS2V5d29yZCBTZWFyY2ggUmFuayIsIAogICAgICAgY2FwdGlvbj0iQSB2aXN1YWwgcmVwcmVzZW50YXRpb24gc2hvd2luZyB0aGUgb3V0bGllciBjYXNlcyBvZiBvbmUgcHJvZHVjdCB3aXRoIDEwMCUgY2xpY2tzaGFyZSBmb3IgYSBrZXl3b3JkIiwgeD0iU2VhcmNoIEZyZXF1ZW5jeSBSYW5rIGZvciBLZXl3b3JkIChCaWdnZXIgIyBNZWFucyBMb3dlciBTZWFyY2ggVm9sdW1lKSIsCiAgICAgICB5PSIlIG9mIENsaWNrIFNoYXJlIGJ5IFRvcCBDbGlja2VkIFByb2R1Y3QiKSArIGNvb3JkX2NhcnRlc2lhbih4bGltID1jKDAsIDEwMDAwMDApLCB5bGltID0gYygwLCAxMDApKQpgYGAKCgpXZSBzZWUgaG93IHRoZXNlIGNhc2VzIGFyZSBpbmZyZXF1ZW50IHdoZXJlIHNlYXJjaCBmcmVxdWVuY3kgcmFuayBpcyBncmVhdGVyIHRoYW4gMjUwLDAwMC4gVGhpcyBtYWtlcyBzZW5zZSB3aXRoIGV4cGVjdGVkIGN1c3RvbWVyIGJlaGF2aW9yLiBXZSBleHBlY3QgdGhhdCBhIHNlYXJjaCB0ZXJtIHRoYXQgaXMgbW9yZSBmcmVxdWVudGx5IHNlYWNoZWQgYnkgY3VzdG9tZXJzIHdpbGwgaGF2ZSBtb3JlIHJlbGV2YW50IHByb2R1Y3Qgb2ZmZXJpbmdzLCBhbmQgdGhlcmVmb3JlIGEgZ3JlYXRlciBsaWtlbGlob29kIHRoYXQgbW9yZSB0aGFuIG9uZSBwcm9kdWN0IHdpbGwgYmUgY2xpY2tlZCBieSBhIGN1c3RvbWVyLgoKIyMjIyBXaGF0IGFib3V0IGNhc2VzIHdoZXJlIHRoZSB0b3AgY2xpY2tlZCBwcm9kdWN0IGNvbnRyb2xzIDEwMCUgb2YgY29udmVyc2lvbi9zYWxlcyBmb3IgdGhhdCBzZWFyY2ggdGVybT8KYGBge3J9Cm91dGxpZXJfbm9fMV9jb252ZXJzaW9uX3NoYXJlIDwtIHNlYXJjaF9kYXRhICU+JSBmaWx0ZXIoc2VhcmNoX2RhdGEkbnVtYmVyXzFfY29udmVyc2lvbl9zaGFyZSA9PSAxMDApCmhlYWQob3V0bGllcl9ub18xX2NvbnZlcnNpb25fc2hhcmUsIDgwKQpzdHIob3V0bGllcl9ub18xX2NvbnZlcnNpb25fc2hhcmUpCmBgYAoKVGhlcmUgYXJlIG92ZXIgOTAwMDAgc3VjaCBjYXNlcyBvciBhYm91dCA5JSBvZiBvdXIgZGF0YSB3aGVyZSBhIHNpbmdsZSBwcm9kdWN0IGdhcm5lcnMgMTAwJSBvZiB0aGUgc2FsZXMgZm9yIGEgc2VhcmNoIHRlcm0hIFdoaWxlIHRoaXMgaXMgYmVsaWV2YWJsZSBpbiBjYXNlcyB3aGVyZSBjdXN0b21lcnMgc2VhcmNoIGZvciBhbiBleGFjdCBwcm9kdWN0LCBhIHJldmlldyBvZiB0aGUgZGF0YSBhYm92ZSBzdWdnZXN0cyBzb21lIGluYWNjdXJhY3kgaW4gdGhlIHByb3ZpZGVkIG51bWJlcnMuIEUuZy4sIGluZGVwZW5kZW50bHkgc2VhcmNoaW5nICJtZW5zIHN1bmdsYXNzZXMgZHVjbyIgKHdoZXJlICJEVUNPIiBpcyBhIGJyYW5kKSBwcm92aWRlcyBzZXZlcmFsIHdlbGwtc2VsbGluZyBtZW5zIHN1bmdsYXNzZXMgbWFkZSBieSBEVUNPLCB5ZXQgdGhlIGRhdGEgc3VnZ2VzdHMgb25seSAxIG9mIHRob3NlIHByb2R1Y3RzIGhhcyAxMDAlIG9mIHRoZSBjb252ZXJzaW9uIGZvciB0aGF0IHNlYXJjaCB0ZXJtLiBUaGUgc2VhcmNoIGlzIGFsc28gcmFua2VkIGF0IDEyODA2LCB3aGljaCBtZWFucyBpdCBpcyBhIHJlbGF0aXZlbHkgaGlnaGx5IHNlYXJjaGVkIHRlcm0sIHdoaWNoIG1lYW5zIGl0IGlzIG1vcmUgY29tcGV0aXRpdmUgYW5kIGEgY2FzZSB3aGVyZSB0aGlzIHdvdWxkIG5vdCBvY2N1ci4gVGhpcyBtYXkgcmVmbGVjdCBlcnJhbnQgZGF0YS4gSSB3aWxsIG1ha2UgYSBzY2F0dGVycGxvdCB0byBzZWUgd2hlcmUgdGhlc2UgdHlwZXMgb2Ygb3V0bGllcnMgZXhpc3QsIGkuZS4gd2hlcmUgdGhlIG1vc3QgY2xpY2tlZCBwcm9kdWN0IGZvciBhIGtleXdvcmQgY29udHJvbHMgMTAwJSBvZiB0aGUgY29udmVyc2lvbnMgZm9yIHRoYXQga2V5d29yZC4gQWxwaGEgaXMgc2V0IHRvIDAuMDA1IGJlY2F1c2Ugb2YgbGFyZ2UgIyBvZiBwb2ludHM6CgpgYGB7cn0KZ2dwbG90KGRhdGE9b3V0bGllcl9ub18xX2NvbnZlcnNpb25fc2hhcmUpICsgZ2VvbV9wb2ludChtYXBwaW5nPWFlcyh4PXNlYXJjaF9mcmVxdWVuY3lfcmFuaywgeT10b3RhbF9jb252ZXJzaW9uX3NoYXJlKSxhbHBoYT0wLjAwNSkgKwogIGxhYnModGl0bGU9IkNvbnZlcnNpb24gU2hhcmUgJSBvZiBUb3AgQ2xpY2tlZCBQcm9kdWN0IHZzLiBLZXl3b3JkIFNlYXJjaCBSYW5rIiwgCiAgICAgICBjYXB0aW9uPSJBIHZpc3VhbCByZXByZXNlbnRhdGlvbiBzaG93aW5nIHRoZSBvdXRsaWVyIGNhc2VzIG9mIG9uZSBwcm9kdWN0IHdpdGggMTAwJSBjb252ZXJzaW9uIGZvciBhIGtleXdvcmQiLCB4PSJTZWFyY2ggRnJlcXVlbmN5IFJhbmsgZm9yIEtleXdvcmQgKEJpZ2dlciAjIE1lYW5zIExvd2VyIFNlYXJjaCBWb2x1bWUpIiwKICAgICAgIHk9IiUgb2YgQ2xpY2sgU2hhcmUgYnkgVG9wIENsaWNrZWQgUHJvZHVjdCIpICsgY29vcmRfY2FydGVzaWFuKHhsaW0gPWMoMCwgMTAwMDAwMCksIHlsaW0gPSBjKDAsIDEwMCkpCmBgYAoKCldlIHNlZSB0aGF0IHRoZXNlIGNhc2VzIGFwcGVhciBtb3JlIGZvciBsZXNzZXIgc2VhcmNoZWQga2V5d29yZHMgKGkuZS4ga2V5d29yZHMgd2l0aCBhIGxvd2VyIHNlYXJjaCBmcmVxdWVuY3kgcmFuayksIGJ1dCB0aGF0IHRoZXkgYXJlIGFsc28gd2VsbCByZXByZXNlbnRlZCBpbiB0aGUgaGlnaGVyIHJhbmtlZCBrZXl3b3JkcyBhcyB3ZWxsLiBXZSB3aWxsIGtlZXAgdGhpcyBkYXRhIGZvciBub3cgYXMgaXQgaXMgaGFyZCB0byBkZXRlcm1pbmUgZ2VudWluZSBjYXNlcyBoZXJlIGZyb20gbm9uLWdlbnVpbmUuCgojIyMjIExldCdzIGV4cGxvcmUgY2FzZXMgd2hlcmUgdGhlIHRvcCAzIGNsaWNrZWQgcHJvZHVjdHMgcmVzdWx0IGluIDAlIG9mIGNvbnZlcnNpb24gc2hhcmUgZm9yIGEga2V5d29yZDoKYGBge3J9Cm91dGxpZXJfemVyb19jb252ZXJzaW9uX3NoYXJlIDwtIHNlYXJjaF9kYXRhICU+JSBmaWx0ZXIoc2VhcmNoX2RhdGEkdG90YWxfY29udmVyc2lvbl9zaGFyZSA9PSAwKQpoZWFkKG91dGxpZXJfemVyb19jb252ZXJzaW9uX3NoYXJlLCA4MCkKc3RyKG91dGxpZXJfemVyb19jb252ZXJzaW9uX3NoYXJlKQpgYGAKClRoZXJlIGFyZSBvdmVyIDE3NSwwMDAgc3VjaCBjYXNlcy4gQSByZXZpZXcgb2Ygc29tZSBleGFtcGxlcyBzaG93cyB0aGF0IHRoZXkgaW5jbHVkZSB2ZXJ5IG9wZW4gZW5kZWQgc2VhcmNoIHRlcm1zIGluY2x1ZGluZyAic3R1ZmYiICJoaW5kaSBtb3ZpZXMiLCAiZG9jdW1lbnRhcmllcyIgIm1vdmVzICYgdHYsIiBhbG9uZyB3aXRoIG5vbm1lYW5pbmdmdWwgc2VhcmNoIHdvcmRzIGxpa2UgInRoZW0iLiBNYW55IG9mIHRoZXNlIGFyZSBpbnN0YW5jZXMgd2hlcmUgMCBjb252ZXJzaW9uIGlzIGJlbGlldmFibGUsIGFzIHRoZSBjdXN0b21lciBpcyBtZXJlbHkgZG9pbmcgYW4gb3BlbiBzZWFyY2ggdG8gc2VlIHdoYXQgaXMgYXZhaWxhYmxlLgoKIyMjIyBMZXQgdXMgZGV0ZXJtaW5lIGlmIGNsaWNrIHNoYXJlIGFuZCBjb252ZXJzaW9uIHNoYXJlIGZvciB0aGUgdG9wIDMgcHJvZHVjdHMgdmFyaWVzIGFjY29yZGluZyB0byBzZWFyY2ggZnJlcXVlbmN5IHJhbms6CgpCZWNhdXNlIG9mIHRoZSBleHRyZW1lbHkgbGFyZ2UgbnVtYmVyIG9mIHBvaW50cyBpbiBvdXIgc2NhdHRlcnBsb3QgKG5lYXIgMSBtaWxsaW9uKSwgd2Ugb3B0IGZvciBhIHZlcnkgbG93IGFscGhhIG9uIGVhY2ggcG9pbnQgdG8gZGV0ZWN0IGRlbnNpdHkgb2YgcG9pbnRzLgoKVGhlIHNjYXR0ZXIgcGxvdCBiZWxvdyBpcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBzaG93aW5nIGhvdyBjb250cm9sIG9mIGNsaWNrIHNoYXJlIGJ5IHRoZSB0b3AgdGhyZWUgY2xpY2tlZCBwcm9kdWN0cyBmb3IgYSBrZXl3b3JkIGJlY29tZXMgZWFzaWVyIHRvIG9idGFpbiBmb3IgbG93ZXIgcmFua2VkIHdvcmRzIHRoYXQgYXJlIGxlc3MgY29tcGV0aXRpdmUuIE5vdGUgdGhhdCB0aGUgeCBheGlzIGlzIHRoZSBTZWFyY2ggRnJlcXVlbmN5IFJhbmsgZm9yIEtleXdvcmQsIHdoZXJlIGEgbGFyZ2VyICMgbWVhbnMgbG93ZXIgc2VhcmNoIHZvbHVtZS4KCmBgYHtyfQpnZ3Bsb3QoZGF0YT1zZWFyY2hfZGF0YSkgKyBnZW9tX3BvaW50KG1hcHBpbmc9YWVzKHg9YXMubnVtZXJpYyhzZWFyY2hfZnJlcXVlbmN5X3JhbmspLCB5PWFzLm51bWVyaWModG90YWxfY2xpY2tfc2hhcmUpKSxhbHBoYT0wLjAwNSkKYGBgCgojIyMjIFdoYXQgaXMgb3VyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IGZvciBzZWFyY2ggZnJlcXVlbmN5IHJhbmsgZXhwbGFpbmluZyB0aGUgdG90YWwgY2xpY2sgc2hhcmUgb2YgdGhlIHRvcCAzIGNsaWNrZWQgcHJvZHVjdHMgZm9yIGEgc2VhcmNoIHRlcm06CmBgYHtyfQpjb3Ioc2VhcmNoX2RhdGEkc2VhcmNoX2ZyZXF1ZW5jeV9yYW5rLCBzZWFyY2hfZGF0YSR0b3RhbF9jbGlja19zaGFyZSkKYGBgCgpUaGVyZSBhcHBlYXJzIHRvIGJlIGEgbm90YWJsZSBjb3JyZWxhdGlvbiwgYXMgdmlzaWJsZSBpbiB0aGUgc2NhdHRlcnBsb3QgYWJvdmUgYXMgd2VsbC4KCk5vdyBsZXQncyBleHBsb3JlIGhvdyB0aGUgY29udmVyc2lvbiBzaGFyZSBvZiB0aGUgdG9wIDMgY2xpY2tlZCBwcm9kdWN0cyBmb3IgYSBzZWFyY2ggdGVybSB2YXJpZXMgd2l0aCB0aGUgc2VhcmNoIGZyZXF1ZW5jeSByYW5rIG9mIGEgc2VhcmNoIGtleXdvcmQuIFRoZSBzY2F0dGVyIHBsb3QgYmVsb3cgaXMgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gc2hvd2luZyBob3cgY29udHJvbCBvZiBjb252ZXJzaW9uIHNoYXJlIGJ5IHRoZSB0b3AgdGhyZWUgY2xpY2tlZCBwcm9kdWN0cyBmb3IgYSBrZXl3b3JkIHNpbWlsYXJseSBiZWNvbWVzIGVhc2llciBhcyB0aGUgc2VhcmNoIHJhbmsgb2YgYSBzZWFyY2ggdGVybSBnb2VzIGRvd24gKGkuZS4gaXQncyBsZXNzIGNvbXBldGl0aXZlKS4gTm90ZSB0aGF0IHRoZSB4IGF4aXMgaXMgdGhlIFNlYXJjaCBGcmVxdWVuY3kgUmFuayBmb3IgS2V5d29yZCwgd2hlcmUgYSBsYXJnZXIgIyBtZWFucyBsb3dlciBzZWFyY2ggdm9sdW1lLgoKYGBge3J9CmdncGxvdChkYXRhPXNlYXJjaF9kYXRhKSArIGdlb21fcG9pbnQobWFwcGluZz1hZXMoeD1hcy5udW1lcmljKHNlYXJjaF9mcmVxdWVuY3lfcmFuayksIHk9YXMubnVtZXJpYyh0b3RhbF9jb252ZXJzaW9uX3NoYXJlKSksYWxwaGE9MC4wMDUpCmBgYAoKIyMjIyBXaGF0IGlzIG91ciBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBmb3Igc2VhcmNoIGZyZXF1ZW5jeSByYW5rIGV4cGxhaW5pbmcgdGhlIHRvdGFsIGNvbnZlcnNpb24gc2hhcmUgb2YgdGhlIHRvcCAzIGNsaWNrZWQgcHJvZHVjdHMgZm9yIGEgc2VhcmNoIHRlcm0/Ck5vdGUgdGhhdCB3ZSBoYXZlIHNvbWUgbmEgdmFsdWVzIHRoYXQgbmVlZCB0byBiZSBjbGVhbmVkIG91dCBmaXJzdC4KYGBge3J9Cm5vX25hX3RvdGFsX2NvbnZlcnNpb25fc2hhcmUgPC0gc2VhcmNoX2RhdGEgJT4lIGRyb3BfbmEodG90YWxfY29udmVyc2lvbl9zaGFyZSkKY29yKG5vX25hX3RvdGFsX2NvbnZlcnNpb25fc2hhcmUkc2VhcmNoX2ZyZXF1ZW5jeV9yYW5rLCBub19uYV90b3RhbF9jb252ZXJzaW9uX3NoYXJlJHRvdGFsX2NvbnZlcnNpb25fc2hhcmUpCmBgYAoKCldoaWxlIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCBpcyBub3QgdmVyeSBtZWFuaW5nZnVsIGhlcmUsIHdlIGRvIHNlZSBhIHRyYWplY3RvcnkgaW4gb3VyIHBsb3R0ZWQgZGF0YSBpbmRpY2F0aW5nIHRoYXQgYXMgYSBzZWFyY2ggdGVybSBiZWNvbWVzIGxlc3MgY29tcGV0aXRpdmUgKGl0IGhhcyBhIGxvd2VyIHNlYXJjaCBmcmVxdWVuY3kgcmFuayksIHRoZSB0b3AgMyBwcm9kdWN0cyBhcmUgbW9yZSBhYmxlIHRvIGdhcm5lciBhIGxhcmdlciBwZXJjZW50YWdlIG9mIHRoZSBjb252ZXJzaW9uIHNoYXJlLCB0aG91Z2ggdGhpcyBpcyBub3QgYXMgc3Ryb25nIGFzIHdlIHNhdyBmb3IgY2xpY2sgc2hhcmUuCgpOb3RlIHRoYXQgaW4gdGhlIHNjYXR0ZXIgcGxvdCB3ZSBzZWUgdGhlIGV4aXN0ZW5jZSBvZiAnbGluZXMnIG9mIHBvaW50cyBhdCBzcGVjaWZpYyAlIGNvbnZlcnNpb24gaW50ZXJ2YWxzICgwJSwgMzMlLCA1MCUsIDY2JSwgNzUlLCAxMDAlKSwgd2hpY2ggbWFrZXMgc2Vuc2UgZ2l2ZW4gb3VyIGRhdGEuIFRoZSBkYXRhIGlzIGZvciAxLCAyLCBvciAzIHByb2R1Y3RzLCBhbmQgaWYgMSwyLCBvciAzIGdhcm5lciBhIGNlcnRhaW4gJSBvZiB0aGUgY29udmVyc2lvbiBzaGFyZSwgd2UgY2FuIHNlZSB0aGVzZSBsaW5lcyBhcHBlYXJpbmcgYXQgdGhlc2UgZ2l2ZW4gaW50ZXJ2YWxzLg==