A/B Testing for ShoeFly.com
Our favorite online shoe store, ShoeFly.com is performing an A/B
Test. They have two different versions of an ad, which they have placed
in emails, as well as in banner ads on Facebook, Twitter, and Google.
They want to know how the two ads are performing on each of the
different platforms on each day of the week. Help them analyze the data
using aggregate measures.
Analyzing Ad Souces
1.Inspect the first few rows of ad_clicks using head(). What
variables are stored in the columns of the data frame?
Click the hint to see a description of the columns of ad_clicks.
# load packages
library(readr)
library(dplyr)
# load ad clicks data
ad_clicks <- read_csv("ad_clicks.csv")
# inspect ad_clicks here:
head(ad_clicks)
ad_clicks
2.Your manager wants to know which ad platform is getting the most
views.
How many views (i.e., rows of the data frame) came from each
utm_source? Group ad_clicks by utm_source and count the number of rows
in each group. Save your result to views_by_utm, and view it.
# define views_by_utm here:
views_by_utm <- ad_clicks %>%
group_by(utm_source) %>%
summarize(count = n())
views_by_utm
3.We want to know the percentage of people who clicked on ads from
each utm_source. Let’s start by finding the number of clicks per
utm_source.
Group ad_clicks by utm_source and ad_clicked and count the number of
rows in each group, naming the resulting column count. Save your answer
to the variable clicks_by_utm, and view it.
*Hint:
To group together the rows of ad_clicks with the same value for
utm_source and ad_clicked with group_by():
”
clicks_by_utm <- ad_clicks %>%
group_by(utm_source, ad_clicked)
”
To find the number of rows in each grouping by utm_source and
ad_clicked, pipe the result from the grouping into summarize() and use
n():
”
clicks_by_utm <- ad_clicks %>%
group_by(utm_source, ad_clicked) %>%
summarize(count = n())
”
# define clicks_by_utm here:
clicks_by_utm <- ad_clicks %>%
group_by(utm_source, ad_clicked) %>%
summarize(count = n())
clicks_by_utm
4.To find the percentage of people who clicked on ads from each
utm_source, we need to add a new column to ad_clicks that stores the
count of clicks (or the count of not clicking) divided by the total
number of ad views.
Group clicks_by_utm by utm_source and pipe the result into mutate(),
creating a new column percentage that is defined as
count/sum(count).
Save your result to percentage_by_utm and view it. Open the hint for
a description of the percentage_by_utm data frame.
*Hint:
To group together the rows of clicks_by_utm with the same value for
utm_source with group_by():
”
percentage_by_utm <- clicks_by_utm %>%
group_by(utm_source) %>%
”
To add a column that stores the percentage of users who did or did
not click on the ad, pipe the result from the grouping into mutate() and
create a new column percentage defined as count/sum(count):
”
percentage_by_utm <- clicks_by_utm %>%
group_by(utm_source) %>%
mutate(percentage = count/sum(count))
”
1.count represents the number of users who did or did not click on an
ad
2.sum(count) represents the total number of users who saw the ad
percentage_by_utm will now contain the click and non-click rates for
each utm_source/ad_clicked
# define percentage_by_utm here:
percentage_by_utm <- clicks_by_utm %>%
group_by(utm_source) %>%
mutate(percentage = count/sum(count))
percentage_by_utm
5.Filter percentage_by_utm to remove all rows that do not describe
clicked ads, and view it.
Was there a difference in click rates for each source?
*Hint
percentage_by_utm <-clicks_by_utm %>%
group_by(utm_source) %>%
mutate(percentage = count/sum(count)) %>%
filter(ad_clicked == TRUE)
percentage_by_utm
Analyzing an A/B Test
6.The column experimental_group tells us whether the user was shown
ad A or ad B.
Were approximately the same number of people shown both ads? Group
ad_clicks by experimental_group and count the rows, saving your result
to experiment_split.
View experiment_split to see how users were split across the
experiment groups!
# define experiment_split here:
experiment_split <- ad_clicks %>%
group_by(experimental_group) %>%
summarize(count = n())
experiment_split
7.Using group_by() and the columns ad_clicked and experimental_group,
check to see if a greater number of users clicked on ad A or ad B. Save
your result to clicks_by_experiment, and view it.
Reveal the hint to see which ad was more effective.
# define clicks_by_experiment here:
clicks_by_experiment <- ad_clicks %>%
group_by(ad_clicked, experimental_group) %>%
summarize(count = n())
clicks_by_experiment
8.The Product Manager for the A/B test thinks that the clicks might
have changed by day of the week.
Start by creating two data frames: a_clicks and b_clicks, which
contain only the results for A group and B group, respectively.
# define a_clicks here:
a_clicks <- ad_clicks %>%
filter(experimental_group == 'A')
a_clicks
# define b_clicks here:
b_clicks <- ad_clicks %>%
filter(experimental_group == 'B')
b_clicks
9.For each data frame (a_clicks and b_clicks), we want to find the
number of users who clicked on the ad (and did not click on the ad) by
day. Save the results to a_clicks_by_day and b_clicks_by_day, and view
them.
# define a_clicks_by_day here:
a_clicks_by_day <- a_clicks %>%
group_by(day, ad_clicked) %>%
summarize(count = n())
a_clicks_by_day
# a_clicks_by_day <- a_clicks %>%
b_clicks_by_day <- b_clicks %>%
group_by(day, ad_clicked) %>%
summarize(count = n())
b_clicks_by_day
10.To find the percentage of people who clicked on the ads from each
day, we need to add a new column to a_clicks_by_day and b_clicks_by_day
that stores the count of clicks (or the count of not clicking) divided
by the total number of ad views.
Group a_clicks_by_day and b_clicks_by_day by day and pipe the result
into mutate(), creating a new column percentage that is defined as
count/sum(count).
Save your result to a_percentage_by_day and b_percentage_by_day and
view them. Open the hint for a description of these data frames.
# define a_percentage_by_day here:
a_percentage_by_day <- a_clicks_by_day %>%
group_by(day) %>%
mutate(percentage = count/sum(count))
a_percentage_by_day
# define b_percentage_by_day here:
b_percentage_by_day <- b_clicks_by_day %>%
group_by(day) %>%
mutate(percentage = count/sum(count))
b_percentage_by_day
11.Filter a_percentage_by_day and b_percentage_by_day to remove all
rows that do not describe clicked ads, and view them.
Was there a difference in click rates for each day between the two
ads? What happened over the course of the week?
Do you recommend that ShoeFly.com use ad A or ad B?
Reveal the hint for our analysis.
# define a_percentage_by_day here:
a_percentage_by_day <- a_clicks_by_day %>%
group_by(day) %>%
mutate(percentage = count/sum(count)) %>%
filter(ad_clicked == TRUE)
a_percentage_by_day
# define b_percentage_by_day here:
b_percentage_by_day <- b_clicks_by_day %>%
group_by(day) %>%
mutate(percentage = count/sum(count)) %>%
filter(ad_clicked == TRUE)
b_percentage_by_day
*Hint:
To filter a_percentage_by_day to only include rows where ads were
clicked, pipe the result from the previous task into filter() with the
additional argument ad_clicked == TRUE:
”
a_percentage_by_day <- a_clicks_by_day
group_by(day
mutate(percentage = count/sum(count)) %>%
filter(ad_clicked == TRUE)
”
LS0tDQp0aXRsZTogIkEvQiBUZXN0aW5nIGZvciBTaG9lRmx5LmNvbSINCmF1dGhvcjogIkFubmFiZWwgS3VvIg0KZGF0ZTogImByIGZvcm1hdChTeXMudGltZSgpLCAnJVktJW0tJWQgJUg6JU0nKWAiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIEEvQiBUZXN0aW5nIGZvciBTaG9lRmx5LmNvbQ0KT3VyIGZhdm9yaXRlIG9ubGluZSBzaG9lIHN0b3JlLCBTaG9lRmx5LmNvbSBpcyBwZXJmb3JtaW5nIGFuIEEvQiBUZXN0LiBUaGV5IGhhdmUgdHdvIGRpZmZlcmVudCB2ZXJzaW9ucyBvZiBhbiBhZCwgd2hpY2ggdGhleSBoYXZlIHBsYWNlZCBpbiBlbWFpbHMsIGFzIHdlbGwgYXMgaW4gYmFubmVyIGFkcyBvbiBGYWNlYm9vaywgVHdpdHRlciwgYW5kIEdvb2dsZS4gVGhleSB3YW50IHRvIGtub3cgaG93IHRoZSB0d28gYWRzIGFyZSBwZXJmb3JtaW5nIG9uIGVhY2ggb2YgdGhlIGRpZmZlcmVudCBwbGF0Zm9ybXMgb24gZWFjaCBkYXkgb2YgdGhlIHdlZWsuIEhlbHAgdGhlbSBhbmFseXplIHRoZSBkYXRhIHVzaW5nIGFnZ3JlZ2F0ZSBtZWFzdXJlcy4NCg0KIyBBbmFseXppbmcgQWQgU291Y2VzDQoxLkluc3BlY3QgdGhlIGZpcnN0IGZldyByb3dzIG9mIGFkX2NsaWNrcyB1c2luZyBoZWFkKCkuIFdoYXQgdmFyaWFibGVzIGFyZSBzdG9yZWQgaW4gdGhlIGNvbHVtbnMgb2YgdGhlIGRhdGEgZnJhbWU/DQoNCkNsaWNrIHRoZSBoaW50IHRvIHNlZSBhIGRlc2NyaXB0aW9uIG9mIHRoZSBjb2x1bW5zIG9mIGFkX2NsaWNrcy4NCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFLCBlcnJvcj1UUlVFfQ0KIyBsb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIGVycm9yPVRSVUV9DQojIGxvYWQgYWQgY2xpY2tzIGRhdGENCmFkX2NsaWNrcyA8LSByZWFkX2NzdigiYWRfY2xpY2tzLmNzdiIpDQpgYGANCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgaW5zcGVjdCBhZF9jbGlja3MgaGVyZToNCmhlYWQoYWRfY2xpY2tzKQ0KYWRfY2xpY2tzDQpgYGANCg0KMi5Zb3VyIG1hbmFnZXIgd2FudHMgdG8ga25vdyB3aGljaCBhZCBwbGF0Zm9ybSBpcyBnZXR0aW5nIHRoZSBtb3N0IHZpZXdzLg0KDQpIb3cgbWFueSB2aWV3cyAoaS5lLiwgcm93cyBvZiB0aGUgZGF0YSBmcmFtZSkgY2FtZSBmcm9tIGVhY2ggdXRtX3NvdXJjZT8gR3JvdXAgYWRfY2xpY2tzIGJ5IHV0bV9zb3VyY2UgYW5kIGNvdW50IHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIGdyb3VwLiBTYXZlIHlvdXIgcmVzdWx0IHRvIHZpZXdzX2J5X3V0bSwgYW5kIHZpZXcgaXQuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIGRlZmluZSB2aWV3c19ieV91dG0gaGVyZToNCnZpZXdzX2J5X3V0bSA8LSBhZF9jbGlja3MgJT4lDQogIGdyb3VwX2J5KHV0bV9zb3VyY2UpICU+JQ0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpDQoNCnZpZXdzX2J5X3V0bQ0KYGBgDQoNCg0KMy5XZSB3YW50IHRvIGtub3cgdGhlIHBlcmNlbnRhZ2Ugb2YgcGVvcGxlIHdobyBjbGlja2VkIG9uIGFkcyBmcm9tIGVhY2ggdXRtX3NvdXJjZS4gTGV04oCZcyBzdGFydCBieSBmaW5kaW5nIHRoZSBudW1iZXIgb2YgY2xpY2tzIHBlciB1dG1fc291cmNlLg0KDQpHcm91cCBhZF9jbGlja3MgYnkgdXRtX3NvdXJjZSBhbmQgYWRfY2xpY2tlZCBhbmQgY291bnQgdGhlIG51bWJlciBvZiByb3dzIGluIGVhY2ggZ3JvdXAsIG5hbWluZyB0aGUgcmVzdWx0aW5nIGNvbHVtbiBjb3VudC4gU2F2ZSB5b3VyIGFuc3dlciB0byB0aGUgdmFyaWFibGUgY2xpY2tzX2J5X3V0bSwgYW5kIHZpZXcgaXQuDQoNCipIaW50Og0KDQpUbyBncm91cCB0b2dldGhlciB0aGUgcm93cyBvZiBhZF9jbGlja3Mgd2l0aCB0aGUgc2FtZSB2YWx1ZSBmb3IgdXRtX3NvdXJjZSBhbmQgYWRfY2xpY2tlZCB3aXRoIGdyb3VwX2J5KCk6DQoNCiINCg0KY2xpY2tzX2J5X3V0bSA8LSBhZF9jbGlja3MgJT4lDQoNCiAgZ3JvdXBfYnkodXRtX3NvdXJjZSwgYWRfY2xpY2tlZCkNCg0KIg0KDQpUbyBmaW5kIHRoZSBudW1iZXIgb2Ygcm93cyBpbiBlYWNoIGdyb3VwaW5nIGJ5IHV0bV9zb3VyY2UgYW5kIGFkX2NsaWNrZWQsIHBpcGUgdGhlIHJlc3VsdCBmcm9tIHRoZSBncm91cGluZyBpbnRvIHN1bW1hcml6ZSgpIGFuZCB1c2UgbigpOg0KDQoiDQoNCmNsaWNrc19ieV91dG0gPC0gYWRfY2xpY2tzICU+JQ0KDQogIGdyb3VwX2J5KHV0bV9zb3VyY2UsIGFkX2NsaWNrZWQpICU+JQ0KICANCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKQ0KDQoiDQoNCg0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KIyBkZWZpbmUgY2xpY2tzX2J5X3V0bSBoZXJlOg0KY2xpY2tzX2J5X3V0bSA8LSBhZF9jbGlja3MgJT4lDQogIGdyb3VwX2J5KHV0bV9zb3VyY2UsIGFkX2NsaWNrZWQpICU+JQ0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpDQoNCmNsaWNrc19ieV91dG0NCmBgYA0KDQo0LlRvIGZpbmQgdGhlIHBlcmNlbnRhZ2Ugb2YgcGVvcGxlIHdobyBjbGlja2VkIG9uIGFkcyBmcm9tIGVhY2ggdXRtX3NvdXJjZSwgd2UgbmVlZCB0byBhZGQgYSBuZXcgY29sdW1uIHRvIGFkX2NsaWNrcyB0aGF0IHN0b3JlcyB0aGUgY291bnQgb2YgY2xpY2tzIChvciB0aGUgY291bnQgb2Ygbm90IGNsaWNraW5nKSBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBudW1iZXIgb2YgYWQgdmlld3MuDQoNCkdyb3VwIGNsaWNrc19ieV91dG0gYnkgdXRtX3NvdXJjZSBhbmQgcGlwZSB0aGUgcmVzdWx0IGludG8gbXV0YXRlKCksIGNyZWF0aW5nIGEgbmV3IGNvbHVtbiBwZXJjZW50YWdlIHRoYXQgaXMgZGVmaW5lZCBhcyBjb3VudC9zdW0oY291bnQpLg0KDQpTYXZlIHlvdXIgcmVzdWx0IHRvIHBlcmNlbnRhZ2VfYnlfdXRtIGFuZCB2aWV3IGl0LiBPcGVuIHRoZSBoaW50IGZvciBhIGRlc2NyaXB0aW9uIG9mIHRoZSBwZXJjZW50YWdlX2J5X3V0bSBkYXRhIGZyYW1lLg0KDQoqSGludDogDQoNClRvIGdyb3VwIHRvZ2V0aGVyIHRoZSByb3dzIG9mIGNsaWNrc19ieV91dG0gd2l0aCB0aGUgc2FtZSB2YWx1ZSBmb3IgdXRtX3NvdXJjZSB3aXRoIGdyb3VwX2J5KCk6DQoNCiINCg0KcGVyY2VudGFnZV9ieV91dG0gPC0gY2xpY2tzX2J5X3V0bSAlPiUNCg0KICBncm91cF9ieSh1dG1fc291cmNlKSAlPiUNCiAgDQoiDQoNClRvIGFkZCBhIGNvbHVtbiB0aGF0IHN0b3JlcyB0aGUgcGVyY2VudGFnZSBvZiB1c2VycyB3aG8gZGlkIG9yIGRpZCBub3QgY2xpY2sgb24gdGhlIGFkLCBwaXBlIHRoZSByZXN1bHQgZnJvbSB0aGUgZ3JvdXBpbmcgaW50byBtdXRhdGUoKSBhbmQgY3JlYXRlIGEgbmV3IGNvbHVtbiBwZXJjZW50YWdlIGRlZmluZWQgYXMgY291bnQvc3VtKGNvdW50KToNCg0KIg0KDQpwZXJjZW50YWdlX2J5X3V0bSA8LSBjbGlja3NfYnlfdXRtICU+JQ0KDQogIGdyb3VwX2J5KHV0bV9zb3VyY2UpICU+JQ0KICANCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBjb3VudC9zdW0oY291bnQpKQ0KDQoiDQoNCjEuY291bnQgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIHVzZXJzIHdobyBkaWQgb3IgZGlkIG5vdCBjbGljayBvbiBhbiBhZA0KDQoyLnN1bShjb3VudCkgcmVwcmVzZW50cyB0aGUgdG90YWwgbnVtYmVyIG9mIHVzZXJzIHdobyBzYXcgdGhlIGFkDQoNCnBlcmNlbnRhZ2VfYnlfdXRtIHdpbGwgbm93IGNvbnRhaW4gdGhlIGNsaWNrIGFuZCBub24tY2xpY2sgcmF0ZXMgZm9yIGVhY2ggdXRtX3NvdXJjZS9hZF9jbGlja2VkIA0KDQpgYGB7ciBlcnJvcj1UUlVFfQ0KIyBkZWZpbmUgcGVyY2VudGFnZV9ieV91dG0gaGVyZToNCnBlcmNlbnRhZ2VfYnlfdXRtIDwtIGNsaWNrc19ieV91dG0gJT4lDQogIGdyb3VwX2J5KHV0bV9zb3VyY2UpICU+JQ0KICBtdXRhdGUocGVyY2VudGFnZSA9IGNvdW50L3N1bShjb3VudCkpDQoNCnBlcmNlbnRhZ2VfYnlfdXRtDQpgYGANCg0KNS5GaWx0ZXIgcGVyY2VudGFnZV9ieV91dG0gdG8gcmVtb3ZlIGFsbCByb3dzIHRoYXQgZG8gbm90IGRlc2NyaWJlIGNsaWNrZWQgYWRzLCBhbmQgdmlldyBpdC4NCg0KV2FzIHRoZXJlIGEgZGlmZmVyZW5jZSBpbiBjbGljayByYXRlcyBmb3IgZWFjaCBzb3VyY2U/DQoNCipIaW50DQpgYGB7cn0NCnBlcmNlbnRhZ2VfYnlfdXRtIDwtY2xpY2tzX2J5X3V0bSAlPiUNCiAgZ3JvdXBfYnkodXRtX3NvdXJjZSkgJT4lDQogIG11dGF0ZShwZXJjZW50YWdlID0gY291bnQvc3VtKGNvdW50KSkgJT4lDQogIGZpbHRlcihhZF9jbGlja2VkID09IFRSVUUpDQoNCnBlcmNlbnRhZ2VfYnlfdXRtDQpgYGANCg0KIyBBbmFseXppbmcgYW4gQS9CIFRlc3QNCg0KNi5UaGUgY29sdW1uIGV4cGVyaW1lbnRhbF9ncm91cCB0ZWxscyB1cyB3aGV0aGVyIHRoZSB1c2VyIHdhcyBzaG93biBhZCBBIG9yIGFkIEIuDQoNCldlcmUgYXBwcm94aW1hdGVseSB0aGUgc2FtZSBudW1iZXIgb2YgcGVvcGxlIHNob3duIGJvdGggYWRzPyBHcm91cCBhZF9jbGlja3MgYnkgZXhwZXJpbWVudGFsX2dyb3VwIGFuZCBjb3VudCB0aGUgcm93cywgc2F2aW5nIHlvdXIgcmVzdWx0IHRvIGV4cGVyaW1lbnRfc3BsaXQuDQoNClZpZXcgZXhwZXJpbWVudF9zcGxpdCB0byBzZWUgaG93IHVzZXJzIHdlcmUgc3BsaXQgYWNyb3NzIHRoZSBleHBlcmltZW50IGdyb3VwcyENCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgZGVmaW5lIGV4cGVyaW1lbnRfc3BsaXQgaGVyZToNCmV4cGVyaW1lbnRfc3BsaXQgPC0gYWRfY2xpY2tzICU+JQ0KICBncm91cF9ieShleHBlcmltZW50YWxfZ3JvdXApICU+JQ0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpDQoNCmV4cGVyaW1lbnRfc3BsaXQNCmBgYA0KDQo3LlVzaW5nIGdyb3VwX2J5KCkgYW5kIHRoZSBjb2x1bW5zIGFkX2NsaWNrZWQgYW5kIGV4cGVyaW1lbnRhbF9ncm91cCwgY2hlY2sgdG8gc2VlIGlmIGEgZ3JlYXRlciBudW1iZXIgb2YgdXNlcnMgY2xpY2tlZCBvbiBhZCBBIG9yIGFkIEIuIFNhdmUgeW91ciByZXN1bHQgdG8gY2xpY2tzX2J5X2V4cGVyaW1lbnQsIGFuZCB2aWV3IGl0Lg0KDQpSZXZlYWwgdGhlIGhpbnQgdG8gc2VlIHdoaWNoIGFkIHdhcyBtb3JlIGVmZmVjdGl2ZS4NCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgZGVmaW5lIGNsaWNrc19ieV9leHBlcmltZW50IGhlcmU6DQpjbGlja3NfYnlfZXhwZXJpbWVudCA8LSBhZF9jbGlja3MgJT4lDQogIGdyb3VwX2J5KGFkX2NsaWNrZWQsIGV4cGVyaW1lbnRhbF9ncm91cCkgJT4lDQogIHN1bW1hcml6ZShjb3VudCA9IG4oKSkNCg0KY2xpY2tzX2J5X2V4cGVyaW1lbnQNCmBgYA0KDQo4LlRoZSBQcm9kdWN0IE1hbmFnZXIgZm9yIHRoZSBBL0IgdGVzdCB0aGlua3MgdGhhdCB0aGUgY2xpY2tzIG1pZ2h0IGhhdmUgY2hhbmdlZCBieSBkYXkgb2YgdGhlIHdlZWsuDQoNClN0YXJ0IGJ5IGNyZWF0aW5nIHR3byBkYXRhIGZyYW1lczogYV9jbGlja3MgYW5kIGJfY2xpY2tzLCB3aGljaCBjb250YWluIG9ubHkgdGhlIHJlc3VsdHMgZm9yIEEgZ3JvdXAgYW5kIEIgZ3JvdXAsIHJlc3BlY3RpdmVseS4NCg0KYGBge3IgZXJyb3I9VFJVRX0NCiMgZGVmaW5lIGFfY2xpY2tzIGhlcmU6DQphX2NsaWNrcyA8LSBhZF9jbGlja3MgJT4lDQogIGZpbHRlcihleHBlcmltZW50YWxfZ3JvdXAgPT0gJ0EnKQ0KDQphX2NsaWNrcw0KDQojIGRlZmluZSBiX2NsaWNrcyBoZXJlOg0KYl9jbGlja3MgPC0gYWRfY2xpY2tzICU+JQ0KICBmaWx0ZXIoZXhwZXJpbWVudGFsX2dyb3VwID09ICdCJykNCg0KYl9jbGlja3MNCmBgYA0KDQo5LkZvciBlYWNoIGRhdGEgZnJhbWUgKGFfY2xpY2tzIGFuZCBiX2NsaWNrcyksIHdlIHdhbnQgdG8gZmluZCB0aGUgbnVtYmVyIG9mIHVzZXJzIHdobyBjbGlja2VkIG9uIHRoZSBhZCAoYW5kIGRpZCBub3QgY2xpY2sgb24gdGhlIGFkKSBieSBkYXkuIFNhdmUgdGhlIHJlc3VsdHMgdG8gYV9jbGlja3NfYnlfZGF5IGFuZCBiX2NsaWNrc19ieV9kYXksIGFuZCB2aWV3IHRoZW0uDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIGRlZmluZSBhX2NsaWNrc19ieV9kYXkgaGVyZToNCmFfY2xpY2tzX2J5X2RheSA8LSBhX2NsaWNrcyAlPiUNCiAgZ3JvdXBfYnkoZGF5LCBhZF9jbGlja2VkKSAlPiUNCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKQ0KYV9jbGlja3NfYnlfZGF5DQoNCiMgYV9jbGlja3NfYnlfZGF5IDwtIGFfY2xpY2tzICU+JQ0KYl9jbGlja3NfYnlfZGF5IDwtIGJfY2xpY2tzICU+JQ0KICBncm91cF9ieShkYXksIGFkX2NsaWNrZWQpICU+JQ0KICBzdW1tYXJpemUoY291bnQgPSBuKCkpDQoNCmJfY2xpY2tzX2J5X2RheQ0KYGBgDQoNCg0KMTAuVG8gZmluZCB0aGUgcGVyY2VudGFnZSBvZiBwZW9wbGUgd2hvIGNsaWNrZWQgb24gdGhlIGFkcyBmcm9tIGVhY2ggZGF5LCB3ZSBuZWVkIHRvIGFkZCBhIG5ldyBjb2x1bW4gdG8gYV9jbGlja3NfYnlfZGF5IGFuZCBiX2NsaWNrc19ieV9kYXkgdGhhdCBzdG9yZXMgdGhlIGNvdW50IG9mIGNsaWNrcyAob3IgdGhlIGNvdW50IG9mIG5vdCBjbGlja2luZykgZGl2aWRlZCBieSB0aGUgdG90YWwgbnVtYmVyIG9mIGFkIHZpZXdzLg0KDQpHcm91cCBhX2NsaWNrc19ieV9kYXkgYW5kIGJfY2xpY2tzX2J5X2RheSBieSBkYXkgYW5kIHBpcGUgdGhlIHJlc3VsdCBpbnRvIG11dGF0ZSgpLCBjcmVhdGluZyBhIG5ldyBjb2x1bW4gcGVyY2VudGFnZSB0aGF0IGlzIGRlZmluZWQgYXMgY291bnQvc3VtKGNvdW50KS4NCg0KU2F2ZSB5b3VyIHJlc3VsdCB0byBhX3BlcmNlbnRhZ2VfYnlfZGF5IGFuZCBiX3BlcmNlbnRhZ2VfYnlfZGF5IGFuZCB2aWV3IHRoZW0uIE9wZW4gdGhlIGhpbnQgZm9yIGEgZGVzY3JpcHRpb24gb2YgdGhlc2UgZGF0YSBmcmFtZXMuDQoNCmBgYHtyIGVycm9yPVRSVUV9DQojIGRlZmluZSBhX3BlcmNlbnRhZ2VfYnlfZGF5IGhlcmU6DQphX3BlcmNlbnRhZ2VfYnlfZGF5IDwtIGFfY2xpY2tzX2J5X2RheSAlPiUNCiAgZ3JvdXBfYnkoZGF5KSAlPiUNCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBjb3VudC9zdW0oY291bnQpKQ0KDQphX3BlcmNlbnRhZ2VfYnlfZGF5DQoNCiMgZGVmaW5lIGJfcGVyY2VudGFnZV9ieV9kYXkgaGVyZToNCmJfcGVyY2VudGFnZV9ieV9kYXkgPC0gYl9jbGlja3NfYnlfZGF5ICU+JQ0KICBncm91cF9ieShkYXkpICU+JQ0KICBtdXRhdGUocGVyY2VudGFnZSA9IGNvdW50L3N1bShjb3VudCkpDQoNCmJfcGVyY2VudGFnZV9ieV9kYXkNCmBgYA0KDQoxMS5GaWx0ZXIgYV9wZXJjZW50YWdlX2J5X2RheSBhbmQgYl9wZXJjZW50YWdlX2J5X2RheSB0byByZW1vdmUgYWxsIHJvd3MgdGhhdCBkbyBub3QgZGVzY3JpYmUgY2xpY2tlZCBhZHMsIGFuZCB2aWV3IHRoZW0uDQoNCldhcyB0aGVyZSBhIGRpZmZlcmVuY2UgaW4gY2xpY2sgcmF0ZXMgZm9yIGVhY2ggZGF5IGJldHdlZW4gdGhlIHR3byBhZHM/IFdoYXQgaGFwcGVuZWQgb3ZlciB0aGUgY291cnNlIG9mIHRoZSB3ZWVrPw0KDQpEbyB5b3UgcmVjb21tZW5kIHRoYXQgU2hvZUZseS5jb20gdXNlIGFkIEEgb3IgYWQgQj8NCg0KUmV2ZWFsIHRoZSBoaW50IGZvciBvdXIgYW5hbHlzaXMuDQoNCmBgYHtyfQ0KIyBkZWZpbmUgYV9wZXJjZW50YWdlX2J5X2RheSBoZXJlOg0KYV9wZXJjZW50YWdlX2J5X2RheSA8LSBhX2NsaWNrc19ieV9kYXkgJT4lDQogIGdyb3VwX2J5KGRheSkgJT4lDQogIG11dGF0ZShwZXJjZW50YWdlID0gY291bnQvc3VtKGNvdW50KSkgJT4lDQogIGZpbHRlcihhZF9jbGlja2VkID09IFRSVUUpDQoNCmFfcGVyY2VudGFnZV9ieV9kYXkNCg0KIyBkZWZpbmUgYl9wZXJjZW50YWdlX2J5X2RheSBoZXJlOg0KYl9wZXJjZW50YWdlX2J5X2RheSA8LSBiX2NsaWNrc19ieV9kYXkgJT4lDQogIGdyb3VwX2J5KGRheSkgJT4lDQogIG11dGF0ZShwZXJjZW50YWdlID0gY291bnQvc3VtKGNvdW50KSkgJT4lDQogIGZpbHRlcihhZF9jbGlja2VkID09IFRSVUUpDQoNCmJfcGVyY2VudGFnZV9ieV9kYXkNCmBgYA0KDQoqSGludDoNCg0KVG8gZmlsdGVyIGFfcGVyY2VudGFnZV9ieV9kYXkgdG8gb25seSBpbmNsdWRlIHJvd3Mgd2hlcmUgYWRzIHdlcmUgY2xpY2tlZCwgcGlwZSB0aGUgcmVzdWx0IGZyb20gdGhlIHByZXZpb3VzIHRhc2sgaW50byBmaWx0ZXIoKSB3aXRoIHRoZSBhZGRpdGlvbmFsIGFyZ3VtZW50IGFkX2NsaWNrZWQgPT0gVFJVRToNCg0KIg0KDQphX3BlcmNlbnRhZ2VfYnlfZGF5IDwtIGFfY2xpY2tzX2J5X2RheQ0KDQogIGdyb3VwX2J5KGRheQ0KICANCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSBjb3VudC9zdW0oY291bnQpKSAlPiUNCiAgDQogIGZpbHRlcihhZF9jbGlja2VkID09IFRSVUUpDQoNCiI=