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==