Background
This is a simple project in R demonstrating how to calculate the
average customer lifetime value (CLV) for an example Amazon business
using Amazon-provided business reports.
CLV is a useful metric that lets a business estimate the average
amount that a customer spends on the business over the course of time.
Some businesses have products or business models with strong customer
repurchase rates (think for example a supplement company or an apparel
company with strong brand loyalty), whereas others will have a less
frequent relationship with their customers. Knowing the lifetime value
of a customer could help a business decide whether the cost of acquiring
a new customer justifies its current marketing spend. It could also help
the business create particular financial projections or serve as a
metric for customer loyalty.
Set up initial libraries:
library(tidyverse)
library(tidyr)
library(dplyr)
library(janitor)
library(readr)
Obtain the relevant reports:
The appropriate sales report needed for this project can be obtained
from one’s Amazon Seller Central account: Reports –> Fulfillment
–> Amazon Fulfilled Shipments
Download reports covering at least 1 year of sales. While the project
below uses ~1.5 years of reports (the maximum available), it may be
easier to stick to 1 year of sales data for ease in interpreting the CLV
number.
For our project, we obtained reports from 12/12/2020 - 5/31/2022.
Reports were generated for 1 month intervals. There were 18 final output
files, and each file was given a date-descriptive name such as:
6_16_21_to_7_16_21.csv 7_17_21_to_8_16_21.csv 8_17_21_to_9_16_21.csv
Each file contain the following columns: Amazon Order Id Merchant
Order Id Shipment ID Shipment Item Id Amazon Order Item Id Merchant
Order Item Id Purchase Date Payments Date Shipment Date Reporting Date
Buyer Email Buyer Name Buyer Phone Number Merchant SKU Title Shipped
Quantity Currency Item Price Item Tax Shipping Price Shipping Tax Gift
Wrap Price Gift Wrap Tax Ship Service Level Recipient Name Shipping
Address 1 Shipping Address 2 Shipping Address 3 Shipping City Shipping
State Shipping Postal Code Shipping Country Code Shipping Phone Number
Billing Address 1 Billing Address 2 Billing Address 3 Billing City
Billing State bill-postal-code bill-country Item Promo Discount Shipment
Promo Discount Carrier Tracking Number Estimated Arrival Date FC
Fulfillment Channel Sales Channel
As of this post (2022), several of the columns are no longer
populated with data by Amazon out of concerns for customer privacy,
including buyer name, phone number and address.
The 18 CSV files are joined together under a variable named df:
temp <- list.files(pattern="*.csv")
myfiles <- lapply(temp, read_csv)
df <- bind_rows(myfiles)
We have a table that includes 60,030 entries and 48 columns.
Clean the titles to make them easier to work with:
df <- clean_names(df)
View(df)
Export the joined single file for convenient use in the future
(change the path to one of your own choosing):
write_csv(df, "/Users/YourName/Desktop/FileName.csv")
Converting item_price column to a single currency:
The Amazon seller account we are working with sells to customers in
the US, Canada and Mexico, and therefore the order amounts are given in
USD, MXN and CAD, which can confuse our summation calculations unless
they are converted to a single currency.
We first confirm that only USD, MXN and CAD are represented in the
dataset:
df %>% filter(currency !="CAD" & currency !="MXN" & currency !="USD")
Let’s use the case_when and round functions to convert our values
into a USD value, which will be put in a new column in a new dataframe
we will call new_df. Note that the conversion values given below reflect
the rates at the time of writing this. Search online for the latest
conversion values.
new_df <- df %>%
mutate(item_price_USD = case_when(
df$currency == "USD" ~ df$item_price,
df$currency == "CAD" ~ round(df$item_price * 0.794585, digits = 2),
df$currency == "MXN" ~ round(df$item_price * 0.051111, digits = 2)))
View(new_df)
Find a unique identifier for customers in the reports:
As noted above, several columns are no longer populated with data by
Amazon out of concerns for customer privacy, including buyer name, phone
number and address. Without this unique identifier information, we will
need to be creative to determine each unique customer. We can do so by
referring to the buyer email address, an Amazon-generated email address
that is not the actual buyer’s email address, but an Amazon-server-tied
email address that appears to be consistent across orders when the same
user purchases.
To verify, try searching a few of the provided Amazon customer email
in our table to see if they are used for more than one order. This would
indicate that the emails are linked to a unique customers, and not
created for each order. When you identify an email that is linked to
more than one order, you can verify it is the same customer because the
customer’s address city and zip code will match, this address
information being the limited customer-specific data that the Amazon
report DOES provide.
To test, try a few emails from the table and replace them with the
the email in this code snippet:
Let’s identify the number of unique buyers over the time period of
our data (1.5 years) by counting the unique email addresses:
unique_buyers <- length(unique(new_df$buyer_email))
unique_buyers
[1] 37796
We have been able to determine that there are 37796 unique customers
represented in our table.
Find the total sales over this time period by summing the
item_price_USD column:
total_sales <- sum(new_df$item_price_USD)
total_sales
[1] 876787.1
Over this time period our example Amazon account has sold 876,787.10
worth of products.
Because data in our report is given on each item sold and not each
order, let us derive the number of unique orders over this period:
unique_orders <- length(unique(new_df$amazon_order_id))
unique_orders
[1] 47788
We find that our example account had made 47788 unique orders during
the period of 1.5 years.
Determining the average order value:
avg_order_value <- round(total_sales/unique_orders, digits=2)
avg_order_value
[1] 18.35
We find that the average order value for this account is $18.35.
Calculating the average order frequency for our customers (total
orders / unique customers):
order_freq <- round(unique_orders/unique_buyers, digits=2)
order_freq
[1] 1.26
We find that the customers of this Amazon business have an average
order frequency of 1.26 orders over the period of 1.5 years.
Calculating the average customer value (average order value / order
frequency):
avg_cust_value = round(avg_order_value*order_freq, digit=2)
avg_cust_value
[1] 23.12
The average customer value for the given data is 23.12, indicating
that this is the amount we expect our average customer to spend over a
period of 1.5 years. If data for only 1 year was selected, then this
figure represents the customer’s value over just one year. To determine
the lifetime value of our customer, we make a reasonable
estimate given our knowledge of the business that the average customer
engages with this business for 3 years. That would make the total
average lifetime customer value equal to $46.24. Our estimate of 3 years
will vary from business to business, and business stakeholders will be
in the best position to make this determination
LS0tCnRpdGxlOiAiQ3VzdG9tZXIgTGlmZXRpbWUgVmFsdWUgZnJvbSBBbWF6b24gU2VsbGVyIERhdGEgKEEgUHJvamVjdCBpbiBSKSIKYXV0aG9yOiAiRiBBYmR1bGxhaCIKZGF0ZTogJzIwMjItMDYtMDYnCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIEJhY2tncm91bmQKClRoaXMgaXMgYSBzaW1wbGUgcHJvamVjdCBpbiBSIGRlbW9uc3RyYXRpbmcgaG93IHRvIGNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBjdXN0b21lciBsaWZldGltZSB2YWx1ZSAoQ0xWKSBmb3IgYW4gZXhhbXBsZSBBbWF6b24gYnVzaW5lc3MgdXNpbmcgQW1hem9uLXByb3ZpZGVkIGJ1c2luZXNzIHJlcG9ydHMuCgpDTFYgaXMgYSB1c2VmdWwgbWV0cmljIHRoYXQgbGV0cyBhIGJ1c2luZXNzIGVzdGltYXRlIHRoZSBhdmVyYWdlIGFtb3VudCB0aGF0IGEgY3VzdG9tZXIgc3BlbmRzIG9uIHRoZSBidXNpbmVzcyBvdmVyIHRoZSBjb3Vyc2Ugb2YgdGltZS4gU29tZSBidXNpbmVzc2VzIGhhdmUgcHJvZHVjdHMgb3IgYnVzaW5lc3MgbW9kZWxzIHdpdGggc3Ryb25nIGN1c3RvbWVyIHJlcHVyY2hhc2UgcmF0ZXMgKHRoaW5rIGZvciBleGFtcGxlIGEgc3VwcGxlbWVudCBjb21wYW55IG9yIGFuIGFwcGFyZWwgY29tcGFueSB3aXRoIHN0cm9uZyBicmFuZCBsb3lhbHR5KSwgd2hlcmVhcyBvdGhlcnMgd2lsbCBoYXZlIGEgbGVzcyBmcmVxdWVudCByZWxhdGlvbnNoaXAgd2l0aCB0aGVpciBjdXN0b21lcnMuIEtub3dpbmcgdGhlIGxpZmV0aW1lIHZhbHVlIG9mIGEgY3VzdG9tZXIgY291bGQgaGVscCBhIGJ1c2luZXNzIGRlY2lkZSB3aGV0aGVyIHRoZSBjb3N0IG9mIGFjcXVpcmluZyBhIG5ldyBjdXN0b21lciBqdXN0aWZpZXMgaXRzIGN1cnJlbnQgbWFya2V0aW5nIHNwZW5kLiBJdCBjb3VsZCBhbHNvIGhlbHAgdGhlIGJ1c2luZXNzIGNyZWF0ZSBwYXJ0aWN1bGFyIGZpbmFuY2lhbCBwcm9qZWN0aW9ucyBvciBzZXJ2ZSBhcyBhIG1ldHJpYyBmb3IgY3VzdG9tZXIgbG95YWx0eS4KCiMjIyMgU2V0IHVwIGluaXRpYWwgbGlicmFyaWVzOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShyZWFkcikKYGBgCgojIyMjIE9idGFpbiB0aGUgcmVsZXZhbnQgcmVwb3J0czoKVGhlIGFwcHJvcHJpYXRlIHNhbGVzIHJlcG9ydCBuZWVkZWQgZm9yIHRoaXMgcHJvamVjdCBjYW4gYmUgb2J0YWluZWQgZnJvbSBvbmUncyBBbWF6b24gU2VsbGVyIENlbnRyYWwgYWNjb3VudDoKUmVwb3J0cyAtLT4gRnVsZmlsbG1lbnQgLS0+IEFtYXpvbiBGdWxmaWxsZWQgU2hpcG1lbnRzCgpEb3dubG9hZCByZXBvcnRzIGNvdmVyaW5nIGF0IGxlYXN0IDEgeWVhciBvZiBzYWxlcy4gV2hpbGUgdGhlIHByb2plY3QgYmVsb3cgdXNlcyB+MS41IHllYXJzIG9mIHJlcG9ydHMgKHRoZSBtYXhpbXVtIGF2YWlsYWJsZSksIGl0IG1heSBiZSBlYXNpZXIgdG8gc3RpY2sgdG8gMSB5ZWFyIG9mIHNhbGVzIGRhdGEgZm9yIGVhc2UgaW4gaW50ZXJwcmV0aW5nIHRoZSBDTFYgbnVtYmVyLgoKRm9yIG91ciBwcm9qZWN0LCB3ZSBvYnRhaW5lZCByZXBvcnRzIGZyb20gMTIvMTIvMjAyMCAtIDUvMzEvMjAyMi4gUmVwb3J0cyB3ZXJlIGdlbmVyYXRlZCBmb3IgMSBtb250aCBpbnRlcnZhbHMuIFRoZXJlIHdlcmUgMTggZmluYWwgb3V0cHV0IGZpbGVzLCBhbmQgZWFjaCBmaWxlIHdhcyBnaXZlbiBhIGRhdGUtZGVzY3JpcHRpdmUgbmFtZSBzdWNoIGFzOgo2XzE2XzIxX3RvXzdfMTZfMjEuY3N2CjdfMTdfMjFfdG9fOF8xNl8yMS5jc3YKOF8xN18yMV90b185XzE2XzIxLmNzdgoKRWFjaCBmaWxlIGNvbnRhaW4gdGhlIGZvbGxvd2luZyBjb2x1bW5zOgpBbWF6b24gT3JkZXIgSWQKTWVyY2hhbnQgT3JkZXIgSWQKU2hpcG1lbnQgSUQKU2hpcG1lbnQgSXRlbSBJZApBbWF6b24gT3JkZXIgSXRlbSBJZApNZXJjaGFudCBPcmRlciBJdGVtIElkClB1cmNoYXNlIERhdGUKUGF5bWVudHMgRGF0ZQpTaGlwbWVudCBEYXRlClJlcG9ydGluZyBEYXRlCkJ1eWVyIEVtYWlsCkJ1eWVyIE5hbWUKQnV5ZXIgUGhvbmUgTnVtYmVyCk1lcmNoYW50IFNLVQpUaXRsZQpTaGlwcGVkIFF1YW50aXR5CkN1cnJlbmN5Ckl0ZW0gUHJpY2UKSXRlbSBUYXgKU2hpcHBpbmcgUHJpY2UKU2hpcHBpbmcgVGF4CkdpZnQgV3JhcCBQcmljZQpHaWZ0IFdyYXAgVGF4ClNoaXAgU2VydmljZSBMZXZlbApSZWNpcGllbnQgTmFtZQpTaGlwcGluZyBBZGRyZXNzIDEKU2hpcHBpbmcgQWRkcmVzcyAyClNoaXBwaW5nIEFkZHJlc3MgMwpTaGlwcGluZyBDaXR5ClNoaXBwaW5nIFN0YXRlClNoaXBwaW5nIFBvc3RhbCBDb2RlClNoaXBwaW5nIENvdW50cnkgQ29kZQpTaGlwcGluZyBQaG9uZSBOdW1iZXIKQmlsbGluZyBBZGRyZXNzIDEKQmlsbGluZyBBZGRyZXNzIDIKQmlsbGluZyBBZGRyZXNzIDMKQmlsbGluZyBDaXR5CkJpbGxpbmcgU3RhdGUKYmlsbC1wb3N0YWwtY29kZQpiaWxsLWNvdW50cnkKSXRlbSBQcm9tbyBEaXNjb3VudApTaGlwbWVudCBQcm9tbyBEaXNjb3VudApDYXJyaWVyClRyYWNraW5nIE51bWJlcgpFc3RpbWF0ZWQgQXJyaXZhbCBEYXRlCkZDCkZ1bGZpbGxtZW50IENoYW5uZWwKU2FsZXMgQ2hhbm5lbAoKQXMgb2YgdGhpcyBwb3N0ICgyMDIyKSwgc2V2ZXJhbCBvZiB0aGUgY29sdW1ucyBhcmUgbm8gbG9uZ2VyIHBvcHVsYXRlZCB3aXRoIGRhdGEgYnkgQW1hem9uIG91dCBvZiBjb25jZXJucyBmb3IgY3VzdG9tZXIgcHJpdmFjeSwgaW5jbHVkaW5nIGJ1eWVyIG5hbWUsIHBob25lIG51bWJlciBhbmQgYWRkcmVzcy4KClRoZSAxOCBDU1YgZmlsZXMgYXJlIGpvaW5lZCB0b2dldGhlciB1bmRlciBhIHZhcmlhYmxlIG5hbWVkIGRmOgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KdGVtcCA8LSBsaXN0LmZpbGVzKHBhdHRlcm49IiouY3N2IikKbXlmaWxlcyA8LSBsYXBwbHkodGVtcCwgcmVhZF9jc3YpCmRmIDwtIGJpbmRfcm93cyhteWZpbGVzKQpgYGAKCldlIGhhdmUgYSB0YWJsZSB0aGF0IGluY2x1ZGVzIDYwLDAzMCBlbnRyaWVzIGFuZCA0OCBjb2x1bW5zLgoKIyMjIyBDbGVhbiB0aGUgdGl0bGVzIHRvIG1ha2UgdGhlbSBlYXNpZXIgdG8gd29yayB3aXRoOgoKYGBge3J9CmRmIDwtIGNsZWFuX25hbWVzKGRmKQoKVmlldyhkZikKYGBgCgojIyMjIEV4cG9ydCB0aGUgam9pbmVkIHNpbmdsZSBmaWxlIGZvciBjb252ZW5pZW50IHVzZSBpbiB0aGUgZnV0dXJlIChjaGFuZ2UgdGhlIHBhdGggdG8gb25lIG9mIHlvdXIgb3duIGNob29zaW5nKToKYGBge3J9CndyaXRlX2NzdihkZiwgIi9Vc2Vycy9Zb3VyTmFtZS9EZXNrdG9wL0ZpbGVOYW1lLmNzdiIpCmBgYAoKIyMjIyBDb252ZXJ0aW5nIGl0ZW1fcHJpY2UgY29sdW1uIHRvIGEgc2luZ2xlIGN1cnJlbmN5OgpUaGUgQW1hem9uIHNlbGxlciBhY2NvdW50IHdlIGFyZSB3b3JraW5nIHdpdGggc2VsbHMgdG8gY3VzdG9tZXJzIGluIHRoZSBVUywgQ2FuYWRhIGFuZCBNZXhpY28sIGFuZCB0aGVyZWZvcmUgdGhlIG9yZGVyIGFtb3VudHMgYXJlIGdpdmVuIGluIFVTRCwgTVhOIGFuZCBDQUQsIHdoaWNoIGNhbiBjb25mdXNlIG91ciBzdW1tYXRpb24gY2FsY3VsYXRpb25zIHVubGVzcyB0aGV5IGFyZSBjb252ZXJ0ZWQgdG8gYSBzaW5nbGUgY3VycmVuY3kuCgpXZSBmaXJzdCBjb25maXJtIHRoYXQgb25seSBVU0QsIE1YTiBhbmQgQ0FEIGFyZSByZXByZXNlbnRlZCBpbiB0aGUgZGF0YXNldDoKYGBge3J9CmRmICU+JSBmaWx0ZXIoY3VycmVuY3kgIT0iQ0FEIiAmIGN1cnJlbmN5ICE9Ik1YTiIgJiBjdXJyZW5jeSAhPSJVU0QiKQpgYGAKCkxldCdzIHVzZSB0aGUgY2FzZV93aGVuIGFuZCByb3VuZCBmdW5jdGlvbnMgdG8gY29udmVydCBvdXIgdmFsdWVzIGludG8gYSBVU0QgdmFsdWUsIHdoaWNoIHdpbGwgYmUgcHV0IGluIGEgbmV3IGNvbHVtbiBpbiBhIG5ldyBkYXRhZnJhbWUgd2Ugd2lsbCBjYWxsIG5ld19kZi4gTm90ZSB0aGF0IHRoZSBjb252ZXJzaW9uIHZhbHVlcyBnaXZlbiBiZWxvdyByZWZsZWN0IHRoZSByYXRlcyBhdCB0aGUgdGltZSBvZiB3cml0aW5nIHRoaXMuIFNlYXJjaCBvbmxpbmUgZm9yIHRoZSBsYXRlc3QgY29udmVyc2lvbiB2YWx1ZXMuCgpgYGB7cn0KbmV3X2RmIDwtIGRmICU+JSAKICBtdXRhdGUoaXRlbV9wcmljZV9VU0QgPSBjYXNlX3doZW4oCiAgICBkZiRjdXJyZW5jeSA9PSAiVVNEIiB+IGRmJGl0ZW1fcHJpY2UsCiAgICBkZiRjdXJyZW5jeSA9PSAiQ0FEIiB+IHJvdW5kKGRmJGl0ZW1fcHJpY2UgKiAwLjc5NDU4NSwgZGlnaXRzID0gMiksCiAgICBkZiRjdXJyZW5jeSA9PSAiTVhOIiB+IHJvdW5kKGRmJGl0ZW1fcHJpY2UgKiAwLjA1MTExMSwgZGlnaXRzID0gMikpKQpWaWV3KG5ld19kZikKYGBgCgojIyMjIEZpbmQgYSB1bmlxdWUgaWRlbnRpZmllciBmb3IgY3VzdG9tZXJzIGluIHRoZSByZXBvcnRzOgpBcyBub3RlZCBhYm92ZSwgc2V2ZXJhbCBjb2x1bW5zIGFyZSBubyBsb25nZXIgcG9wdWxhdGVkIHdpdGggZGF0YSBieSBBbWF6b24gb3V0IG9mIGNvbmNlcm5zIGZvciBjdXN0b21lciBwcml2YWN5LCBpbmNsdWRpbmcgYnV5ZXIgbmFtZSwgcGhvbmUgbnVtYmVyIGFuZCBhZGRyZXNzLiBXaXRob3V0IHRoaXMgdW5pcXVlIGlkZW50aWZpZXIgaW5mb3JtYXRpb24sIHdlIHdpbGwgbmVlZCB0byBiZSBjcmVhdGl2ZSB0byBkZXRlcm1pbmUgZWFjaCB1bmlxdWUgY3VzdG9tZXIuIFdlIGNhbiBkbyBzbyBieSByZWZlcnJpbmcgdG8gdGhlIGJ1eWVyIGVtYWlsIGFkZHJlc3MsIGFuIEFtYXpvbi1nZW5lcmF0ZWQgZW1haWwgYWRkcmVzcyB0aGF0IGlzIG5vdCB0aGUgYWN0dWFsIGJ1eWVyJ3MgZW1haWwgYWRkcmVzcywgYnV0IGFuIEFtYXpvbi1zZXJ2ZXItdGllZCBlbWFpbCBhZGRyZXNzIHRoYXQgYXBwZWFycyB0byBiZSBjb25zaXN0ZW50IGFjcm9zcyBvcmRlcnMgd2hlbiB0aGUgc2FtZSB1c2VyIHB1cmNoYXNlcy4gCgpUbyB2ZXJpZnksIHRyeSBzZWFyY2hpbmcgYSBmZXcgb2YgdGhlIHByb3ZpZGVkIEFtYXpvbiBjdXN0b21lciBlbWFpbCBpbiBvdXIgdGFibGUgdG8gc2VlIGlmIHRoZXkgYXJlIHVzZWQgZm9yIG1vcmUgdGhhbiBvbmUgb3JkZXIuIFRoaXMgd291bGQgaW5kaWNhdGUgdGhhdCB0aGUgZW1haWxzIGFyZSBsaW5rZWQgdG8gYSB1bmlxdWUgY3VzdG9tZXJzLCBhbmQgbm90IGNyZWF0ZWQgZm9yIGVhY2ggb3JkZXIuIFdoZW4geW91IGlkZW50aWZ5IGFuIGVtYWlsIHRoYXQgaXMgbGlua2VkIHRvIG1vcmUgdGhhbiBvbmUgb3JkZXIsIHlvdSBjYW4gdmVyaWZ5IGl0IGlzIHRoZSBzYW1lIGN1c3RvbWVyIGJlY2F1c2UgdGhlIGN1c3RvbWVyJ3MgYWRkcmVzcyBjaXR5IGFuZCB6aXAgY29kZSB3aWxsIG1hdGNoLCB0aGlzIGFkZHJlc3MgaW5mb3JtYXRpb24gYmVpbmcgdGhlIGxpbWl0ZWQgY3VzdG9tZXItc3BlY2lmaWMgZGF0YSB0aGF0IHRoZSBBbWF6b24gcmVwb3J0IERPRVMgcHJvdmlkZS4KClRvIHRlc3QsIHRyeSBhIGZldyBlbWFpbHMgZnJvbSB0aGUgdGFibGUgYW5kIHJlcGxhY2UgdGhlbSB3aXRoIHRoZSB0aGUgZW1haWwgaW4gdGhpcyBjb2RlIHNuaXBwZXQ6CmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CmRmICU+JSBmaWx0ZXIoYnV5ZXJfZW1haWw9PSJhYmNkMWFiY2RlNWFiY2RAbWFya2V0cGxhY2UuYW1hem9uLmNvbSIpCmBgYAoKIyMjIyBMZXQncyBpZGVudGlmeSB0aGUgbnVtYmVyIG9mIHVuaXF1ZSBidXllcnMgb3ZlciB0aGUgdGltZSBwZXJpb2Qgb2Ygb3VyIGRhdGEgKDEuNSB5ZWFycykgYnkgY291bnRpbmcgdGhlIHVuaXF1ZSBlbWFpbCBhZGRyZXNzZXM6CmBgYHtyfQp1bmlxdWVfYnV5ZXJzIDwtIGxlbmd0aCh1bmlxdWUobmV3X2RmJGJ1eWVyX2VtYWlsKSkKdW5pcXVlX2J1eWVycwpgYGAKCldlIGhhdmUgYmVlbiBhYmxlIHRvIGRldGVybWluZSB0aGF0IHRoZXJlIGFyZSAzNzc5NiB1bmlxdWUgY3VzdG9tZXJzIHJlcHJlc2VudGVkIGluIG91ciB0YWJsZS4KCiMjIyMgRmluZCB0aGUgdG90YWwgc2FsZXMgb3ZlciB0aGlzIHRpbWUgcGVyaW9kIGJ5IHN1bW1pbmcgdGhlIGl0ZW1fcHJpY2VfVVNEIGNvbHVtbjoKYGBge3J9CnRvdGFsX3NhbGVzIDwtIHN1bShuZXdfZGYkaXRlbV9wcmljZV9VU0QpCnRvdGFsX3NhbGVzCmBgYAoKT3ZlciB0aGlzIHRpbWUgcGVyaW9kIG91ciBleGFtcGxlIEFtYXpvbiBhY2NvdW50IGhhcyBzb2xkIDg3Niw3ODcuMTAgd29ydGggb2YgcHJvZHVjdHMuCgoKIyMjIyBCZWNhdXNlIGRhdGEgaW4gb3VyIHJlcG9ydCBpcyBnaXZlbiBvbiBlYWNoIGl0ZW0gc29sZCBhbmQgbm90IGVhY2ggb3JkZXIsIGxldCB1cyBkZXJpdmUgdGhlIG51bWJlciBvZiB1bmlxdWUgb3JkZXJzIG92ZXIgdGhpcyBwZXJpb2Q6CmBgYHtyfQp1bmlxdWVfb3JkZXJzIDwtIGxlbmd0aCh1bmlxdWUobmV3X2RmJGFtYXpvbl9vcmRlcl9pZCkpCnVuaXF1ZV9vcmRlcnMKYGBgCgpXZSBmaW5kIHRoYXQgb3VyIGV4YW1wbGUgYWNjb3VudCBoYWQgbWFkZSA0Nzc4OCB1bmlxdWUgb3JkZXJzIGR1cmluZyB0aGUgcGVyaW9kIG9mIDEuNSB5ZWFycy4KCiMjIyMgRGV0ZXJtaW5pbmcgdGhlIGF2ZXJhZ2Ugb3JkZXIgdmFsdWU6CmBgYHtyfQphdmdfb3JkZXJfdmFsdWUgPC0gcm91bmQodG90YWxfc2FsZXMvdW5pcXVlX29yZGVycywgZGlnaXRzPTIpCmF2Z19vcmRlcl92YWx1ZQpgYGAKCldlIGZpbmQgdGhhdCB0aGUgYXZlcmFnZSBvcmRlciB2YWx1ZSBmb3IgdGhpcyBhY2NvdW50IGlzICQxOC4zNS4KCiMjIyMgQ2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2Ugb3JkZXIgZnJlcXVlbmN5IGZvciBvdXIgY3VzdG9tZXJzICh0b3RhbCBvcmRlcnMgLyB1bmlxdWUgY3VzdG9tZXJzKToKYGBge3J9Cm9yZGVyX2ZyZXEgPC0gcm91bmQodW5pcXVlX29yZGVycy91bmlxdWVfYnV5ZXJzLCBkaWdpdHM9MikKb3JkZXJfZnJlcQpgYGAKCldlIGZpbmQgdGhhdCB0aGUgY3VzdG9tZXJzIG9mIHRoaXMgQW1hem9uIGJ1c2luZXNzIGhhdmUgYW4gYXZlcmFnZSBvcmRlciBmcmVxdWVuY3kgb2YgMS4yNiBvcmRlcnMgb3ZlciB0aGUgcGVyaW9kIG9mIDEuNSB5ZWFycy4KCiMjIyMgQ2FsY3VsYXRpbmcgdGhlIGF2ZXJhZ2UgY3VzdG9tZXIgdmFsdWUgKGF2ZXJhZ2Ugb3JkZXIgdmFsdWUgLyBvcmRlciBmcmVxdWVuY3kpOgpgYGB7cn0KYXZnX2N1c3RfdmFsdWUgPSByb3VuZChhdmdfb3JkZXJfdmFsdWUqb3JkZXJfZnJlcSwgZGlnaXQ9MikKYXZnX2N1c3RfdmFsdWUKYGBgCgpUaGUgYXZlcmFnZSBjdXN0b21lciB2YWx1ZSBmb3IgdGhlIGdpdmVuIGRhdGEgaXMgMjMuMTIsIGluZGljYXRpbmcgdGhhdCB0aGlzIGlzIHRoZSBhbW91bnQgd2UgZXhwZWN0IG91ciBhdmVyYWdlIGN1c3RvbWVyIHRvIHNwZW5kIG92ZXIgYSBwZXJpb2Qgb2YgMS41IHllYXJzLiBJZiBkYXRhIGZvciBvbmx5IDEgeWVhciB3YXMgc2VsZWN0ZWQsIHRoZW4gdGhpcyBmaWd1cmUgcmVwcmVzZW50cyB0aGUgY3VzdG9tZXIncyB2YWx1ZSBvdmVyIGp1c3Qgb25lIHllYXIuIFRvIGRldGVybWluZSB0aGUgKmxpZmV0aW1lIHZhbHVlKiBvZiBvdXIgY3VzdG9tZXIsIHdlIG1ha2UgYSByZWFzb25hYmxlIGVzdGltYXRlIGdpdmVuIG91ciBrbm93bGVkZ2Ugb2YgdGhlIGJ1c2luZXNzIHRoYXQgdGhlIGF2ZXJhZ2UgY3VzdG9tZXIgZW5nYWdlcyB3aXRoIHRoaXMgYnVzaW5lc3MgZm9yIDMgeWVhcnMuIFRoYXQgd291bGQgbWFrZSB0aGUgdG90YWwgYXZlcmFnZSBsaWZldGltZSBjdXN0b21lciB2YWx1ZSBlcXVhbCB0byAkNDYuMjQuIE91ciBlc3RpbWF0ZSBvZiAzIHllYXJzIHdpbGwgdmFyeSBmcm9tIGJ1c2luZXNzIHRvIGJ1c2luZXNzLCBhbmQgYnVzaW5lc3Mgc3Rha2Vob2xkZXJzIHdpbGwgYmUgaW4gdGhlIGJlc3QgcG9zaXRpb24gdG8gbWFrZSB0aGlzIGRldGVybWluYXRpb24=