Summary
Bellabeat is a high-tech company that manufactures health-focused
smart products.They offer different smart devices that collect data on
activity, sleep, stress, and reproductive health to empower women with
knowledge about their own health and habits.
The main focus of this case is to analyze smart devices fitness data
and determine how it could help unlock new growth opportunities for
Bellabeat. We will focus on one of Bellabeat’s products: Bellabeat
app and Leaf.
The Bellabeat app provides users with health data related to their
activity, sleep, stress, menstrual cycle, and mindfulness habits. This
data can help users better understand their current
The Leaf is Bellabeat’s classic wellness tracker can be worn as a
bracelet, necklace, or clip. The Leaf tracker connects to the Bellabeat
app to track activity, sleep, and stress habits and make healthy
decisions. The Bellabeat app connects to their line of smart wellness
products
Prepare Phase
Dataset used:
The data source used for our case study is FitBit Fitness Tracker
Data. This data set is stored in Kaggle and was made available through
Mobius.
Accessibility and privacy of data:
Verifying the metadata of our data set we can confirm it is
open-source. The owner has dedicated the work to the public domain by
waiving all of his or her rights to the work worldwide under copyright
law, including all related and neighboring rights, to the extent allowed
by law. You can copy, modify, distribute and perform the work, even for
commercial purposes, all without asking permission.
Cleaning the data using Excel
The following steps were taken within each dataset:
- Sorted and filtered data by Id to obtain how many unique users there
were within the dataset.
- Checked for duplicate data using the ‘duplicate data’ tool in
Excel
- Formatted date data into MM/DD/YY date format
- Formatted all numerical data into Number format with either no
decimils or up to 2 decimials.
- Sorted by date to find the first and last date of the dataset (this
is what first indicated only a 31-day period of activity was
captured).
- Separated Date and Hour into two columns when needed for later
analysis. Utilized the ‘Text to Columns’ tool to do so.
- Formatted any time data into 00:00:00 format for consistency.
- Checked Id entries and other columns for LEN to make sure the data
was correct and uniform in length
After the cleaning process was finished, only 3 rows of duplicate
information was found within the Daily_Sleep_Merged file. These were
removed before analysis.
Process phase
In this analysis I will focus on Bigquery SQL and MS excel and to be
able to create data viz for the stakeholders.
Importing dataset
I opened Bigquery Console, then select “Create Project”. Typed down
the name of the project you are going to explore, in this case I used
first-analyst. I created a new dataset for Bellabeat and
named it bellabeat_data. Inside bellabeat dataset, I imported the .csv
datasets I previously downloaded from FitBit Fitness Tracker Data.
- Daily_Activity_Merged
- Daily_Sleep_Merged
- Hourly_Steps_Merged
After that, I started my work by finding the total number of users’
id
Number of users
SELECT
COUNT( DISTINCT Id)
FROM
`first-analyst.bellabeat_data.hourly_steps` -- 33
SELECT
COUNT(DISTINCT Id)
FROM
`first-analyst.bellabeat_data.daily_activity` --33
SELECT
COUNT(DISTINCT Id)
FROM
`first-analyst.bellabeat_data.daily_sleep` -- 24
Checking Start-End Date and Id
SELECT
MIN(Date) as start_date,
MAX(Date) as end_date
FROM
`first-analyst.bellabeat_data.daily_activity`
SELECT
MIN(Date) as start_date,
MAX(Date) as end_date
FROM
`first-analyst.bellabeat_data.daily_sleep`
SELECT
MIN(Date) as start_date,
MAX(Date) as end_date
FROM
`first-analyst.bellabeat_data.hourly_steps`
-- daily activity, daily sleep, hourlysteps are same startdate: 2016-04-12, enddate: 2016-05-12. 31 days in total
Check all ids have the same length
SELECT
Id
FROM
`first-analyst.bellabeat_data.daily_activity`
WHERE
LENGTH(CAST(Id as STRING)) > 10 OR LENGTH(CAST(Id as String)) < 10
-- No data display meaning there are no Id more than or less than to 10
SELECT
Id
FROM
`first-analyst.bellabeat_data.daily_sleep`
WHERE
LENGTH(CAST(Id as STRING)) > 10 OR LENGTH(CAST(Id as String)) < 10
-- No data display meaning there are no Id more than or less than to 10
SELECT
Id
FROM
`first-analyst.bellabeat_data.hourly_steps`
WHERE
LENGTH(CAST(Id as STRING)) > 10 OR LENGTH(CAST(Id as String)) < 10
-- No data display meaning there are no Id more than or less than to 10
It showed that all datasets have the same start and end date: start
2016-04-12 and end 2016-05-12. In term of id’s length, all datasets also
showed the same length: 10 characters.
Cleaning the data
Finding Duplicates
Note: I just repating this below code to check for
daily sleep and hourly steps
SELECT
Id,
Date,
COUNT(*) as num_of_id
FROM
`first-analyst.bellabeat_data.daily_activity`
GROUP BY
Id, Date
HAVING
num_of_id > 1
-- no data to display / no duplicates in daily_activity
SELECT
Id,
Date,
COUNT(*) as num_of_id
FROM
`first-analyst.bellabeat_data.hourly_steps`
GROUP BY
Id, Date
HAVING
num_of_id > 24
-- I put 24 because it is 24 hours in a day / no display no duplicates
SELECT
Id,
Date,
COUNT(*) as num_of_id
FROM
`first-analyst.bellabeat_data.daily_sleep`
GROUP BY
Id, Date
HAVING
num_of_id > 1
-- displays 3 duplicates
According to the result of finding duplicates, it showed that there
are 3 duplicate rows in sleep_day dataset. We need to create a new
sleep_day table, and remove the duplicates in the new table. In this
case, I named the new table: daily_sleep_new.
Duplicate rows in daily_step table need to be removed
Creating and replacing new sleep_day table with all distinct
values
CREATE or REPLACE TABLE `first-analyst.bellabeat_data.daily_sleep_new`
AS SELECT *
FROM
(
SELECT *,
ROW_NUMBER()
OVER (PARTITION BY Id, Date)
row_number
FROM `first-analyst.bellabeat_data.daily_sleep`
)
WHERE row_number = 1
-- Check it again if it the new table had no duplicates
SELECT
Id,
Date,
COUNT(*) as num_of_id
FROM `first-analyst.bellabeat_data.daily_sleep_new`
GROUP BY
Id, Date
HAVING
num_of_id > 1
-- no data display/ no duplicates
Removing the unwanted Data
During the checking and cleaning process, I found that there were
some zero data in TotalSteps column inside the daily_activity dataset.
Therefore, I decided to check and remove those zero value. I created new
table and named it daily_activity_new, so that the previous dataset
still remained.
--Check if total steps = 0 in daily_activity table
SELECT
Id,
Count(*) as num_of_zero_steps
FROM `first-analyst.bellabeat_data.daily_activity`
WHERE
Total_Steps = 0
GROUP BY Id
ORDER BY num_of_zero_steps
-- 15 ids with 0 total steps
-- Create new daily activity table
CREATE TABLE `first-analyst.bellabeat_data.daily_activity_new`
AS SELECT *
FROM `first-analyst.bellabeat_data.daily_activity`
-- Delete all rows that contain zero total steps
DELETE FROM `first-analyst.bellabeat_data.daily_activity_new`
WHERE Total_Steps = 0
--77 data deleted
-- Removing the zero value on Hourly steps
SELECT
Id,
Count(*) as num_of_zero_steps
FROM `first-analyst.bellabeat_data.hourly_steps`
WHERE
Step_Total = 0
GROUP BY Id
ORDER BY num_of_zero_steps
-- 33 ids with 0 total steps
DELETE FROM `first-analyst.bellabeat_data.hourly_steps`
WHERE Step_Total = 0
-- 7535 deleted
Find the null data
--Check for null data
SELECT *
FROM `first-analyst.bellabeat_data.daily_activity_new`
WHERE Id IS NULL
-- no data display
SELECT *
FROM `first-analyst.bellabeat_data.daily_sleep_new`
WHERE Id IS NULL
-- no data display
SELECT *
FROM `first-analyst.bellabeat_data.hourly_steps`
WHERE Id IS NULL
-- no data display
--Delete rows of null data
DELETE FROM `first-analyst.bellabeat_data.daily_activity_new`
WHERE Id IS NULL
Analyze Phase and Share Phase
We were going analyze the trends of FitBit user and to determine if
can help us to make a decision for marketing strategy
User Level
We want to determine the type of users with the data we have because
we don’t have any demographic variables from our sample. We can
categorize users based on their daily number of steps. Users can be
classified as follows:
- Sedentary - Less than 5000 steps a day.
- Lightly active - Between 5000 and 7499 steps a day.
- Fairly active - Between 7500 and 9999 steps a day.
- Very active - More than 10000 steps a day. Classification has been
made per the following article https://www.10000steps.org.au/articles/counting-steps/
--Creating temp table for the mean of daily steps
WITH
daily_average AS (
SELECT
Id,
AVG(Total_Steps) AS totalsteps_mean,
FROM
`first-analyst.bellabeat_data.daily_activity_new`
GROUP BY
Id
ORDER BY
totalsteps_mean
),
--After getting all the total mean, we will now categorize each user base on User Level
users AS (
SELECT
Id,
AVG(totalsteps_mean) as avg_total_steps,
CASE
WHEN AVG(totalsteps_mean) < 5000 THEN 'Sedentary'
WHEN AVG(totalsteps_mean) BETWEEN 5001 AND 7500 THEN 'Lightly Active'
WHEN AVG(totalsteps_mean) BETWEEN 7501 AND 10000 THEN 'Fairly Active'
WHEN AVG(totalsteps_mean) > 10000 THEN 'Very Active'
END AS user_level
FROM daily_average
GROUP BY
Id
ORDER BY avg_total_steps
),
user_level_counts AS (
SELECT user_level, COUNT(*) AS total
FROM users
GROUP BY user_level
),
total_user_level_counts AS (
SELECT SUM(total) AS total_user_level
FROM user_level_counts
),
user_level_percentages AS (
SELECT user_level, CAST(total AS FLOAT64) / total_user_level_counts.total_user_level AS total_percent
FROM user_level_counts, total_user_level_counts
WHERE 1 = 1
)
SELECT user_level,
total_percent,
FROM user_level_percentages
Spreedsheet
data
Steps and minutes asleep per weekday
We want to know now what days of the week are the users more active
and also what days of the week users sleep more. We will also verify if
the users walk the recommended amount of steps and have the recommended
amount of sleep.
Below we are calculating the weekdays based on our column date. We
are also calculating the average steps walked and minutes asleep by
weekday.
WITH
-- Merging two tables
daily_activity_sleep AS (
SELECT
Total_Steps,
TotalMinutesAsleep,
daily_activity_new.Id AS id,
daily_activity_new.Date AS date
FROM `first-analyst.bellabeat_data.daily_activity_new` AS daily_activity_new
INNER JOIN
`first-analyst.bellabeat_data.daily_sleep_new` AS daily_sleep_new
ON
daily_activity_new.Id = daily_sleep_new.Id AND
daily_activity_new.Date = daily_sleep_new.Date
)
--Find the average of Total steps and Total minute asleep per week
SELECT
day_of_week,
ROUND(AVG(Total_Steps),2) as ave_totalsteps_perday,
ROUND(AVG(TotalMinutesAsleep),2) AS ave_minutesasleep_perday
FROM
(
SELECT *,
CASE
WHEN (EXTRACT(DAYOFWEEK FROM date)= 1) THEN 'Mon'
WHEN (EXTRACT(DAYOFWEEK FROM date)= 2) THEN 'Tue'
WHEN (EXTRACT(DAYOFWEEK FROM date)= 3) THEN 'Wed'
WHEN (EXTRACT(DAYOFWEEK FROM date)= 4) THEN 'Thu'
WHEN (EXTRACT(DAYOFWEEK FROM date)= 5) THEN 'Fri'
WHEN (EXTRACT(DAYOFWEEK FROM date)= 6) THEN 'Sat'
WHEN (EXTRACT(DAYOFWEEK FROM date)= 7) THEN 'Sun'
END AS day_of_week
FROM daily_activity_sleep
)
GROUP BY day_of_week
Spreedsheet
data
In the graphs above we can determine the following:
Users walk daily the recommended amount of steps of 7500 besides
Sunday’s. based on the article above Users don’t sleep the recommended
amount of minutes/ hours - 8 hours.
Hourly steps within a day
We were going to find out when the users more active throughout the
day.
Spreedsheet
data
As we can see the graph above user are more active 7:00AM to 9:00PM.
We also user more likely walk more at 11:00AM to 2:00PM, rest at 3:00PM,
and 5:00PM to 7:00PM in the evening.
Correlation
We will now determine if there is any correlation between different
variables: - Daily steps and daily sleep - Daily steps and calories
Spreedsheet
data
Based on our plots:
There’s is no correlation between the daily steps and minutes asleep.
Basically walking daily does not affect the minutes of their sleep But,
there is a correlation between the daily steps and calories. Basically
the more user steps the more they burn calories.
Total days used by users
Now that we have seen some trends in activity, sleep and calories
burned, we want to see how often do the users in our sample use their
device. That way we can plan our marketing strategy and see what
features would benefit the use of smart devices.
We will calculate the number of users that use their smart device on
a daily basis, classifying our sample into three categories knowing that
the date interval is 31 days:
- high use - users who use their device between 21 and 31 days.
- moderate use - users who use their device between 11 and 20
days.
- low use - users who use their device between 1 and 10 days.
First I will make a temp table.
WITH
--Merging two tables with two primary key
daily_activity_and_sleep AS (
SELECT
daily_activity.Id as Id,
COUNT(*) as num_of_use
FROM `first-analyst.bellabeat_data.daily_activity_new` as daily_activity
INNER JOIN `first-analyst.bellabeat_data.daily_sleep_new` as daily_sleep
ON daily_activity.Id = daily_sleep.Id AND daily_activity.Date = daily_sleep.Date
GROUP BY Id
),
#Filtering user usage based on daily sleep and activity of a users
usages AS (
SELECT
Id,
SUM(num_of_use) AS day_used,
CASE
WHEN SUM(num_of_use) BETWEEN 1 AND 10 THEN 'low use'
WHEN SUM(num_of_use) BETWEEN 11 AND 20 THEN 'moderate use'
WHEN SUM(num_of_use) BETWEEN 21 AND 31 THEN 'high use'
END AS usage
FROM daily_activity_and_sleep
GROUP BY Id
),
-- Counting the number of usage
usage_summary AS (
SELECT
usage,
COUNT(*) AS total
FROM usages
GROUP BY usage
),
-- Getting the average of number of usage and total usage
usage_percentage AS (
SELECT
usage,
total,
total_usage,
CAST(total AS FLOAT64) / total_usage AS total_percentage
-- Selecting it FROM usage summary, and finding the total usage
FROM (
SELECT
usage,
total,
SUM(total) OVER () AS total_usage
FROM usage_summary
)
)
SELECT
usage,
total_percentage,
CONCAT(CAST(ROUND(total_percentage * 100, 1) AS INT64), '% (', CAST(total AS INT64), ')') AS labels
FROM usage_percentage
Spreedsheet
data
The results on out graph are: - Between 21 and 31 days, 50% of users
frequently use their phone. - 38% of users balance their phone usage
between 11 and 20 days. - 12% of the users are rarely used their phone 1
to 10 days
Time used smart device
Being more precise we want to see how many minutes do users wear
their device per day. For that we will merge the created daily_use data
frame and daily_activity to be able to filter results by daily use of
device as well.
WITH
--Merging two tables with two primary key
daily_activity_and_sleep AS (
SELECT
daily_activity.Id as Id,
COUNT(*) as num_of_use
FROM `first-analyst.bellabeat_data.daily_activity_new` as daily_activity
INNER JOIN `first-analyst.bellabeat_data.daily_sleep_new` as daily_sleep
ON daily_activity.Id = daily_sleep.Id AND daily_activity.Date = daily_sleep.Date
GROUP BY Id
),
--Filtering user usage based on daily sleep and activity of a users
usages AS (
SELECT
Id,
SUM(num_of_use) AS day_used,
CASE
WHEN SUM(num_of_use) BETWEEN 1 AND 10 THEN 'low use'
WHEN SUM(num_of_use) BETWEEN 11 AND 20 THEN 'moderate use'
WHEN SUM(num_of_use) BETWEEN 21 AND 31 THEN 'high use'
END AS usage
FROM daily_activity_and_sleep
GROUP BY Id
),
-- Counting the number of usage
usage_summary AS (
SELECT
usage,
COUNT(*) AS total
FROM usages
GROUP BY usage
),
-- Getting the average of number of usage and total usage
usage_percentage AS (
SELECT
usage,
total,
total_usage,
CAST(total AS FLOAT64) / total_usage AS total_percentage
-- Selecting it FROM usage summary, and finding the total usage
FROM (
SELECT
usage,
total,
SUM(total) OVER () AS total_usage
FROM usage_summary
)
),
-- Creating new subquery for daily used
daily_activity_used AS (
SELECT *
FROM `first-analyst.bellabeat_data.daily_activity_new`, usage_summary
),
minutes_worn AS (
SELECT *,
CASE
WHEN minutes_worn_percentage = 100 THEN 'All day'
WHEN minutes_worn_percentage >= 50 AND minutes_worn_percentage < 100 THEN 'More than half day'
WHEN minutes_worn_percentage > 0 AND minutes_worn_percentage < 50 THEN 'Less than half day'
END as worn
FROM (
SELECT *,
(Very_Active_Minutes + Fairly_Active_Minutes + Lightly_Active_Minutes +
Sedentary_Minutes) as total_worn_minutes,
(Very_Active_Minutes + Fairly_Active_Minutes + Lightly_Active_Minutes +
Sedentary_Minutes) / 1440 * 100 as minutes_worn_percentage
FROM daily_activity_used
)
),
-- As we have done before, to better visualize our results we will create new subqueries.
worn_summary AS (
SELECT
worn,
COUNT(*) as total
FROM minutes_worn
GROUP BY worn
),
worn_percentage AS (
SELECT
worn,
total,
total_worn,
FROM (
SELECT
worn,
total,
SUM(total) OVER() total_worn
FROM worn_summary
)
),
minutes_worn_highuse AS (
SELECT
worn,
total/totals AS total_percentage,
CONCAT(ROUND(total / totals * 100, 2), '%') AS labels,
FROM (
SELECT
worn,
COUNT(*) AS total,
SUM(COUNT(*)) OVER () AS totals -- Calculating the sum of the number of rows (which is total)
FROM minutes_worn
WHERE usage = 'high use'
GROUP BY worn
)
GROUP BY worn, total_percentage,labels
),
minutes_worn_moderateuse AS (
SELECT
worn,
total / totals as total_percentage,
CONCAT(ROUND(total/totals * 100, 2), '%') as labels
FROM (
SELECT
worn,
COUNT(*) as total,
SUM(COUNT(*)) OVER() as totals
FROM minutes_worn
WHERE usage = 'moderate use'
GROUP BY worn
)
GROUP BY worn, total_percentage,labels
),
minutes_worn_lowuse AS (
SELECT
worn,
total / totals as total_percentage,
CONCAT(ROUND(total/totals * 100, 2), '%') as labels
FROM (
SELECT
worn,
COUNT(*) as total,
SUM(COUNT(*)) OVER() as totals
FROM minutes_worn
WHERE usage = 'low use'
GROUP BY worn
)
GROUP BY worn, total_percentage,labels
)
-- SHOWING THE TOTAL USAGE OF ALL USER
-- SELECT
-- worn,
-- total_percentage,
-- CONCAT(CAST(ROUND(total_percentage * 100, 1) AS INT64), '% (', CAST(total AS INT64), ')') AS labels
-- FROM worn_percentage
SELECT
*
FROM minutes_worn_lowuse
Conclusion and Recommendation
Based on our findings, I would recommend that we use our own tracking
data for further analysis in order to respond to our business task and
assist Bellabeat with their mission. Because we didn’t have any
demographic information about users, the data sets we used had a small
sample size and could be biased. Knowing that our primary demographic is
young and adult women, I would encourage you to continue looking for
trends in order to develop a marketing strategy centered on them.
That being said, after our analysis we have found different trends
that may help our online campaign and improve Bellabeat app:
library(knitr)
recommendation <- c("Adding a 'goal' on the app", "Sleeping recommendation for users", "Level or rank badge system")
description <- c("Putting a goal to track daily steps and calories helps users monitor their progress towards their fitness goals.",
"A sleep recommendation feature can remind users to maintain a healthy sleep routine and provide helpful tips for better sleep.",
"A level or rank system can motivate users to be more active by allowing them to track their progress and earn badges as rewards.")
tbl <- data.frame(Recommendation = recommendation, Description = description)
kable(tbl)
| Adding a ‘goal’ on the app |
Putting a goal to track daily steps and calories helps
users monitor their progress towards their fitness goals. |
| Sleeping recommendation for users |
A sleep recommendation feature can remind users to
maintain a healthy sleep routine and provide helpful tips for better
sleep. |
| Level or rank badge system |
A level or rank system can motivate users to be more
active by allowing them to track their progress and earn badges as
rewards. |
NA
NA
On our analysis we dint just check for the trends but also we see
that healthy users tend to be use their device and 36% of the users wear
the device all time.
LS0tDQp0aXRsZTogIkNhcHN0b25lOiBCZWxsYWJlYXQgQ2FzZSBTdHVkeSBTUUwiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KX09uIHRoaXMgQ2FzZSBTdHVkeSBJIHVzZWQgdGhlIHNhbWUgZGF0YXNldCBvbiBteSBwcmV2aW91cyB3b3JrIFtDYXBzdG9uZTogQmVsbGFiZWF0IENhc2UgU3R1ZHkgd2l0aCBSXShodHRwczovL3d3dy5rYWdnbGUuY29tL2NvZGUvam9uZGVyZWNrbmlmYXMvY2Fwc3RvbmUtYmVsbGFiZWF0LWNhc2Utc3R1ZHkvKSAgYnV0IHRoaXMgdGltZSBJbSB1c2luZyBzb2xleSBvbiBTUUwgYW5kIEV4Y2VsIHRvIHNob3djYXNlIG15IHNraWxsLl8NCg0KIVtBbHQgdGV4dF0oaHR0cHM6Ly9wb2x5Z3Jvd3RoLmlvL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDIyLzAxL0JlbGxhYmVhdC1jYXNlLXN0dWR5LTEyMDB4Njc1LnBuZykgPC9icj4NCg0KDQojIFN1bW1hcnkNCkJlbGxhYmVhdCBpcyBhIGhpZ2gtdGVjaCBjb21wYW55IHRoYXQgbWFudWZhY3R1cmVzIGhlYWx0aC1mb2N1c2VkIHNtYXJ0IHByb2R1Y3RzLlRoZXkgb2ZmZXIgZGlmZmVyZW50IHNtYXJ0IGRldmljZXMgdGhhdCBjb2xsZWN0IGRhdGEgb24gYWN0aXZpdHksIHNsZWVwLCBzdHJlc3MsIGFuZCByZXByb2R1Y3RpdmUgaGVhbHRoIHRvIGVtcG93ZXIgd29tZW4gd2l0aCBrbm93bGVkZ2UgYWJvdXQgdGhlaXIgb3duIGhlYWx0aCBhbmQgaGFiaXRzLg0KDQpUaGUgbWFpbiBmb2N1cyBvZiB0aGlzIGNhc2UgaXMgdG8gYW5hbHl6ZSBzbWFydCBkZXZpY2VzIGZpdG5lc3MgZGF0YSBhbmQgZGV0ZXJtaW5lIGhvdyBpdCBjb3VsZCBoZWxwIHVubG9jayBuZXcgZ3Jvd3RoIG9wcG9ydHVuaXRpZXMgZm9yIEJlbGxhYmVhdC4gV2Ugd2lsbCBmb2N1cyBvbiBvbmUgb2YgQmVsbGFiZWF04oCZcyBwcm9kdWN0czogKkJlbGxhYmVhdCBhcHAgYW5kIExlYWYqLg0KDQpUaGUgQmVsbGFiZWF0IGFwcCBwcm92aWRlcyB1c2VycyB3aXRoIGhlYWx0aCBkYXRhIHJlbGF0ZWQgdG8gdGhlaXIgYWN0aXZpdHksIHNsZWVwLCBzdHJlc3MsIG1lbnN0cnVhbCBjeWNsZSwgYW5kIG1pbmRmdWxuZXNzIGhhYml0cy4gVGhpcyBkYXRhIGNhbiBoZWxwIHVzZXJzIGJldHRlciB1bmRlcnN0YW5kIHRoZWlyIGN1cnJlbnQgDQoNClRoZSBMZWFmIGlzIEJlbGxhYmVhdOKAmXMgY2xhc3NpYyB3ZWxsbmVzcyB0cmFja2VyIGNhbiBiZSB3b3JuIGFzIGEgYnJhY2VsZXQsIG5lY2tsYWNlLCBvciBjbGlwLiBUaGUgTGVhZiB0cmFja2VyIGNvbm5lY3RzDQp0byB0aGUgQmVsbGFiZWF0IGFwcCB0byB0cmFjayBhY3Rpdml0eSwgc2xlZXAsIGFuZCBzdHJlc3MgaGFiaXRzIGFuZCBtYWtlIGhlYWx0aHkgZGVjaXNpb25zLiBUaGUgQmVsbGFiZWF0IGFwcCBjb25uZWN0cyB0byB0aGVpciBsaW5lIG9mIHNtYXJ0IHdlbGxuZXNzIHByb2R1Y3RzDQoNCiMgQXNrIFBoYXNlIA0KIyMgQnVzaW5lc3MgVGFzayANCklkZW50aWZ5IHRyZW5kcyBpbiBob3cgY29uc3VtZXJzIHVzZSBub24tQmVsbGFiZWF0IHNtYXJ0IGRldmljZXMgdG8gYXBwbHkgaW5zaWdodHMgaW50byBCZWxsYWJlYXTigJlzIG1hcmtldGluZyBzdHJhdGVneS4NCg0KIyMgU3Rha2Vob2xkZXJzDQoNCiogVXLFoWthIFNyxaFlbiAtIEJlbGxhYmVhdCBjb2ZvdW5kZXIgYW5kIENoaWVmIENyZWF0aXZlIE9mZmljZXINCiogU2FuZG8gTXVyIC0gQmVsbGFiZWF0IGNvZm91bmRlciBhbmQga2V5IG1lbWJlciBvZiBCZWxsYWJlYXQgZXhlY3V0aXZlIHRlYW0NCiogQmVsbGFiZWF0IE1hcmtldGluZyBBbmFseXRpY3MgdGVhbQ0KDQojIFByZXBhcmUgUGhhc2UgDQojIyBEYXRhc2V0IHVzZWQ6IA0KVGhlIGRhdGEgc291cmNlIHVzZWQgZm9yIG91ciBjYXNlIHN0dWR5IGlzIEZpdEJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YS4gVGhpcyBkYXRhIHNldCBpcyBzdG9yZWQgaW4gS2FnZ2xlIGFuZCB3YXMgbWFkZSBhdmFpbGFibGUgdGhyb3VnaCBNb2JpdXMuDQoNCiMjIEFjY2Vzc2liaWxpdHkgYW5kIHByaXZhY3kgb2YgZGF0YTogDQpWZXJpZnlpbmcgdGhlIG1ldGFkYXRhIG9mIG91ciBkYXRhIHNldCB3ZSBjYW4gY29uZmlybSBpdCBpcyBvcGVuLXNvdXJjZS4gVGhlIG93bmVyIGhhcyBkZWRpY2F0ZWQgdGhlIHdvcmsgdG8gdGhlIHB1YmxpYyBkb21haW4gYnkgd2FpdmluZyBhbGwgb2YgaGlzIG9yIGhlciByaWdodHMgdG8gdGhlIHdvcmsgd29ybGR3aWRlIHVuZGVyIGNvcHlyaWdodCBsYXcsIGluY2x1ZGluZyBhbGwgcmVsYXRlZCBhbmQgbmVpZ2hib3JpbmcgcmlnaHRzLCB0byB0aGUgZXh0ZW50IGFsbG93ZWQgYnkgbGF3LiBZb3UgY2FuIGNvcHksIG1vZGlmeSwgZGlzdHJpYnV0ZSBhbmQgcGVyZm9ybSB0aGUgd29yaywgZXZlbiBmb3IgY29tbWVyY2lhbCBwdXJwb3NlcywgYWxsIHdpdGhvdXQgYXNraW5nIHBlcm1pc3Npb24uDQoNCiMjIEluZm9ybWF0aW9uIGFib3V0IG91ciBkYXRhc2V0Og0KVGhlc2UgZGF0YSBzZXRzIHdlcmUgZ2VuZXJhdGVkIGJ5IHJlc3BvbmRlbnRzIHRvIGEgZGlzdHJpYnV0ZWQgc3VydmV5IHZpYSBBbWF6b24gTWVjaGFuaWNhbCBUdXJrIGJldHdlZW4gMDMuMTIuMjAxNi0wNS4xMi4yMDE2LiBUaGlydHkgZWxpZ2libGUgRml0Yml0IHVzZXJzIGNvbnNlbnRlZCB0byB0aGUgc3VibWlzc2lvbiBvZiBwZXJzb25hbCB0cmFja2VyIGRhdGEsIGluY2x1ZGluZyBtaW51dGUtbGV2ZWwgb3V0cHV0IGZvciBwaHlzaWNhbCBhY3Rpdml0eSwgaGVhcnQgcmF0ZSwgYW5kIHNsZWVwIG1vbml0b3JpbmcuIFZhcmlhdGlvbiBiZXR3ZWVuIG91dHB1dCByZXByZXNlbnRzIHVzZSBvZiBkaWZmZXJlbnQgdHlwZXMgb2YgRml0Yml0IHRyYWNrZXJzIGFuZCBpbmRpdmlkdWFsIHRyYWNraW5nIGJlaGF2aW9ycyAvIHByZWZlcmVuY2VzLg0KDQojIyBDbGVhbmluZyB0aGUgZGF0YSB1c2luZyBFeGNlbA0KVGhlIGZvbGxvd2luZyBzdGVwcyB3ZXJlIHRha2VuIHdpdGhpbiBlYWNoIGRhdGFzZXQ6DQoNCi0gU29ydGVkIGFuZCBmaWx0ZXJlZCBkYXRhIGJ5IElkIHRvIG9idGFpbiBob3cgbWFueSB1bmlxdWUgdXNlcnMgdGhlcmUgd2VyZSB3aXRoaW4gdGhlIGRhdGFzZXQuDQotIENoZWNrZWQgZm9yIGR1cGxpY2F0ZSBkYXRhIHVzaW5nIHRoZSAnZHVwbGljYXRlIGRhdGEnIHRvb2wgaW4gRXhjZWwNCi0gRm9ybWF0dGVkIGRhdGUgZGF0YSBpbnRvIE1NL0REL1lZIGRhdGUgZm9ybWF0DQotIEZvcm1hdHRlZCBhbGwgbnVtZXJpY2FsIGRhdGEgaW50byBOdW1iZXIgZm9ybWF0IHdpdGggZWl0aGVyIG5vIGRlY2ltaWxzIG9yIHVwIHRvIDIgZGVjaW1pYWxzLg0KLSBTb3J0ZWQgYnkgZGF0ZSB0byBmaW5kIHRoZSBmaXJzdCBhbmQgbGFzdCBkYXRlIG9mIHRoZSBkYXRhc2V0ICh0aGlzIGlzIHdoYXQgZmlyc3QgaW5kaWNhdGVkIG9ubHkgYSAzMS1kYXkgcGVyaW9kIG9mIGFjdGl2aXR5IHdhcyBjYXB0dXJlZCkuDQotIFNlcGFyYXRlZCBEYXRlIGFuZCBIb3VyIGludG8gdHdvIGNvbHVtbnMgd2hlbiBuZWVkZWQgZm9yIGxhdGVyIGFuYWx5c2lzLiBVdGlsaXplZCB0aGUgJ1RleHQgdG8gQ29sdW1ucycgdG9vbCB0byBkbyBzby4NCi0gRm9ybWF0dGVkIGFueSB0aW1lIGRhdGEgaW50byAwMDowMDowMCBmb3JtYXQgZm9yIGNvbnNpc3RlbmN5Lg0KLSBDaGVja2VkIElkIGVudHJpZXMgYW5kIG90aGVyIGNvbHVtbnMgZm9yIExFTiB0byBtYWtlIHN1cmUgdGhlIGRhdGEgd2FzIGNvcnJlY3QgYW5kIHVuaWZvcm0gaW4gbGVuZ3RoDQoNCkFmdGVyIHRoZSBjbGVhbmluZyBwcm9jZXNzIHdhcyBmaW5pc2hlZCwgb25seSAzIHJvd3Mgb2YgZHVwbGljYXRlIGluZm9ybWF0aW9uIHdhcyBmb3VuZCB3aXRoaW4gdGhlIERhaWx5X1NsZWVwX01lcmdlZCBmaWxlLiBUaGVzZSB3ZXJlIHJlbW92ZWQgYmVmb3JlIGFuYWx5c2lzLg0KDQojIFByb2Nlc3MgcGhhc2UNCkluIHRoaXMgYW5hbHlzaXMgSSB3aWxsIGZvY3VzIG9uIEJpZ3F1ZXJ5IFNRTCBhbmQgTVMgZXhjZWwgYW5kIHRvIGJlIGFibGUgdG8gY3JlYXRlIGRhdGEgdml6IGZvciB0aGUgc3Rha2Vob2xkZXJzLg0KDQojIyMgSW1wb3J0aW5nIGRhdGFzZXQNCiBJIG9wZW5lZCBCaWdxdWVyeSBDb25zb2xlLCB0aGVuIHNlbGVjdCAiQ3JlYXRlIFByb2plY3QiLiBUeXBlZCBkb3duIHRoZSBuYW1lIG9mIHRoZSBwcm9qZWN0IHlvdSBhcmUgZ29pbmcgdG8gZXhwbG9yZSwgaW4gdGhpcyBjYXNlIEkgdXNlZCBgZmlyc3QtYW5hbHlzdGAuIEkgY3JlYXRlZCBhIG5ldyBkYXRhc2V0IGZvciBCZWxsYWJlYXQgYW5kIG5hbWVkIGl0IGJlbGxhYmVhdF9kYXRhLiBJbnNpZGUgYmVsbGFiZWF0IGRhdGFzZXQsIEkgaW1wb3J0ZWQgdGhlIC5jc3YgZGF0YXNldHMgSSBwcmV2aW91c2x5IGRvd25sb2FkZWQgZnJvbSBGaXRCaXQgRml0bmVzcyBUcmFja2VyIERhdGEuIA0KIA0KIC0gRGFpbHlfQWN0aXZpdHlfTWVyZ2VkDQogLSBEYWlseV9TbGVlcF9NZXJnZWQNCiAtIEhvdXJseV9TdGVwc19NZXJnZWQNCiANCkFmdGVyIHRoYXQsIEkgc3RhcnRlZCBteSB3b3JrIGJ5IGZpbmRpbmcgdGhlIHRvdGFsIG51bWJlciBvZiB1c2VycycgaWQNCg0KIyMjIE51bWJlciBvZiB1c2Vycw0KYGBge3NxbCBjb25uZWN0aW9uPX0NCg0KU0VMRUNUIA0KICBDT1VOVCggRElTVElOQ1QgSWQpDQpGUk9NDQogIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmhvdXJseV9zdGVwc2AgLS0gMzMNCg0KDQpTRUxFQ1QgDQogIENPVU5UKERJU1RJTkNUIElkKQ0KRlJPTSANCiAgYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuZGFpbHlfYWN0aXZpdHlgIC0tMzMNCiAgDQogIA0KU0VMRUNUIA0KICBDT1VOVChESVNUSU5DVCBJZCkNCkZST00gDQogIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwYCAtLSAyNA0KYGBgDQoNCg0KIyMjIENoZWNraW5nIFN0YXJ0LUVuZCBEYXRlIGFuZCBJZCANCmBgYHtzcWwgY29ubmVjdGlvbj19DQoNClNFTEVDVCANCiAgTUlOKERhdGUpIGFzIHN0YXJ0X2RhdGUsDQogIE1BWChEYXRlKSBhcyBlbmRfZGF0ZQ0KRlJPTSANCiAgYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuZGFpbHlfYWN0aXZpdHlgDQoNClNFTEVDVCANCiAgTUlOKERhdGUpIGFzIHN0YXJ0X2RhdGUsDQogIE1BWChEYXRlKSBhcyBlbmRfZGF0ZQ0KRlJPTSANCiAgYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuZGFpbHlfc2xlZXBgDQoNClNFTEVDVCANCiAgTUlOKERhdGUpIGFzIHN0YXJ0X2RhdGUsDQogIE1BWChEYXRlKSBhcyBlbmRfZGF0ZQ0KRlJPTSANCiAgYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuaG91cmx5X3N0ZXBzYA0KDQogIC0tIGRhaWx5IGFjdGl2aXR5LCBkYWlseSBzbGVlcCwgaG91cmx5c3RlcHMgYXJlIHNhbWUgc3RhcnRkYXRlOiAyMDE2LTA0LTEyLCBlbmRkYXRlOiAyMDE2LTA1LTEyLiAzMSBkYXlzIGluIHRvdGFsDQoNCmBgYA0KIyMjIENoZWNrIGFsbCBpZHMgaGF2ZSB0aGUgc2FtZSBsZW5ndGgNCg0KYGBge3NxbCBjb25uZWN0aW9uPX0NClNFTEVDVCANCiAgSWQNCkZST00gDQogIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5YA0KV0hFUkUNCiAgTEVOR1RIKENBU1QoSWQgYXMgU1RSSU5HKSkgPiAxMCBPUiBMRU5HVEgoQ0FTVChJZCBhcyBTdHJpbmcpKSA8IDEwDQoNCiAgLS0gTm8gZGF0YSBkaXNwbGF5IG1lYW5pbmcgdGhlcmUgYXJlIG5vIElkIG1vcmUgdGhhbiBvciBsZXNzIHRoYW4gdG8gMTANCiAgDQogIFNFTEVDVCANCiAgSWQNCkZST00gDQogIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwYA0KV0hFUkUNCiAgTEVOR1RIKENBU1QoSWQgYXMgU1RSSU5HKSkgPiAxMCBPUiBMRU5HVEgoQ0FTVChJZCBhcyBTdHJpbmcpKSA8IDEwDQoNCiAgLS0gTm8gZGF0YSBkaXNwbGF5IG1lYW5pbmcgdGhlcmUgYXJlIG5vIElkIG1vcmUgdGhhbiBvciBsZXNzIHRoYW4gdG8gMTANCg0KDQpTRUxFQ1QgDQogIElkDQpGUk9NIA0KICBgZmlyc3QtYW5hbHlzdC5iZWxsYWJlYXRfZGF0YS5ob3VybHlfc3RlcHNgDQpXSEVSRQ0KICBMRU5HVEgoQ0FTVChJZCBhcyBTVFJJTkcpKSA+IDEwIE9SIExFTkdUSChDQVNUKElkIGFzIFN0cmluZykpIDwgMTANCg0KICAtLSBObyBkYXRhIGRpc3BsYXkgbWVhbmluZyB0aGVyZSBhcmUgbm8gSWQgbW9yZSB0aGFuIG9yIGxlc3MgdGhhbiB0byAxMA0KDQoNCg0KDQpgYGANCg0KSXQgc2hvd2VkIHRoYXQgYWxsIGRhdGFzZXRzIGhhdmUgdGhlIHNhbWUgc3RhcnQgYW5kIGVuZCBkYXRlOiBzdGFydCAyMDE2LTA0LTEyIGFuZCBlbmQgMjAxNi0wNS0xMi4gSW4gdGVybSBvZiBpZCdzIGxlbmd0aCwgYWxsIGRhdGFzZXRzIGFsc28gc2hvd2VkIHRoZSBzYW1lIGxlbmd0aDogMTAgY2hhcmFjdGVycy4NCg0KIyMgQ2xlYW5pbmcgdGhlIGRhdGENCg0KDQojIyMgRmluZGluZyBEdXBsaWNhdGVzDQoqKk5vdGU6KiogIEkganVzdCByZXBhdGluZyB0aGlzIGJlbG93IGNvZGUgdG8gY2hlY2sgZm9yIGRhaWx5IHNsZWVwIGFuZCBob3VybHkgc3RlcHMNCmBgYHtzcWwgY29ubmVjdGlvbj19DQpTRUxFQ1QgDQogIElkLA0KICBEYXRlLA0KICBDT1VOVCgqKSBhcyBudW1fb2ZfaWQNCkZST00gDQogIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5YA0KR1JPVVAgQlkNCiAgSWQsIERhdGUNCkhBVklORyANCiAgbnVtX29mX2lkID4gMQ0KDQotLSBubyBkYXRhIHRvIGRpc3BsYXkgLyBubyBkdXBsaWNhdGVzIGluIGRhaWx5X2FjdGl2aXR5DQogIA0KU0VMRUNUIA0KICBJZCwNCiAgRGF0ZSwNCiAgQ09VTlQoKikgYXMgbnVtX29mX2lkDQpGUk9NIA0KICBgZmlyc3QtYW5hbHlzdC5iZWxsYWJlYXRfZGF0YS5ob3VybHlfc3RlcHNgDQpHUk9VUCBCWQ0KICBJZCwgRGF0ZQ0KSEFWSU5HIA0KICBudW1fb2ZfaWQgPiAyNA0KLS0gSSBwdXQgMjQgYmVjYXVzZSBpdCBpcyAyNCBob3VycyBpbiBhIGRheSAvIG5vIGRpc3BsYXkgbm8gZHVwbGljYXRlcw0KICANCiAgDQpTRUxFQ1QgDQogIElkLA0KICBEYXRlLA0KICBDT1VOVCgqKSBhcyBudW1fb2ZfaWQNCkZST00gDQogIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwYA0KR1JPVVAgQlkNCiAgSWQsIERhdGUNCkhBVklORyANCiAgbnVtX29mX2lkID4gMQ0KDQotLSBkaXNwbGF5cyAzIGR1cGxpY2F0ZXMNCg0KDQpgYGANCkFjY29yZGluZyB0byB0aGUgcmVzdWx0IG9mIGZpbmRpbmcgZHVwbGljYXRlcywgaXQgc2hvd2VkIHRoYXQgdGhlcmUgYXJlIDMgZHVwbGljYXRlIHJvd3MgaW4gc2xlZXBfZGF5IGRhdGFzZXQuIFdlIG5lZWQgdG8gY3JlYXRlIGEgbmV3IHNsZWVwX2RheSB0YWJsZSwgYW5kIHJlbW92ZSB0aGUgZHVwbGljYXRlcyBpbiB0aGUgbmV3IHRhYmxlLiBJbiB0aGlzIGNhc2UsIEkgbmFtZWQgdGhlIG5ldyB0YWJsZTogZGFpbHlfc2xlZXBfbmV3Lg0KDQojIyBEdXBsaWNhdGUgcm93cyBpbiBkYWlseV9zdGVwIHRhYmxlIG5lZWQgdG8gYmUgcmVtb3ZlZA0KQ3JlYXRpbmcgYW5kIHJlcGxhY2luZyBuZXcgc2xlZXBfZGF5IHRhYmxlIHdpdGggYWxsIGRpc3RpbmN0IHZhbHVlcw0KDQpgYGB7c3FsIGNvbm5lY3Rpb249fQ0KQ1JFQVRFIG9yIFJFUExBQ0UgVEFCTEUgYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuZGFpbHlfc2xlZXBfbmV3YA0KQVMgU0VMRUNUICoNCkZST00NCigNCiAgU0VMRUNUICosIA0KICBST1dfTlVNQkVSKCkgDQogIE9WRVIgKFBBUlRJVElPTiBCWSBJZCwgRGF0ZSkNCiAgcm93X251bWJlcg0KICBGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwYA0KKQ0KV0hFUkUgcm93X251bWJlciA9IDENCg0KDQotLSBDaGVjayBpdCBhZ2FpbiBpZiBpdCB0aGUgbmV3IHRhYmxlIGhhZCBubyBkdXBsaWNhdGVzDQoNClNFTEVDVA0KICBJZCwNCiAgRGF0ZSwNCiAgQ09VTlQoKikgYXMgbnVtX29mX2lkDQpGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwX25ld2ANCkdST1VQIEJZDQogIElkLCBEYXRlDQpIQVZJTkcgDQogIG51bV9vZl9pZCA+IDENCg0KICAtLSBubyBkYXRhIGRpc3BsYXkvIG5vIGR1cGxpY2F0ZXMNCmBgYA0KDQojIyMgUmVtb3ZpbmcgdGhlIHVud2FudGVkIERhdGEgDQpEdXJpbmcgdGhlIGNoZWNraW5nIGFuZCBjbGVhbmluZyBwcm9jZXNzLCBJIGZvdW5kIHRoYXQgdGhlcmUgd2VyZSBzb21lIHplcm8gZGF0YSBpbiBUb3RhbFN0ZXBzIGNvbHVtbiBpbnNpZGUgdGhlIGRhaWx5X2FjdGl2aXR5IGRhdGFzZXQuIFRoZXJlZm9yZSwgSSBkZWNpZGVkIHRvIGNoZWNrIGFuZCByZW1vdmUgdGhvc2UgemVybyB2YWx1ZS4gSSBjcmVhdGVkIG5ldyB0YWJsZSBhbmQgbmFtZWQgaXQgZGFpbHlfYWN0aXZpdHlfbmV3LCBzbyB0aGF0IHRoZSBwcmV2aW91cyBkYXRhc2V0IHN0aWxsIHJlbWFpbmVkLg0KDQpgYGB7c3FsIGNvbm5lY3Rpb249fQ0KLS1DaGVjayBpZiB0b3RhbCBzdGVwcyA9IDAgaW4gZGFpbHlfYWN0aXZpdHkgdGFibGUNClNFTEVDVCANCiAgSWQsIA0KICBDb3VudCgqKSBhcyBudW1fb2ZfemVyb19zdGVwcw0KRlJPTSBgZmlyc3QtYW5hbHlzdC5iZWxsYWJlYXRfZGF0YS5kYWlseV9hY3Rpdml0eWANCldIRVJFIA0KICBUb3RhbF9TdGVwcyA9IDANCkdST1VQIEJZIElkDQpPUkRFUiBCWSBudW1fb2ZfemVyb19zdGVwcw0KDQogIC0tIDE1IGlkcyB3aXRoIDAgdG90YWwgc3RlcHMNCg0KLS0gQ3JlYXRlIG5ldyBkYWlseSBhY3Rpdml0eSB0YWJsZQ0KQ1JFQVRFIFRBQkxFIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2ANCkFTIFNFTEVDVCAqDQpGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5YA0KDQoNCi0tIERlbGV0ZSBhbGwgcm93cyB0aGF0IGNvbnRhaW4gemVybyB0b3RhbCBzdGVwcw0KREVMRVRFIEZST00gYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuZGFpbHlfYWN0aXZpdHlfbmV3YA0KV0hFUkUgVG90YWxfU3RlcHMgPSAwDQotLTc3IGRhdGEgZGVsZXRlZA0KDQoNCi0tIFJlbW92aW5nIHRoZSB6ZXJvIHZhbHVlIG9uIEhvdXJseSBzdGVwcw0KU0VMRUNUIA0KICBJZCwgDQogIENvdW50KCopIGFzIG51bV9vZl96ZXJvX3N0ZXBzDQpGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmhvdXJseV9zdGVwc2ANCldIRVJFIA0KICBTdGVwX1RvdGFsID0gMA0KR1JPVVAgQlkgSWQNCk9SREVSIEJZIG51bV9vZl96ZXJvX3N0ZXBzDQotLSAzMyBpZHMgd2l0aCAwIHRvdGFsIHN0ZXBzDQoNCkRFTEVURSBGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmhvdXJseV9zdGVwc2ANCldIRVJFIFN0ZXBfVG90YWwgPSAwDQotLSA3NTM1IGRlbGV0ZWQNCmBgYA0KIyMjIEZpbmQgdGhlIG51bGwgZGF0YQ0KDQpgYGB7c3FsIGNvbm5lY3Rpb249fQ0KDQotLUNoZWNrIGZvciBudWxsIGRhdGENClNFTEVDVCAqDQpGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2ANCldIRVJFIElkIElTIE5VTEwNCi0tIG5vIGRhdGEgZGlzcGxheQ0KDQpTRUxFQ1QgKg0KRlJPTSBgZmlyc3QtYW5hbHlzdC5iZWxsYWJlYXRfZGF0YS5kYWlseV9zbGVlcF9uZXdgDQpXSEVSRSBJZCBJUyBOVUxMDQotLSBubyBkYXRhIGRpc3BsYXkNCg0KU0VMRUNUICoNCkZST00gYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuaG91cmx5X3N0ZXBzYA0KV0hFUkUgSWQgSVMgTlVMTA0KLS0gbm8gZGF0YSBkaXNwbGF5DQoNCg0KLS1EZWxldGUgcm93cyBvZiBudWxsIGRhdGENCkRFTEVURSBGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2ANCldIRVJFIElkIElTIE5VTEwNCg0KDQpgYGANCiMgQW5hbHl6ZSBQaGFzZSBhbmQgU2hhcmUgUGhhc2UNCldlIHdlcmUgZ29pbmcgYW5hbHl6ZSB0aGUgdHJlbmRzIG9mIEZpdEJpdCB1c2VyIGFuZCB0byBkZXRlcm1pbmUgaWYgY2FuIGhlbHAgdXMgdG8gbWFrZSBhIGRlY2lzaW9uIGZvciBtYXJrZXRpbmcgc3RyYXRlZ3kNCg0KIyMjIFVzZXIgTGV2ZWwNCldlIHdhbnQgdG8gZGV0ZXJtaW5lIHRoZSB0eXBlIG9mIHVzZXJzIHdpdGggdGhlIGRhdGEgd2UgaGF2ZSBiZWNhdXNlIHdlIGRvbuKAmXQgaGF2ZSBhbnkgZGVtb2dyYXBoaWMgdmFyaWFibGVzIGZyb20gb3VyIHNhbXBsZS4gV2UgY2FuIGNhdGVnb3JpemUgdXNlcnMgYmFzZWQgb24gdGhlaXIgZGFpbHkgbnVtYmVyIG9mIHN0ZXBzLiBVc2VycyBjYW4gYmUgY2xhc3NpZmllZCBhcyBmb2xsb3dzOg0KDQoqIFNlZGVudGFyeSAtIExlc3MgdGhhbiA1MDAwIHN0ZXBzIGEgZGF5LiANCiogTGlnaHRseSBhY3RpdmUgLSBCZXR3ZWVuIDUwMDAgYW5kIDc0OTkgc3RlcHMgYSBkYXkuIA0KKiBGYWlybHkgYWN0aXZlIC0gQmV0d2VlbiA3NTAwIGFuZCA5OTk5IHN0ZXBzIGEgZGF5LiANCiogVmVyeSBhY3RpdmUgLSBNb3JlIHRoYW4gMTAwMDAgc3RlcHMgYSBkYXkuIA0KQ2xhc3NpZmljYXRpb24gaGFzIGJlZW4gbWFkZSBwZXIgdGhlIGZvbGxvd2luZyBhcnRpY2xlIGh0dHBzOi8vd3d3LjEwMDAwc3RlcHMub3JnLmF1L2FydGljbGVzL2NvdW50aW5nLXN0ZXBzLw0KDQpgYGB7c3FsIGNvbm5lY3Rpb249fQ0KLS1DcmVhdGluZyB0ZW1wIHRhYmxlIGZvciB0aGUgbWVhbiBvZiBkYWlseSBzdGVwcw0KV0lUSA0KICBkYWlseV9hdmVyYWdlIEFTICgNCiAgU0VMRUNUDQogICAgSWQsDQogICAgQVZHKFRvdGFsX1N0ZXBzKSBBUyB0b3RhbHN0ZXBzX21lYW4sDQogIEZST00NCiAgICBgZmlyc3QtYW5hbHlzdC5iZWxsYWJlYXRfZGF0YS5kYWlseV9hY3Rpdml0eV9uZXdgDQogIEdST1VQIEJZDQogICAgSWQNCiAgT1JERVIgQlkNCiAgICB0b3RhbHN0ZXBzX21lYW4gIA0KICANCiksDQotLUFmdGVyIGdldHRpbmcgYWxsIHRoZSB0b3RhbCBtZWFuLCB3ZSB3aWxsIG5vdyBjYXRlZ29yaXplIGVhY2ggdXNlciBiYXNlIG9uIFVzZXIgTGV2ZWwgDQogdXNlcnMgQVMgKA0KU0VMRUNUIA0KICBJZCwgDQogIEFWRyh0b3RhbHN0ZXBzX21lYW4pIGFzIGF2Z190b3RhbF9zdGVwcywNCiAgQ0FTRQ0KICBXSEVOIEFWRyh0b3RhbHN0ZXBzX21lYW4pIDwgNTAwMCBUSEVOICdTZWRlbnRhcnknDQogIFdIRU4gQVZHKHRvdGFsc3RlcHNfbWVhbikgQkVUV0VFTiA1MDAxIEFORCA3NTAwIFRIRU4gJ0xpZ2h0bHkgQWN0aXZlJw0KICBXSEVOIEFWRyh0b3RhbHN0ZXBzX21lYW4pIEJFVFdFRU4gNzUwMSBBTkQgMTAwMDAgVEhFTiAnRmFpcmx5IEFjdGl2ZScNCiAgV0hFTiBBVkcodG90YWxzdGVwc19tZWFuKSA+IDEwMDAwIFRIRU4gJ1ZlcnkgQWN0aXZlJw0KICBFTkQgQVMgdXNlcl9sZXZlbA0KRlJPTSBkYWlseV9hdmVyYWdlDQpHUk9VUCBCWQ0KICBJZA0KT1JERVIgQlkgYXZnX3RvdGFsX3N0ZXBzDQoNCiksDQogdXNlcl9sZXZlbF9jb3VudHMgQVMgKA0KICAgIFNFTEVDVCB1c2VyX2xldmVsLCBDT1VOVCgqKSBBUyB0b3RhbA0KICAgIEZST00gdXNlcnMNCiAgICBHUk9VUCBCWSB1c2VyX2xldmVsDQogICksDQogIHRvdGFsX3VzZXJfbGV2ZWxfY291bnRzIEFTICgNCiAgICBTRUxFQ1QgU1VNKHRvdGFsKSBBUyB0b3RhbF91c2VyX2xldmVsDQogICAgRlJPTSB1c2VyX2xldmVsX2NvdW50cw0KICApLA0KICB1c2VyX2xldmVsX3BlcmNlbnRhZ2VzIEFTICgNCiAgICBTRUxFQ1QgdXNlcl9sZXZlbCwgQ0FTVCh0b3RhbCBBUyBGTE9BVDY0KSAvIHRvdGFsX3VzZXJfbGV2ZWxfY291bnRzLnRvdGFsX3VzZXJfbGV2ZWwgQVMgdG90YWxfcGVyY2VudA0KICAgIEZST00gdXNlcl9sZXZlbF9jb3VudHMsIHRvdGFsX3VzZXJfbGV2ZWxfY291bnRzDQogICAgV0hFUkUgMSA9IDENCiAgKQ0KU0VMRUNUIHVzZXJfbGV2ZWwsDQp0b3RhbF9wZXJjZW50LA0KRlJPTSB1c2VyX2xldmVsX3BlcmNlbnRhZ2VzDQoNCg0KYGBgDQo8L2JyPg0KDQohW1VzZXIgTGV2ZWxdKGh0dHBzOi8vaS5pYmIuY28vR0ZHTlJuQi9TY3JlZW5zaG90LTIwMjMtMDItMTctMDkzMDM4LnBuZykNCg0KW1NwcmVlZHNoZWV0IGRhdGFdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFCa2p3bDhtQnJVRmhOaVFKb0tibHI5Ump5dS10bkJRajhxMmNJNlZvZEY4L2VkaXQjZ2lkPTE4NDAzOTMxMjIpPC9icj4NCg0KIyMjIFN0ZXBzIGFuZCBtaW51dGVzIGFzbGVlcCBwZXIgd2Vla2RheSAgIA0KV2Ugd2FudCB0byBrbm93IG5vdyB3aGF0IGRheXMgb2YgdGhlIHdlZWsgYXJlIHRoZSB1c2VycyBtb3JlIGFjdGl2ZSBhbmQgYWxzbyB3aGF0IGRheXMgb2YgdGhlIHdlZWsgdXNlcnMgc2xlZXAgbW9yZS4gV2Ugd2lsbCBhbHNvIHZlcmlmeSBpZiB0aGUgdXNlcnMgd2FsayB0aGUgcmVjb21tZW5kZWQgYW1vdW50IG9mIHN0ZXBzIGFuZCBoYXZlIHRoZSByZWNvbW1lbmRlZCBhbW91bnQgb2Ygc2xlZXAuDQoNCkJlbG93IHdlIGFyZSBjYWxjdWxhdGluZyB0aGUgd2Vla2RheXMgYmFzZWQgb24gb3VyIGNvbHVtbiBkYXRlLiBXZSBhcmUgYWxzbyBjYWxjdWxhdGluZyB0aGUgYXZlcmFnZSBzdGVwcyB3YWxrZWQgYW5kIG1pbnV0ZXMgYXNsZWVwIGJ5IHdlZWtkYXkuDQoNCmBgYHtzcWwgY29ubmVjdGlvbj0gfQ0KV0lUSA0KLS0gTWVyZ2luZyAgdHdvIHRhYmxlcw0KICBkYWlseV9hY3Rpdml0eV9zbGVlcCAgQVMgKA0KICAgIFNFTEVDVA0KICAgIFRvdGFsX1N0ZXBzLA0KICAgIFRvdGFsTWludXRlc0FzbGVlcCwNCiAgICBkYWlseV9hY3Rpdml0eV9uZXcuSWQgQVMgaWQsDQogICAgZGFpbHlfYWN0aXZpdHlfbmV3LkRhdGUgQVMgZGF0ZQ0KICBGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2AgQVMgZGFpbHlfYWN0aXZpdHlfbmV3DQogIElOTkVSIEpPSU4gDQogICAgYGZpcnN0LWFuYWx5c3QuYmVsbGFiZWF0X2RhdGEuZGFpbHlfc2xlZXBfbmV3YCBBUyBkYWlseV9zbGVlcF9uZXcNCiAgT04NCiAgZGFpbHlfYWN0aXZpdHlfbmV3LklkID0gZGFpbHlfc2xlZXBfbmV3LklkICBBTkQNCiAgIGRhaWx5X2FjdGl2aXR5X25ldy5EYXRlID0gZGFpbHlfc2xlZXBfbmV3LkRhdGUNCiAgICkNCg0KDQotLUZpbmQgdGhlIGF2ZXJhZ2Ugb2YgVG90YWwgc3RlcHMgYW5kIFRvdGFsIG1pbnV0ZSBhc2xlZXAgcGVyIHdlZWsNClNFTEVDVCANCiAgZGF5X29mX3dlZWssIA0KICBST1VORChBVkcoVG90YWxfU3RlcHMpLDIpIGFzIGF2ZV90b3RhbHN0ZXBzX3BlcmRheSwNCiAgUk9VTkQoQVZHKFRvdGFsTWludXRlc0FzbGVlcCksMikgQVMgYXZlX21pbnV0ZXNhc2xlZXBfcGVyZGF5DQpGUk9NDQogICgNCiAgU0VMRUNUICosDQogIENBU0UNCiAgV0hFTiAoRVhUUkFDVChEQVlPRldFRUsgRlJPTSBkYXRlKT0gMSkgVEhFTiAnTW9uJw0KICBXSEVOIChFWFRSQUNUKERBWU9GV0VFSyBGUk9NIGRhdGUpPSAyKSBUSEVOICdUdWUnDQogIFdIRU4gKEVYVFJBQ1QoREFZT0ZXRUVLIEZST00gZGF0ZSk9IDMpIFRIRU4gJ1dlZCcNCiAgV0hFTiAoRVhUUkFDVChEQVlPRldFRUsgRlJPTSBkYXRlKT0gNCkgVEhFTiAnVGh1Jw0KICBXSEVOIChFWFRSQUNUKERBWU9GV0VFSyBGUk9NIGRhdGUpPSA1KSBUSEVOICdGcmknDQogIFdIRU4gKEVYVFJBQ1QoREFZT0ZXRUVLIEZST00gZGF0ZSk9IDYpIFRIRU4gJ1NhdCcNCiAgV0hFTiAoRVhUUkFDVChEQVlPRldFRUsgRlJPTSBkYXRlKT0gNykgVEhFTiAnU3VuJw0KICBFTkQgQVMgZGF5X29mX3dlZWsNCiAgRlJPTSBkYWlseV9hY3Rpdml0eV9zbGVlcA0KICApDQoNCkdST1VQIEJZIGRheV9vZl93ZWVrDQoNCg0KYGBgDQo8L2JyPg0KDQohW1N0ZXBzIGFuZCBtaW51dGVzIGFzbGVlcCBwZXIgd2Vla2RheSAgIF0oaHR0cHM6Ly9pLmliYi5jby9Ka2g4S3haL1NjcmVlbnNob3QtMjAyMy0wMi0xNy0xMTQ0MjAucG5nKQ0KDQpbU3ByZWVkc2hlZXQgZGF0YV0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMWNMTDlMeEtpSTVKR0ZEZk5BN0R2U0ZfWTlhV2JIb2JaMERBYU1rTkxyb0EvZWRpdCNnaWQ9NTM5OTgyMTE5KSANCg0KSW4gdGhlIGdyYXBocyBhYm92ZSB3ZSBjYW4gZGV0ZXJtaW5lIHRoZSBmb2xsb3dpbmc6DQoNClVzZXJzIHdhbGsgZGFpbHkgdGhlIHJlY29tbWVuZGVkIGFtb3VudCBvZiBzdGVwcyBvZiA3NTAwIGJlc2lkZXMgU3VuZGF54oCZcy4gYmFzZWQgb24gdGhlIGFydGljbGUgYWJvdmUNClVzZXJzIGRvbuKAmXQgc2xlZXAgdGhlIHJlY29tbWVuZGVkIGFtb3VudCBvZiBtaW51dGVzLyBob3VycyAtIDggaG91cnMuDQoNCiMjIyBIb3VybHkgc3RlcHMgd2l0aGluIGEgZGF5DQpXZSB3ZXJlIGdvaW5nIHRvIGZpbmQgb3V0IHdoZW4gdGhlIHVzZXJzIG1vcmUgYWN0aXZlIHRocm91Z2hvdXQgdGhlIGRheS4NCg0KIVtIb3VybHkgc3RlcHMgd2l0aGluIGEgZGF5XShodHRwczovL2kuaWJiLmNvL3lSVGs5cUsvU2NyZWVuc2hvdC0yMDIzLTAyLTE3LTEzMDEwNi5wbmcpDQoNCg0KW1NwcmVlZHNoZWV0IGRhdGFdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFwcXpaTkh1SURkRDlfRnhNRktzVmowN2tKa0gzRlduTTZWZi00aUNOMTVnL2VkaXQjZ2lkPTk5NzIyMzM5KSANCg0KDQpBcyB3ZSBjYW4gc2VlIHRoZSBncmFwaCBhYm92ZSB1c2VyIGFyZSBtb3JlIGFjdGl2ZSA3OjAwQU0gdG8gOTowMFBNLiBXZSBhbHNvIHVzZXIgbW9yZSBsaWtlbHkgd2FsayBtb3JlIGF0IDExOjAwQU0gdG8gMjowMFBNLCByZXN0IGF0IDM6MDBQTSwgYW5kIDU6MDBQTSB0byA3OjAwUE0gaW4gdGhlIGV2ZW5pbmcuDQoNCg0KIyMjIENvcnJlbGF0aW9uDQpXZSB3aWxsIG5vdyBkZXRlcm1pbmUgaWYgdGhlcmUgaXMgYW55IGNvcnJlbGF0aW9uIGJldHdlZW4gZGlmZmVyZW50IHZhcmlhYmxlczoNCi0gRGFpbHkgc3RlcHMgYW5kIGRhaWx5IHNsZWVwIA0KLSBEYWlseSBzdGVwcyBhbmQgY2Fsb3JpZXMgDQoNCg0KIVtEYWlseSBzdGVwcyBhbmQgZGFpbHkgc2xlZXBdKGh0dHBzOi8vaS5pYmIuY28vNDRwZldnSy9TY3JlZW5zaG90LTIwMjMtMDItMTctMTI1NjU1LnBuZykNCg0KW1NwcmVlZHNoZWV0IGRhdGFdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFwcXpaTkh1SURkRDlfRnhNRktzVmowN2tKa0gzRlduTTZWZi00aUNOMTVnL2VkaXQjZ2lkPTk5NzIyMzM5KSANCg0KQmFzZWQgb24gb3VyIHBsb3RzOg0KDQpUaGVyZeKAmXMgaXMgbm8gY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgZGFpbHkgc3RlcHMgYW5kIG1pbnV0ZXMgYXNsZWVwLiBCYXNpY2FsbHkgd2Fsa2luZyBkYWlseSBkb2VzIG5vdCBhZmZlY3QgdGhlIG1pbnV0ZXMgb2YgdGhlaXIgc2xlZXANCkJ1dCwgdGhlcmUgaXMgYSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBkYWlseSBzdGVwcyBhbmQgY2Fsb3JpZXMuIEJhc2ljYWxseSB0aGUgbW9yZSB1c2VyIHN0ZXBzIHRoZSBtb3JlIHRoZXkgYnVybiBjYWxvcmllcy4NCg0KDQoNCiMjIyBUb3RhbCBkYXlzIHVzZWQgYnkgdXNlcnMNCk5vdyB0aGF0IHdlIGhhdmUgc2VlbiBzb21lIHRyZW5kcyBpbiBhY3Rpdml0eSwgc2xlZXAgYW5kIGNhbG9yaWVzIGJ1cm5lZCwgd2Ugd2FudCB0byBzZWUgaG93IG9mdGVuIGRvIHRoZSB1c2VycyBpbiBvdXIgc2FtcGxlIHVzZSB0aGVpciBkZXZpY2UuIFRoYXQgd2F5IHdlIGNhbiBwbGFuIG91ciBtYXJrZXRpbmcgc3RyYXRlZ3kgYW5kIHNlZSB3aGF0IGZlYXR1cmVzIHdvdWxkIGJlbmVmaXQgdGhlIHVzZSBvZiBzbWFydCBkZXZpY2VzLg0KDQpXZSB3aWxsIGNhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIHVzZXJzIHRoYXQgdXNlIHRoZWlyIHNtYXJ0IGRldmljZSBvbiBhIGRhaWx5IGJhc2lzLCBjbGFzc2lmeWluZyBvdXIgc2FtcGxlIGludG8gdGhyZWUgY2F0ZWdvcmllcyBrbm93aW5nIHRoYXQgdGhlIGRhdGUgaW50ZXJ2YWwgaXMgMzEgZGF5czoNCg0KKiBoaWdoIHVzZSAtIHVzZXJzIHdobyB1c2UgdGhlaXIgZGV2aWNlIGJldHdlZW4gMjEgYW5kIDMxIGRheXMuIA0KKiBtb2RlcmF0ZSB1c2UgLSB1c2VycyB3aG8gdXNlIHRoZWlyIGRldmljZSBiZXR3ZWVuIDExIGFuZCAyMCBkYXlzLiANCiogbG93IHVzZSAtIHVzZXJzIHdobyB1c2UgdGhlaXIgZGV2aWNlIGJldHdlZW4gMSBhbmQgMTAgZGF5cy4gDQoNCkZpcnN0IEkgd2lsbCBtYWtlIGEgdGVtcCB0YWJsZS4NCg0KYGBge3NxbCBjb25uZWN0aW9uPX0NCg0KV0lUSA0KICAtLU1lcmdpbmcgdHdvIHRhYmxlcyB3aXRoIHR3byBwcmltYXJ5IGtleQ0KICBkYWlseV9hY3Rpdml0eV9hbmRfc2xlZXAgQVMgKA0KICAgIFNFTEVDVA0KICAgIGRhaWx5X2FjdGl2aXR5LklkIGFzIElkLA0KICAgIENPVU5UKCopIGFzIG51bV9vZl91c2UNCiAgICBGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2AgYXMgZGFpbHlfYWN0aXZpdHkNCiAgICBJTk5FUiBKT0lOIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwX25ld2AgYXMgZGFpbHlfc2xlZXANCiAgICBPTiBkYWlseV9hY3Rpdml0eS5JZCA9IGRhaWx5X3NsZWVwLklkIEFORCBkYWlseV9hY3Rpdml0eS5EYXRlID0gZGFpbHlfc2xlZXAuRGF0ZQ0KICAgIEdST1VQIEJZIElkDQogICksDQogICNGaWx0ZXJpbmcgdXNlciB1c2FnZSBiYXNlZCBvbiBkYWlseSBzbGVlcCBhbmQgYWN0aXZpdHkgb2YgYSB1c2Vycw0KICB1c2FnZXMgQVMgKA0KICAgIFNFTEVDVCANCiAgSWQsIA0KICBTVU0obnVtX29mX3VzZSkgQVMgZGF5X3VzZWQsIA0KICBDQVNFIA0KICAgIFdIRU4gU1VNKG51bV9vZl91c2UpIEJFVFdFRU4gMSBBTkQgMTAgVEhFTiAnbG93IHVzZScNCiAgICBXSEVOIFNVTShudW1fb2ZfdXNlKSBCRVRXRUVOIDExIEFORCAyMCBUSEVOICdtb2RlcmF0ZSB1c2UnDQogICAgV0hFTiBTVU0obnVtX29mX3VzZSkgQkVUV0VFTiAyMSBBTkQgMzEgVEhFTiAnaGlnaCB1c2UnDQogIEVORCBBUyB1c2FnZQ0KRlJPTSBkYWlseV9hY3Rpdml0eV9hbmRfc2xlZXANCkdST1VQIEJZIElkDQogICksDQogIC0tIENvdW50aW5nIHRoZSBudW1iZXIgb2YgdXNhZ2UNCiAgdXNhZ2Vfc3VtbWFyeSBBUyAoDQogICAgU0VMRUNUIA0KICAgICAgdXNhZ2UsIA0KICAgICAgQ09VTlQoKikgQVMgdG90YWwNCiAgICBGUk9NIHVzYWdlcw0KICAgIEdST1VQIEJZIHVzYWdlDQogICksDQogIC0tIEdldHRpbmcgdGhlIGF2ZXJhZ2Ugb2YgbnVtYmVyIG9mIHVzYWdlIGFuZCB0b3RhbCB1c2FnZQ0KICB1c2FnZV9wZXJjZW50YWdlIEFTICgNCiAgICBTRUxFQ1QgDQogICAgICB1c2FnZSwgDQogICAgICB0b3RhbCwgDQogICAgICB0b3RhbF91c2FnZSwgDQogICAgICBDQVNUKHRvdGFsIEFTIEZMT0FUNjQpIC8gdG90YWxfdXNhZ2UgQVMgdG90YWxfcGVyY2VudGFnZQ0KICAgLS0gU2VsZWN0aW5nIGl0IEZST00gdXNhZ2Ugc3VtbWFyeSwgYW5kIGZpbmRpbmcgdGhlIHRvdGFsIHVzYWdlIA0KICAgIEZST00gKA0KICAgICAgU0VMRUNUIA0KICAgICAgICB1c2FnZSwgDQogICAgICAgIHRvdGFsLCANCiAgICAgICAgU1VNKHRvdGFsKSBPVkVSICgpIEFTIHRvdGFsX3VzYWdlDQogICAgICBGUk9NIHVzYWdlX3N1bW1hcnkNCiAgICApIA0KICApDQpTRUxFQ1QgDQogIHVzYWdlLCANCiAgdG90YWxfcGVyY2VudGFnZSwgDQogIENPTkNBVChDQVNUKFJPVU5EKHRvdGFsX3BlcmNlbnRhZ2UgKiAxMDAsIDEpIEFTIElOVDY0KSwgJyUgKCcsIENBU1QodG90YWwgQVMgSU5UNjQpLCAnKScpIEFTIGxhYmVscw0KRlJPTSB1c2FnZV9wZXJjZW50YWdlDQoNCg0KYGBgDQoNCiFbVG90YWwgZGF5cyB1c2VkIGJ5IHVzZXJzXShodHRwczovL2kuaWJiLmNvL2dkQ1RNODMvaW1hZ2UucG5nKQ0KDQpbU3ByZWVkc2hlZXQgZGF0YV0oaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMUJqUkFhbmdseVdJVF9lREtKZjRVTjVxelRiS2ZiRGdfS2dzWEdSVnZyTncvZWRpdD91c3A9c2hhcmluZykNCg0KDQpUaGUgcmVzdWx0cyBvbiBvdXQgZ3JhcGggYXJlOiANCi0gQmV0d2VlbiAyMSBhbmQgMzEgZGF5cywgNTAlIG9mIHVzZXJzIGZyZXF1ZW50bHkgdXNlIHRoZWlyIHBob25lLg0KLSAzOCUgb2YgdXNlcnMgYmFsYW5jZSB0aGVpciBwaG9uZSB1c2FnZSBiZXR3ZWVuIDExIGFuZCAyMCBkYXlzLiANCi0gMTIlIG9mIHRoZSB1c2VycyBhcmUgcmFyZWx5IHVzZWQgdGhlaXIgcGhvbmUgMSB0byAxMCBkYXlzDQoNCiMjIyBUaW1lIHVzZWQgc21hcnQgZGV2aWNlDQpCZWluZyBtb3JlIHByZWNpc2Ugd2Ugd2FudCB0byBzZWUgaG93IG1hbnkgbWludXRlcyBkbyB1c2VycyB3ZWFyIHRoZWlyIGRldmljZSBwZXIgZGF5LiBGb3IgdGhhdCB3ZSB3aWxsIG1lcmdlIHRoZSBjcmVhdGVkIGRhaWx5X3VzZSBkYXRhIGZyYW1lIGFuZCBkYWlseV9hY3Rpdml0eSB0byBiZSBhYmxlIHRvIGZpbHRlciByZXN1bHRzIGJ5IGRhaWx5IHVzZSBvZiBkZXZpY2UgYXMgd2VsbC4NCg0KYGBge3NxbCBjb25uZWN0aW9uPX0NCg0KV0lUSA0KICAtLU1lcmdpbmcgdHdvIHRhYmxlcyB3aXRoIHR3byBwcmltYXJ5IGtleQ0KICBkYWlseV9hY3Rpdml0eV9hbmRfc2xlZXAgQVMgKA0KICAgIFNFTEVDVA0KICAgIGRhaWx5X2FjdGl2aXR5LklkIGFzIElkLA0KICAgIENPVU5UKCopIGFzIG51bV9vZl91c2UNCiAgICBGUk9NIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2AgYXMgZGFpbHlfYWN0aXZpdHkNCiAgICBJTk5FUiBKT0lOIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X3NsZWVwX25ld2AgYXMgZGFpbHlfc2xlZXANCiAgICBPTiBkYWlseV9hY3Rpdml0eS5JZCA9IGRhaWx5X3NsZWVwLklkIEFORCBkYWlseV9hY3Rpdml0eS5EYXRlID0gZGFpbHlfc2xlZXAuRGF0ZQ0KICAgIEdST1VQIEJZIElkDQogICksDQogIC0tRmlsdGVyaW5nIHVzZXIgdXNhZ2UgYmFzZWQgb24gZGFpbHkgc2xlZXAgYW5kIGFjdGl2aXR5IG9mIGEgdXNlcnMNCiAgdXNhZ2VzIEFTICgNCiAgICBTRUxFQ1QgDQogIElkLCANCiAgU1VNKG51bV9vZl91c2UpIEFTIGRheV91c2VkLCANCiAgQ0FTRSANCiAgICBXSEVOIFNVTShudW1fb2ZfdXNlKSBCRVRXRUVOIDEgQU5EIDEwIFRIRU4gJ2xvdyB1c2UnDQogICAgV0hFTiBTVU0obnVtX29mX3VzZSkgQkVUV0VFTiAxMSBBTkQgMjAgVEhFTiAnbW9kZXJhdGUgdXNlJw0KICAgIFdIRU4gU1VNKG51bV9vZl91c2UpIEJFVFdFRU4gMjEgQU5EIDMxIFRIRU4gJ2hpZ2ggdXNlJw0KICBFTkQgQVMgdXNhZ2UNCkZST00gZGFpbHlfYWN0aXZpdHlfYW5kX3NsZWVwDQpHUk9VUCBCWSBJZA0KICApLA0KICAtLSBDb3VudGluZyB0aGUgbnVtYmVyIG9mIHVzYWdlDQogIHVzYWdlX3N1bW1hcnkgQVMgKA0KICAgIFNFTEVDVCANCiAgICAgIHVzYWdlLCANCiAgICAgIENPVU5UKCopIEFTIHRvdGFsDQogICAgRlJPTSB1c2FnZXMNCiAgICBHUk9VUCBCWSB1c2FnZQ0KICApLA0KICAtLSBHZXR0aW5nIHRoZSBhdmVyYWdlIG9mIG51bWJlciBvZiB1c2FnZSBhbmQgdG90YWwgdXNhZ2UNCiAgdXNhZ2VfcGVyY2VudGFnZSBBUyAoDQogICAgU0VMRUNUIA0KICAgICAgdXNhZ2UsIA0KICAgICAgdG90YWwsIA0KICAgICAgdG90YWxfdXNhZ2UsIA0KICAgICAgQ0FTVCh0b3RhbCBBUyBGTE9BVDY0KSAvIHRvdGFsX3VzYWdlIEFTIHRvdGFsX3BlcmNlbnRhZ2UNCiAgIC0tIFNlbGVjdGluZyBpdCBGUk9NIHVzYWdlIHN1bW1hcnksIGFuZCBmaW5kaW5nIHRoZSB0b3RhbCB1c2FnZSANCiAgICBGUk9NICgNCiAgICAgIFNFTEVDVCANCiAgICAgICAgdXNhZ2UsIA0KICAgICAgICB0b3RhbCwgDQogICAgICAgIFNVTSh0b3RhbCkgT1ZFUiAoKSBBUyB0b3RhbF91c2FnZQ0KICAgICAgRlJPTSB1c2FnZV9zdW1tYXJ5DQogICAgKSANCiAgKSwgDQogIC0tIENyZWF0aW5nIG5ldyBzdWJxdWVyeSBmb3IgZGFpbHkgdXNlZA0KICBkYWlseV9hY3Rpdml0eV91c2VkIEFTICgNCiAgICBTRUxFQ1QgKg0KICAgIEZST00gIGBmaXJzdC1hbmFseXN0LmJlbGxhYmVhdF9kYXRhLmRhaWx5X2FjdGl2aXR5X25ld2AsIHVzYWdlX3N1bW1hcnkNCiAgKSwNCg0KICBtaW51dGVzX3dvcm4gQVMgKA0KICAgU0VMRUNUICosDQogICBDQVNFIA0KICAgIFdIRU4gbWludXRlc193b3JuX3BlcmNlbnRhZ2UgPSAxMDAgVEhFTiAnQWxsIGRheScNCiAgICBXSEVOIG1pbnV0ZXNfd29ybl9wZXJjZW50YWdlID49IDUwIEFORCBtaW51dGVzX3dvcm5fcGVyY2VudGFnZSA8IDEwMCBUSEVOICdNb3JlIHRoYW4gaGFsZiBkYXknDQogICAgV0hFTiBtaW51dGVzX3dvcm5fcGVyY2VudGFnZSA+IDAgQU5EIG1pbnV0ZXNfd29ybl9wZXJjZW50YWdlIDwgNTAgVEhFTiAnTGVzcyB0aGFuIGhhbGYgZGF5Jw0KICAgRU5EIGFzIHdvcm4NCg0KICBGUk9NICgNCiAgICAgU0VMRUNUICosIA0KICAgICAgKFZlcnlfQWN0aXZlX01pbnV0ZXMgKyBGYWlybHlfQWN0aXZlX01pbnV0ZXMgKyBMaWdodGx5X0FjdGl2ZV9NaW51dGVzICsNClNlZGVudGFyeV9NaW51dGVzKSBhcyB0b3RhbF93b3JuX21pbnV0ZXMsDQogIChWZXJ5X0FjdGl2ZV9NaW51dGVzICsgRmFpcmx5X0FjdGl2ZV9NaW51dGVzICsgTGlnaHRseV9BY3RpdmVfTWludXRlcyArDQpTZWRlbnRhcnlfTWludXRlcykgLyAxNDQwICogMTAwIGFzIG1pbnV0ZXNfd29ybl9wZXJjZW50YWdlDQogICAgIEZST00gZGFpbHlfYWN0aXZpdHlfdXNlZA0KICAgKQ0KDQogICksDQoNCi0tIEFzIHdlIGhhdmUgZG9uZSBiZWZvcmUsIHRvIGJldHRlciB2aXN1YWxpemUgb3VyIHJlc3VsdHMgd2Ugd2lsbCBjcmVhdGUgbmV3IHN1YnF1ZXJpZXMuIA0KDQp3b3JuX3N1bW1hcnkgQVMgKA0KICBTRUxFQ1QgDQogIHdvcm4sDQogIENPVU5UKCopIGFzIHRvdGFsDQogIEZST00gbWludXRlc193b3JuDQogIEdST1VQIEJZIHdvcm4NCiksDQp3b3JuX3BlcmNlbnRhZ2UgQVMgKA0KICBTRUxFQ1QNCiAgd29ybiwNCiAgdG90YWwsDQogIHRvdGFsX3dvcm4sDQoNCiAgRlJPTSAoDQogICAgU0VMRUNUDQogICAgd29ybiwNCiAgICB0b3RhbCwNCiAgICBTVU0odG90YWwpIE9WRVIoKSB0b3RhbF93b3JuDQogICAgRlJPTSB3b3JuX3N1bW1hcnkNCiAgKQ0KDQopLA0KDQoNCg0KICBtaW51dGVzX3dvcm5faGlnaHVzZSBBUyAoDQogICAgU0VMRUNUDQogICAgd29ybiwNCiAgICB0b3RhbC90b3RhbHMgQVMgdG90YWxfcGVyY2VudGFnZSwNCiAgICBDT05DQVQoUk9VTkQodG90YWwgLyB0b3RhbHMgKiAxMDAsIDIpLCAnJScpIEFTIGxhYmVscywNCiAgICBGUk9NICgNCiAgICAgIFNFTEVDVCANCiAgICAgIHdvcm4sDQogICAgICBDT1VOVCgqKSBBUyB0b3RhbCwNCiAgICAgIFNVTShDT1VOVCgqKSkgT1ZFUiAoKSBBUyB0b3RhbHMgICAtLSBDYWxjdWxhdGluZyB0aGUgc3VtIG9mIHRoZSBudW1iZXIgb2Ygcm93cyAod2hpY2ggaXMgdG90YWwpDQogICAgICBGUk9NIG1pbnV0ZXNfd29ybg0KICAgICAgV0hFUkUgdXNhZ2UgPSAnaGlnaCB1c2UnDQogICAgICBHUk9VUCBCWSB3b3JuDQogICAgKQ0KICAgIEdST1VQIEJZIHdvcm4sIHRvdGFsX3BlcmNlbnRhZ2UsbGFiZWxzDQogICksDQogIG1pbnV0ZXNfd29ybl9tb2RlcmF0ZXVzZSBBUyAoDQogICAgU0VMRUNUDQogICAgd29ybiwNCiAgICB0b3RhbCAvIHRvdGFscyBhcyB0b3RhbF9wZXJjZW50YWdlLA0KICAgIENPTkNBVChST1VORCh0b3RhbC90b3RhbHMgKiAxMDAsIDIpLCAnJScpIGFzIGxhYmVscw0KICAgIEZST00gKA0KICAgICAgU0VMRUNUDQogICAgICB3b3JuLA0KICAgICAgQ09VTlQoKikgYXMgdG90YWwsDQogICAgICBTVU0oQ09VTlQoKikpIE9WRVIoKSBhcyB0b3RhbHMgDQogICAgICBGUk9NIG1pbnV0ZXNfd29ybg0KICAgICAgV0hFUkUgdXNhZ2UgPSAnbW9kZXJhdGUgdXNlJw0KICAgICAgR1JPVVAgQlkgd29ybg0KDQogICAgKQ0KICAgIEdST1VQIEJZIHdvcm4sIHRvdGFsX3BlcmNlbnRhZ2UsbGFiZWxzDQogICksDQogICBtaW51dGVzX3dvcm5fbG93dXNlIEFTICgNCiAgICBTRUxFQ1QNCiAgICB3b3JuLA0KICAgIHRvdGFsIC8gdG90YWxzIGFzIHRvdGFsX3BlcmNlbnRhZ2UsDQogICAgQ09OQ0FUKFJPVU5EKHRvdGFsL3RvdGFscyAqIDEwMCwgMiksICclJykgYXMgbGFiZWxzDQogICAgRlJPTSAoDQogICAgICBTRUxFQ1QNCiAgICAgIHdvcm4sDQogICAgICBDT1VOVCgqKSBhcyB0b3RhbCwNCiAgICAgIFNVTShDT1VOVCgqKSkgT1ZFUigpIGFzIHRvdGFscyANCiAgICAgIEZST00gbWludXRlc193b3JuDQogICAgICBXSEVSRSB1c2FnZSA9ICdsb3cgdXNlJw0KICAgICAgR1JPVVAgQlkgd29ybg0KDQogICAgKQ0KICAgIEdST1VQIEJZIHdvcm4sIHRvdGFsX3BlcmNlbnRhZ2UsbGFiZWxzDQogICkNCg0KLS0gU0hPV0lORyBUSEUgVE9UQUwgVVNBR0UgT0YgQUxMIFVTRVINCi0tIFNFTEVDVCANCi0tIHdvcm4sDQotLSB0b3RhbF9wZXJjZW50YWdlLA0KLS0gIENPTkNBVChDQVNUKFJPVU5EKHRvdGFsX3BlcmNlbnRhZ2UgKiAxMDAsIDEpIEFTIElOVDY0KSwgJyUgKCcsIENBU1QodG90YWwgQVMgSU5UNjQpLCAnKScpIEFTIGxhYmVscw0KLS0gRlJPTSB3b3JuX3BlcmNlbnRhZ2UNCg0KDQpTRUxFQ1QNCioNCkZST00gbWludXRlc193b3JuX2xvd3VzZQ0KDQoNCg0KDQpgYGANCiMgQ29uY2x1c2lvbiBhbmQgUmVjb21tZW5kYXRpb24NCg0KQmFzZWQgb24gb3VyIGZpbmRpbmdzLCBJIHdvdWxkIHJlY29tbWVuZCB0aGF0IHdlIHVzZSBvdXIgb3duIHRyYWNraW5nIGRhdGEgZm9yIGZ1cnRoZXIgYW5hbHlzaXMgaW4gb3JkZXIgdG8gcmVzcG9uZCB0byBvdXIgYnVzaW5lc3MgdGFzayBhbmQgYXNzaXN0IEJlbGxhYmVhdCB3aXRoIHRoZWlyIG1pc3Npb24uIEJlY2F1c2Ugd2UgZGlkbid0IGhhdmUgYW55IGRlbW9ncmFwaGljIGluZm9ybWF0aW9uIGFib3V0IHVzZXJzLCB0aGUgZGF0YSBzZXRzIHdlIHVzZWQgaGFkIGEgc21hbGwgc2FtcGxlIHNpemUgYW5kIGNvdWxkIGJlIGJpYXNlZC4gS25vd2luZyB0aGF0IG91ciBwcmltYXJ5IGRlbW9ncmFwaGljIGlzIHlvdW5nIGFuZCBhZHVsdCB3b21lbiwgSSB3b3VsZCBlbmNvdXJhZ2UgeW91IHRvIGNvbnRpbnVlIGxvb2tpbmcgZm9yIHRyZW5kcyBpbiBvcmRlciB0byBkZXZlbG9wIGEgbWFya2V0aW5nIHN0cmF0ZWd5IGNlbnRlcmVkIG9uIHRoZW0uDQoNClRoYXQgYmVpbmcgc2FpZCwgYWZ0ZXIgb3VyIGFuYWx5c2lzIHdlIGhhdmUgZm91bmQgZGlmZmVyZW50IHRyZW5kcyB0aGF0IG1heSBoZWxwIG91ciBvbmxpbmUgY2FtcGFpZ24gYW5kIGltcHJvdmUgQmVsbGFiZWF0IGFwcDoNCg0KDQoNCmBgYHtyfQ0KbGlicmFyeShrbml0cikNCnJlY29tbWVuZGF0aW9uIDwtIGMoIkFkZGluZyBhICdnb2FsJyBvbiB0aGUgYXBwIiwgIlNsZWVwaW5nIHJlY29tbWVuZGF0aW9uIGZvciB1c2VycyIsICJMZXZlbCBvciByYW5rIGJhZGdlIHN5c3RlbSIpDQpkZXNjcmlwdGlvbiA8LSBjKCJQdXR0aW5nIGEgZ29hbCB0byB0cmFjayBkYWlseSBzdGVwcyBhbmQgY2Fsb3JpZXMgaGVscHMgdXNlcnMgbW9uaXRvciB0aGVpciBwcm9ncmVzcyB0b3dhcmRzIHRoZWlyIGZpdG5lc3MgZ29hbHMuIiwgDQogICAgICAgICAgICAgICAgICJBIHNsZWVwIHJlY29tbWVuZGF0aW9uIGZlYXR1cmUgY2FuIHJlbWluZCB1c2VycyB0byBtYWludGFpbiBhIGhlYWx0aHkgc2xlZXAgcm91dGluZSBhbmQgcHJvdmlkZSBoZWxwZnVsIHRpcHMgZm9yIGJldHRlciBzbGVlcC4iLA0KICAgICAgICAgICAgICAgICAiQSBsZXZlbCBvciByYW5rIHN5c3RlbSBjYW4gbW90aXZhdGUgdXNlcnMgdG8gYmUgbW9yZSBhY3RpdmUgYnkgYWxsb3dpbmcgdGhlbSB0byB0cmFjayB0aGVpciBwcm9ncmVzcyBhbmQgZWFybiBiYWRnZXMgYXMgcmV3YXJkcy4iKQ0KdGJsIDwtIGRhdGEuZnJhbWUoUmVjb21tZW5kYXRpb24gPSByZWNvbW1lbmRhdGlvbiwgRGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvbikNCmthYmxlKHRibCkNCg0KDQpgYGANCg0KT24gb3VyIGFuYWx5c2lzIHdlIGRpbnQganVzdCBjaGVjayBmb3IgdGhlIHRyZW5kcyBidXQgYWxzbyB3ZSBzZWUgdGhhdCBoZWFsdGh5IHVzZXJzIHRlbmQgdG8gYmUgdXNlIHRoZWlyIGRldmljZSBhbmQgMzYlIG9mIHRoZSB1c2VycyB3ZWFyIHRoZSBkZXZpY2UgYWxsIHRpbWUuDQoNCiogSXQgY291bGQgYmUgZ29vZCB0byBtYXJrZXQgdGhpcyBkZXZpY2Ugd2hvIGFyZSBtb3JlIGFjdGl2ZSBhbmQgd2hvIHdhbnQgdG8gdHJhY2sgdGhlaXIgZGFpbHkgYWN0aXZpdGllcw0KIFdlIGNhbiBjb250aW51ZSBwcm9tb3RlIEJlbGxhYmVhdCdzIHByb2R1Y3RzIGZlYXR1cmVzOg0KDQoqIFdvcmFibGUgZGV2aWNlIGZvciBtZW4NCiogV2F0ZXItcmVzaXN0YW50DQoqIExvbmctbGFzdGluZyBiYXR0ZXJpZXMNCiogRmFzaGlvbi8gZWxlZ2FudCBwcm9kdWN0cw0KDQo=