‘Ask’ Phase of Data Analysis Process

Scenario

Cyclistic, a fictional bike-share company out of Chicago, plans to update its marketing strategy, previously built on generating general awareness and appeal to broad consumer markets. The director of marketing, Lily Moreno, now believes the company’s future success depends on maximizing the number of annual memberships, due to the conclusion that annual members are much more profitable than casual riders.

Key Stakeholders

  • Cyclistic Executive team: Detail-oriented executive team which will decide whether to approve the recommended marketing program.
  • Lily Moreno: Manager and Director of Marketing. Responsible for the development of campaigns and initiatives to promote the bike-share program.
  • Cyclistic Marketing Analytics Team: A team of data analysts responsible for the collecting, analyzing, and reporting data that helps guide Cyclistic’s marketing strategy.

Problem Statement and Business Task

Using Cyclistic’s historical trip data from the previous 12 months, the business task is to find out how annual members and casual riders use Cyclistic bikes differently in 2023 (latest update of dataset). Understanding the differences in usages of the two types of riders can help deliver insights into how the marketing team can increase the number of annual memberships purchased.



‘Prepare’ Phase of Data Analysis Process

Data Location and Organization

This data was made available by Motivate International Inc. and is open to the public for download. (Note: The dataset has a different name because Cyclisitc is a fictional company. For the purposes of this case study, the dataset is appropriate for the business task).

The data being used to conduct this analysis is a csv file containing trip data from 2023:

202301-divvy-tripdata.csv * 202302-divvy-tripdata.csv * 202303-divvy-tripdata.csv * 202304-divvy-tripdata.csv * 202305-divvy-tripdata.csv * 202306-divvy-tripdata.csv * 202307-divvy-tripdata.csv * 202308-divvy-tripdata.csv * 202309-divvy-tripdata.csv * 202310-divvy-tripdata.csv * 202311-divvy-tripdata.csv * 202312-divvy-tripdata.csv

After downloading and unzipping the csv file, using R and Rstudio, the dataset were read into their own data frames and merged into one data frame representing the whole year. From here we can easily view and manipulate the data:

library(tidyverse)

# create data frame for each months data set
data_01_df <- read.csv("new_data/202301-divvy-tripdata.csv")
data_02_df <- read.csv("new_data/202302-divvy-tripdata.csv")
data_03_df <- read.csv("new_data/202303-divvy-tripdata.csv")
data_04_df <- read.csv("new_data/202304-divvy-tripdata.csv")
data_05_df <- read.csv("new_data/202305-divvy-tripdata.csv")
data_06_df <- read.csv("new_data/202306-divvy-tripdata.csv")
data_07_df <- read.csv("new_data/202307-divvy-tripdata.csv")
data_08_df <- read.csv("new_data/202308-divvy-tripdata.csv")
data_09_df <- read.csv("new_data/202309-divvy-tripdata.csv")
data_10_df <- read.csv("new_data/202310-divvy-tripdata.csv")
data_11_df <- read.csv("new_data/202311-divvy-tripdata.csv")
data_12_df <- read.csv("new_data/202312-divvy-tripdata.csv")

# merge data frames into one data set for the whole year
trip_data_df <- rbind(data_01_df, data_02_df, data_03_df, data_04_df, 
              data_05_df, data_06_df, data_07_df, data_08_df, data_09_df, 
              data_10_df, data_11_df, data_12_df)

The data is organized such that each row represents a single bike trip containing the following measurements and attributes:

glimpse(trip_data_df)
Rows: 5,719,877
Columns: 13
$ ride_id            <chr> "F96D5A74A3E41399", "13CB7EB698CEDB88", "BD88A2E6…
$ rideable_type      <chr> "electric_bike", "classic_bike", "electric_bike",…
$ started_at         <chr> "2023-01-21 20:05:42", "2023-01-10 15:37:36", "20…
$ ended_at           <chr> "2023-01-21 20:16:33", "2023-01-10 15:46:05", "20…
$ start_station_name <chr> "Lincoln Ave & Fullerton Ave", "Kimbark Ave & 53r…
$ start_station_id   <chr> "TA1309000058", "TA1309000037", "RP-005", "TA1309…
$ end_station_name   <chr> "Hampden Ct & Diversey Ave", "Greenwood Ave & 47t…
$ end_station_id     <chr> "202480.0", "TA1308000002", "599", "TA1308000002"…
$ start_lat          <dbl> 41.92407, 41.79957, 42.00857, 41.79957, 41.79957,…
$ start_lng          <dbl> -87.64628, -87.59475, -87.69048, -87.59475, -87.5…
$ end_lat            <dbl> 41.93000, 41.80983, 42.03974, 41.80983, 41.80983,…
$ end_lng            <dbl> -87.64000, -87.59938, -87.69941, -87.59938, -87.5…
$ member_casual      <chr> "member", "member", "casual", "member", "member",…

This includes data for ride IDs, bike types, start and end times for trips, to and from station names and IDs, start and end latitudes and longitudes, and user types (either a member or causal rider).

Credibility and Bias of the Data

Being that the data is first-party data and is provided by the bike-share company, for the sake of this case study we are going to assume that there is no bias or credibility concerns with our data set. However, in a real setting as a data analyst when working with outside data, you should always be sure that there is no bias and that your data is reliable before continuing.

Licensing, Privacy, Security, and Accessibility

The data set is provided under a Data License Agreement, which permits users to access, reproduce, copy, modify, analyze, and distribute the data so long as it is used as source material in analyses, reports, and/or studies. It prohibits users from host, stream, publish, distribute, sublicense, or selling the data as a stand-alone dataset (as stated here).

Privacy issues have been addressed by the removal of any personally identifiable information in the dataset.

The dataset is securely managed my Motivate International Inc. employees and is stored using a well-known cloud services provider in Amazon Web Services.

Cyclisics bike-share data is open to the public, meaning it is accessible to anyone so long as the license agreement is being abided by.

Data Integrity

The data’s integrity will be verified in the cleaning process of this analysis. Issues such as missing or duplicate values, incorrect values, and inconsistencies will be addressed.

How does the Data help answer the business question?

The dataset contains all of the information we will need to determine how annual members and casual riders use Cyclistic bikes differently. Data such as rider type, start and end stations, latitudes and longitudes, start and end times, and bike types will help us differentiate the usage in many ways in order to provide valuable insights to our stakeholders as to how Cyclistic can maximize the number of annual memberships being purchased.

Potential Problems

Potential problems could include inconsistencies in the data, missing values, outliers of ride times, or duplicate records for rides.



‘Process’ Phase of Data Analysis Process

What Tools to Use?

I started off using Google Sheets but I began experiencing performance issues because of how large the dataset is. I then switched to using Rstudio and the R programming language to carry out my analysis. R is a great tool for this case study because it can handle large datasets (or as big what can fit in RAM comfortably). It also has a package called “tidyverse”, which is a collection of packages specifically designed for working with data. This includes data cleaning, manipulation, exploration, and visualization.

Ensuring Data Integrity (Cleaning the Data)

First, we’ll start by making a copy of the dataset and storing it in the data frame trip_data_clean_df. It is always good practice to not modify the original dataset unless given specific instructions to do so. This is so if anything goes wrong, we can always refer back to the original data without worrying about compromising it. We’ll also start by removing any null values that a record may contain. Since each column attribute is something we will use in our analysis, we need to make sure each cell contains a value.

# Make copy of original data set for cleaning and removing null values in records
trip_data_clean_df <- trip_data_df %>% 
  drop_na()
# Examine data set
head(trip_data_clean_df)


Cleaning up Column Names

At first glance I noticed some of the columns names are not very descriptive, which include started_at, ended_at, and member_casual. Changing these names to start_time, end_time, and user_type would make them less ambiguous and clearer as to what they represent.

trip_data_clean_df <- trip_data_clean_df %>% 
  rename(start_time = started_at, end_time = ended_at, user_type = member_casual)

colnames(trip_data_clean_df)
 [1] "ride_id"            "rideable_type"      "start_time"        
 [4] "end_time"           "start_station_name" "start_station_id"  
 [7] "end_station_name"   "end_station_id"     "start_lat"         
[10] "start_lng"          "end_lat"            "end_lng"           
[13] "user_type"         


Check for Duplicate Records

To make we are not using duplicate records in our analysis we can use the duplicated function nested inside the sum function which will give us the total number of duplicates in the dataset.

# Number of duplicate records
sum(duplicated(trip_data_clean_df))
[1] 0

We got a sum of zero duplicates so that means were are not using repeated data in our analysis.


Conversion of Start and End Times

The start and end times are of a character format in the form ‘YYYY-MM-DD H:M:S UTC’. To make them easier to work with in terms of manipulation and calculation of dates and times, we are going to covert them to class POSIXct. This data type makes working with date-time objects easier, as opposed to if they were left as character strings.

# Converting start and end times to POSIXct class
trip_data_clean_df <- trip_data_clean_df %>% 
  mutate(start_time = as.POSIXct(trip_data_clean_df$start_time, format = "%Y-%m-%d %H:%M:%S")) %>% 
  mutate(end_time = as.POSIXct(trip_data_clean_df$end_time, format = "%Y-%m-%d %H:%M:%S"))

From here we can now filter out rows in which the start time of bike rides is greater than or equal to end times. This is important because we do not want ride times that are have a time of zero or negative time, which is impossible, when the new column for ride times in seconds is created. We can also create a column for the day of the week in which rides were taken, extracted from the start time column.

trip_data_clean_df <- trip_data_clean_df %>% 
  filter(end_time > start_time) %>%  # filter out start_time >= end_time
  mutate(ride_time_sec = difftime(end_time, start_time)) %>%  # calculate ride time in seconds
  mutate(day_of_week = weekdays(start_time)) # get day of week ride was taken
trip_data_clean_df %>% 
  select(ride_time_sec, day_of_week)


Additional Concerns

While searching for errors in the data, I noticed that some station names have more than one ID associated with them. I verified which stations, how many IDs were associated with them, and what the IDs were with this:

# checks which start_station have more than one unique ids and lists the ids associated with them
trip_data_clean_df %>% 
  group_by(start_station_name) %>% 
  summarise(num_unique_ids = n_distinct(start_station_id), id_list=paste(unique(start_station_id), collapse = ", ")) %>% 
  filter(num_unique_ids > 1)

This could be a problem if we needed to extract information about certain stations with their ID, however for this analysis, we are not too concerned with accessing a station’s ID so it will be left unchanged. If I were to fix these station’s IDs, I would further investigate each station to determine which ID is used more often and ensure that each record that contains that station will have the more frequent ID associated with it.



‘Analyze’ Phase of Data Analysis Process

Now that we have a clean dataset, we can perfrom exploratory analysis to identify trends and relationships among the data to give us insights that could help answer our business question.

Key Tasks

  • Aggregate data so that it is useful and accessible
  • Organize and format data
  • Perform calculations
  • Identify trends and relationships



Usertype vs. Ride Time

The aim of this analysis is to identify the difference in ride times of casual and member riders:

library(ggplot2)
# User type vs mean ride time
mean_ride_times <- trip_data_clean_df %>% 
  group_by(user_type) %>% 
  summarise(mean_ride_time_sec = mean(ride_time_sec), max_ride_time=max(ride_time_sec), 
            min_ride_time=min(ride_time_sec))

mean_ride_times

# Causal Rider: just over 20 min 30 sec average ride time
# Member Rider: just over 12 min average ride time
ggplot(data=mean_ride_times) + 
  geom_col(mapping=aes(x=user_type, y=mean_ride_time_sec,fill=user_type)) +
  geom_text(aes(x=user_type, y=mean_ride_time_sec, label=round(mean_ride_time_sec, 2)),vjust=1) +
  labs(title = "Average Ride Times by Usertype", x="Usertype",y="Avg. Ride Time(Sec)")

Analysis:

  • Casual riders have a higher average ride time than riders with annual memberships by about 8 minutes and 30 seconds.
  • According to the max_ride_time column in the pivot table, the max ride time for casual riders is 728,178 seconds which equal to about eight and half days of ride time. Assuming that this is an outlier statistic, my best guess is that the rider during this bike trip may have forgotten to dock their bike at a bike station, which could have led to such a high ride time. The same goes for the max ride time for the membership rider, but only equals to just over a 24 hours of ride time.
  • However, with casual riders having a higher average ride time could indicate that they use Cyclistic bike more leisurely than members.



Which days do different riders use Cyclistic bikes?

To get a better sense of when casual and member riders use bikes, we can count the number of rides each type of rider took for each weekday of the year. While aggregating my data into a useful data frame, I changed the type of the day_of_week column from a character string to an ordinal factor. This made it easier to sort, organize, and count the data.

# Aggregate and organize data by week day counts for each type of rider
weekday_rides <- trip_data_clean_df %>% 
  mutate(day_of_week = wday(start_time, label=TRUE)) %>% 
  group_by(day_of_week, user_type) %>% 
  summarise(.groups="drop",ride_count=n()) %>% 
  arrange(day_of_week)

ggplot(data = weekday_rides) + 
  geom_col(mapping = aes(x=day_of_week, y=ride_count,fill=user_type),position = 'dodge') +
  labs(title = "Ride Count Per Day of Week", x="Day of Week",y="Number of Rides") +
  scale_y_continuous(labels = scales::comma)

Analysis:

  • Membership riders tend to use Cyclistic bikes more during the work week (Monday-Friday), while casual riders use the bikes more on weekends.
  • Again, this could indicate more leisure-oriented riding for casual riders leading to the higher ride times.
  • It seems that member riders may use their bike rides more for commuting or public transport. Maybe to and from work or another pre-planned trip.



Do casual and member riders differ in the time of year in which they ride?

# which months do rides occur the most
trip_data_clean_df %>% 
  mutate(month_of_ride = month(start_time, label=TRUE)) %>% 
  group_by(month_of_ride, user_type) %>% 
  summarise(.groups="drop", rides_per_month = n()) %>% 
  arrange(month_of_ride) %>% 
  ggplot() + 
  geom_line(mapping=aes(x=month_of_ride,y=rides_per_month, 
                        color=user_type, group=user_type)) +
  labs(title="Rides per Month by Usertype",x="Month",y="Number of Rides") +
  scale_y_continuous(labels = scales::comma)

Analysis:

  • About what I expected, bike rides for both types of riders occur more in the warmer months.
  • The peak number of bike rides for members happened in August and for casual riders in July
  • Bike rides start to increase in the spring as temperatures start to rise and decrease in the fall as temperatures decrease.



Rideable Type Preference for Casual and Member Riders

To further investigate how casual and member riders use bikes differently, we can see which type of bike each prefer based on which type of bikes were used for rides in the past 12 months. (Note: When organizing data for casual riders, I noticed there was a third type of bike, docked_bike, which is just a bike docked at a station so I excluded it from my analysis)

# Analysing rideable bike types per usertype

# Calculating the number of rides and percent of total rides taken with each
# type of bike for casual riders
c_rideable <- trip_data_clean_df %>% 
  filter(user_type == "casual") %>% 
  filter(rideable_type != "docked_bike") %>% 
  group_by(rideable_type) %>% 
  summarise(num_rides = n()) %>% 
  ungroup() %>% 
  mutate(percent_total_rides = num_rides/sum(num_rides) * 100)

# Pie chart displaying percentages of each bike used for casual riders
casual_pie <- ggplot(data = c_rideable, aes(x = "", y = percent_total_rides, fill = rideable_type)) +
  geom_bar(stat = "identity", width = 1) +
  geom_label(aes(label = paste0(round(percent_total_rides, 1), "%")), 
             position = position_stack(vjust = 0.5),show.legend = FALSE) +
  coord_polar(theta = "y") +
  labs(title = "Distribution of Rides by Rideable Type (Casual)",
       fill = "Rideable Type") +
  theme_void() +
  theme(legend.position = "right")
  

# Calculating the number of rides and percent of total rides taken with each
# type of bike for casual riders
m_rideable <- trip_data_clean_df %>% 
  filter(user_type == "member") %>% 
  group_by(rideable_type) %>% 
  summarise(num_rides = n()) %>% 
  ungroup() %>% 
  mutate(percent_total_rides = num_rides/sum(num_rides)*100)

# Pie chart displaying percentages of each bike used for member riders
member_pie <- ggplot(data = m_rideable, aes(x = "", y = percent_total_rides, fill = rideable_type)) +
  geom_bar(stat = "identity", width = 1) +
  geom_label(aes(label = paste0(round(percent_total_rides, 1), "%")), 
             position = position_stack(vjust = 0.5),show.legend = FALSE) +
  coord_polar(theta = "y") +
  labs(title = "Distribution of Rides by Rideable Type (Member)",
       fill = "Rideable Type") +
  theme_void() +
  theme(legend.position = "right")

casual_pie

member_pie

Analysis:

  • Annual members do not really show a preference for one bike type or the other, as they use both electric and classic bikes pretty evenly.
  • Casual riders show a slight preference for electric bikes over classic bikes. According to the pie chart, about 11.6% more rides were taken with electric bikes by casusal riders.



Identifying the Top 5 Starting Stations for Casual and Member Riders

It is important to identify the most popular start stations riders go to for their bike rides. Knowing the difference in where casual riders like to go for their bikes and where members go can give insights to stakeholders in determining which area would be best to target when trying to convert casual riders to annual members.

######## Identify the top 5 starting stations for each usertype

# selecting user_type, start_station_name, and rideable_type from and storing 
# in a data frame station_data
station_data <- trip_data_clean_df %>% 
  filter(rideable_type != "docked_bike") %>% 
  select(user_type, start_station_name, rideable_type) %>% 
  drop_na()

# Group data by user_type, start_station_name, and rideable type, calculate 
# number of rides for each combination
station_summary <- station_data %>% 
  filter(start_station_name != "") %>% 
  group_by(user_type, start_station_name, rideable_type) %>% 
  summarise(.groups="drop",num_rides = n()) %>% 
  arrange(user_type, start_station_name)

# Transform data to a wide format to create separate columns for each rideable 
# bike type
station_summary_pivot <- station_summary %>%
  pivot_wider(names_from = rideable_type, values_from = num_rides, values_fill = 0)

# Calculate the total number of rides and how much number of rides with classic 
# and electric bike types for each combination or user_type and 
# start_station_name
station_summary_final <- station_summary_pivot %>%
  group_by(user_type, start_station_name) %>%
  summarise(.groups="drop",num_rides = sum(classic_bike)+sum(electric_bike),
            num_rides_w_classic = sum(classic_bike),
            num_rides_w_electric = sum(electric_bike))

# Get the top 5 stations with the most num_rides for each user_type
top_5_stations <- station_summary_final %>%
  group_by(user_type) %>%
  top_n(5, num_rides) %>% 
  arrange(user_type, desc(num_rides))

top_5_stations


Casual Riders Top Start Stations:

## Bar graph for Top 5 start stations by num rides (casual rider)
top_5_stations %>% 
  filter(user_type == "casual") %>% 
  ggplot(aes(x=start_station_name, y=num_rides,fill="All Stations")) + 
  geom_bar(stat = "identity",) +
  labs(title = "Top 5 Start Stations by Number of Rides (Casual)",
       x = "Start Station", y = "Total Number of Rides") +
  geom_text(aes(x=start_station_name,y=num_rides,label=num_rides),vjust=1.5,
            color="white", size=6)+
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 7),
        legend.position = "none",plot.title = element_text(size = 12)) +
  scale_fill_manual(values = "brown")


Annual Members Top Start Stations:

## Bar graph for Top 5 start stations by num rides (member rider)
top_5_stations %>% 
  filter(user_type == "member") %>% 
  ggplot(aes(x=start_station_name, y=num_rides,fill="All Stations")) + 
  geom_bar(stat = "identity",) +
  labs(title = "Top 5 Start Stations by Number of Rides (Member)",
       x = "Start Station", y = "Total Number of Rides") +
  geom_text(aes(x=start_station_name,y=num_rides,label=num_rides),vjust=1.5,
            color="white", size=6) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 7),
        legend.position = "none",plot.title = element_text(size = 12)) +
  scale_fill_manual(values = "brown")

Analysis:

  • Casual riders and members have different preferences for their top five starting stations.
  • The station ‘Streeter Dr & Grand Ave’ has had about 15,000 more casual riders start there than the next closest starting station.


Summarization of Findings

Both Casual and Member Riders:

  • Both use Cyclistic bikes more in the warmer months and rarely take rides in the winter.

Annual Membership Riders:

  • Have a lower average ride time by about 8 minutes 30 seconds.
  • Take most of their rides during the work week (Mon-Fri) and do not ride as much during the weekend.
  • Show little to no preference in the type of bike used for their rides
  • Have different start station preferences than casual riders but have more of an even distribution between their top stations.

Casual Riders:

  • Have a higher average ride time.
  • Tend to take more rides on the weekend as opposed to during the week.
  • Show a preference for using electric bikes more than classic bikes.
  • Have different start station preferences than members. The highest being ‘Streeter Dr & Grand Ave’ station with 41,260 rides and the next closest station in terms of rides is ‘DuSable Lake Shore Dr & Monroe St’ with 26,922 rides. Almost a difference of 15,000 rides during the year.



‘Share and Act’ Phase of Data Analysis Process

Throughout this analysis, we have identified key differences in how casual riders and riders with annual memberships use Cyclistic bikes. With these insights, here are my top three recommendations for ways the marketing team can campaign for converting more casual riders to members:

  1. Emphasize Weekday Riding Benefits: Annual members tend to take most of their rides during the work week, while casual riders prefer weekends. To encourage casual riders to consider membership, marketing could promote the advantages of weekday riding, such as avoiding rush hour traffic, incorporating exercise or being outside into daily routines, and convenient transport to work or errands.

  2. Highlight Electric Bike Preference: Casual riders show a preference for using electric bikes more than classic bikes, which could indicate a preference for more comfortable and leisurely riding experience. Cyclistic’s marketing campaign could specifically highlight the benefits of electric bikes for leisure riding, such as effortless navigation through city streets without exerting excess effort. By emphasizing the suitability of electric bikes for leisurely rides, Cyclistic can position membership as a solution for casual riders looking to enjoy relaxed and comfortable outings around the city regularly.

  3. Offer Membership Benefits to High-Volume Starting Stations: Given the differences in start station preferences between casual and member riders, Cyclistic can consider offering membership benefits to align with the needs and preferences of casual riders. This could include offering exclusive incentives or discounts for rides starting from the most popular stations for casual riders. This acknowledges the preferences of casual riders and provides additional value to encourage them to become members. Additionally, to further tailor to casual rider preference and add more incentive to transition to membership, Cyclistic can optimize bike availability, maintenance, and infrastructure at these stations to enhance the overall riding experience.

LS0tDQp0aXRsZTogIkN5Y2xpc3RpYyBCaWtlLVNoYXJlIE1lbWJlciB2cy4gQ2FzdWFsIFJpZGVyIEFuYWx5c2lzIg0KYXV0aG9yOiAiVG9ieSBHcmlmZmluIg0KZGF0ZTogImByIFN5cy5EYXRlKClgIg0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDogdHJ1ZQ0KDQotLS0NCg0KPGJyPg0KPGJyPg0KDQoNCg0KIyAnQXNrJyBQaGFzZSBvZiBEYXRhIEFuYWx5c2lzIFByb2Nlc3MNCg0KIyMjIFNjZW5hcmlvDQoNCkN5Y2xpc3RpYywgYSBmaWN0aW9uYWwgYmlrZS1zaGFyZSBjb21wYW55IG91dCBvZiBDaGljYWdvLCBwbGFucyB0byB1cGRhdGUgaXRzIG1hcmtldGluZyBzdHJhdGVneSwgcHJldmlvdXNseSBidWlsdCBvbiBnZW5lcmF0aW5nIGdlbmVyYWwgYXdhcmVuZXNzIGFuZCBhcHBlYWwgdG8gYnJvYWQgY29uc3VtZXIgbWFya2V0cy4gVGhlIGRpcmVjdG9yIG9mIG1hcmtldGluZywgTGlseSBNb3Jlbm8sIG5vdyBiZWxpZXZlcyB0aGUgY29tcGFueSdzIGZ1dHVyZSBzdWNjZXNzIGRlcGVuZHMgb24gbWF4aW1pemluZyB0aGUgbnVtYmVyIG9mIGFubnVhbCBtZW1iZXJzaGlwcywgZHVlIHRvIHRoZSBjb25jbHVzaW9uIHRoYXQgYW5udWFsIG1lbWJlcnMgYXJlIG11Y2ggbW9yZSBwcm9maXRhYmxlIHRoYW4gY2FzdWFsIHJpZGVycy4NCg0KDQoNCg0KIyMjIEtleSBTdGFrZWhvbGRlcnMNCg0KKiBDeWNsaXN0aWMgRXhlY3V0aXZlIHRlYW06IERldGFpbC1vcmllbnRlZCBleGVjdXRpdmUgdGVhbSB3aGljaCB3aWxsIGRlY2lkZSB3aGV0aGVyIHRvIGFwcHJvdmUgdGhlIHJlY29tbWVuZGVkIG1hcmtldGluZyBwcm9ncmFtLg0KKiBMaWx5IE1vcmVubzogTWFuYWdlciBhbmQgRGlyZWN0b3Igb2YgTWFya2V0aW5nLiBSZXNwb25zaWJsZSBmb3IgdGhlIGRldmVsb3BtZW50IG9mIGNhbXBhaWducyBhbmQgaW5pdGlhdGl2ZXMgdG8gcHJvbW90ZSB0aGUgYmlrZS1zaGFyZSBwcm9ncmFtLg0KKiBDeWNsaXN0aWMgTWFya2V0aW5nIEFuYWx5dGljcyBUZWFtOiBBIHRlYW0gb2YgZGF0YSBhbmFseXN0cyByZXNwb25zaWJsZSBmb3IgdGhlIGNvbGxlY3RpbmcsIGFuYWx5emluZywgYW5kIHJlcG9ydGluZyBkYXRhIHRoYXQgaGVscHMgZ3VpZGUgQ3ljbGlzdGljJ3MgbWFya2V0aW5nIHN0cmF0ZWd5Lg0KIA0KDQoNCiMjIyBQcm9ibGVtIFN0YXRlbWVudCBhbmQgQnVzaW5lc3MgVGFzaw0KDQpVc2luZyBDeWNsaXN0aWMncyBoaXN0b3JpY2FsIHRyaXAgZGF0YSBmcm9tIHRoZSBwcmV2aW91cyAxMiBtb250aHMsIHRoZSBidXNpbmVzcyB0YXNrIGlzIHRvIGZpbmQgb3V0IGhvdyBhbm51YWwgbWVtYmVycyBhbmQgY2FzdWFsIHJpZGVycyB1c2UgQ3ljbGlzdGljIGJpa2VzIGRpZmZlcmVudGx5IGluIDIwMjMgKGxhdGVzdCB1cGRhdGUgb2YgZGF0YXNldCkuIFVuZGVyc3RhbmRpbmcgdGhlIGRpZmZlcmVuY2VzIGluIHVzYWdlcyBvZiB0aGUgdHdvIHR5cGVzIG9mIHJpZGVycyBjYW4gaGVscCBkZWxpdmVyIGluc2lnaHRzIGludG8gaG93IHRoZSBtYXJrZXRpbmcgdGVhbSBjYW4gaW5jcmVhc2UgdGhlIG51bWJlciBvZiBhbm51YWwgbWVtYmVyc2hpcHMgcHVyY2hhc2VkLiANCg0KPGJyPg0KPGJyPg0KDQojICdQcmVwYXJlJyBQaGFzZSBvZiBEYXRhIEFuYWx5c2lzIFByb2Nlc3MNCg0KIyMjIERhdGEgTG9jYXRpb24gYW5kIE9yZ2FuaXphdGlvbg0KDQpUaGlzIGRhdGEgd2FzIG1hZGUgYXZhaWxhYmxlIGJ5IE1vdGl2YXRlIEludGVybmF0aW9uYWwgSW5jLiBhbmQgaXMgb3BlbiB0byB0aGUgcHVibGljIGZvciBkb3dubG9hZC4gKE5vdGU6IFRoZSBkYXRhc2V0IGhhcyBhIGRpZmZlcmVudCBuYW1lIGJlY2F1c2UgQ3ljbGlzaXRjIGlzIGEgZmljdGlvbmFsIGNvbXBhbnkuIEZvciB0aGUgcHVycG9zZXMgb2YgdGhpcyBjYXNlIHN0dWR5LCB0aGUgZGF0YXNldCBpcyBhcHByb3ByaWF0ZSBmb3IgdGhlIGJ1c2luZXNzIHRhc2spLiANCg0KVGhlIGRhdGEgYmVpbmcgdXNlZCB0byBjb25kdWN0IHRoaXMgYW5hbHlzaXMgaXMgYSBjc3YgZmlsZSBjb250YWluaW5nIHRyaXAgZGF0YSBmcm9tIDIwMjM6IA0KDQoyMDIzMDEtZGl2dnktdHJpcGRhdGEuY3N2DQoqIDIwMjMwMi1kaXZ2eS10cmlwZGF0YS5jc3YNCiogMjAyMzAzLWRpdnZ5LXRyaXBkYXRhLmNzdg0KKiAyMDIzMDQtZGl2dnktdHJpcGRhdGEuY3N2DQoqIDIwMjMwNS1kaXZ2eS10cmlwZGF0YS5jc3YgDQoqIDIwMjMwNi1kaXZ2eS10cmlwZGF0YS5jc3YNCiogMjAyMzA3LWRpdnZ5LXRyaXBkYXRhLmNzdg0KKiAyMDIzMDgtZGl2dnktdHJpcGRhdGEuY3N2DQoqIDIwMjMwOS1kaXZ2eS10cmlwZGF0YS5jc3YNCiogMjAyMzEwLWRpdnZ5LXRyaXBkYXRhLmNzdg0KKiAyMDIzMTEtZGl2dnktdHJpcGRhdGEuY3N2DQoqIDIwMjMxMi1kaXZ2eS10cmlwZGF0YS5jc3YNCg0KQWZ0ZXIgZG93bmxvYWRpbmcgYW5kIHVuemlwcGluZyB0aGUgY3N2IGZpbGUsIHVzaW5nIFIgYW5kIFJzdHVkaW8sIHRoZSBkYXRhc2V0IHdlcmUgcmVhZCBpbnRvIHRoZWlyIG93biBkYXRhIGZyYW1lcyBhbmQgbWVyZ2VkIGludG8gb25lIGRhdGEgZnJhbWUgcmVwcmVzZW50aW5nIHRoZSB3aG9sZSB5ZWFyLiBGcm9tIGhlcmUgd2UgY2FuIGVhc2lseSB2aWV3IGFuZCBtYW5pcHVsYXRlIHRoZSBkYXRhOg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIGNyZWF0ZSBkYXRhIGZyYW1lIGZvciBlYWNoIG1vbnRocyBkYXRhIHNldA0KZGF0YV8wMV9kZiA8LSByZWFkLmNzdigibmV3X2RhdGEvMjAyMzAxLWRpdnZ5LXRyaXBkYXRhLmNzdiIpDQpkYXRhXzAyX2RmIDwtIHJlYWQuY3N2KCJuZXdfZGF0YS8yMDIzMDItZGl2dnktdHJpcGRhdGEuY3N2IikNCmRhdGFfMDNfZGYgPC0gcmVhZC5jc3YoIm5ld19kYXRhLzIwMjMwMy1kaXZ2eS10cmlwZGF0YS5jc3YiKQ0KZGF0YV8wNF9kZiA8LSByZWFkLmNzdigibmV3X2RhdGEvMjAyMzA0LWRpdnZ5LXRyaXBkYXRhLmNzdiIpDQpkYXRhXzA1X2RmIDwtIHJlYWQuY3N2KCJuZXdfZGF0YS8yMDIzMDUtZGl2dnktdHJpcGRhdGEuY3N2IikNCmRhdGFfMDZfZGYgPC0gcmVhZC5jc3YoIm5ld19kYXRhLzIwMjMwNi1kaXZ2eS10cmlwZGF0YS5jc3YiKQ0KZGF0YV8wN19kZiA8LSByZWFkLmNzdigibmV3X2RhdGEvMjAyMzA3LWRpdnZ5LXRyaXBkYXRhLmNzdiIpDQpkYXRhXzA4X2RmIDwtIHJlYWQuY3N2KCJuZXdfZGF0YS8yMDIzMDgtZGl2dnktdHJpcGRhdGEuY3N2IikNCmRhdGFfMDlfZGYgPC0gcmVhZC5jc3YoIm5ld19kYXRhLzIwMjMwOS1kaXZ2eS10cmlwZGF0YS5jc3YiKQ0KZGF0YV8xMF9kZiA8LSByZWFkLmNzdigibmV3X2RhdGEvMjAyMzEwLWRpdnZ5LXRyaXBkYXRhLmNzdiIpDQpkYXRhXzExX2RmIDwtIHJlYWQuY3N2KCJuZXdfZGF0YS8yMDIzMTEtZGl2dnktdHJpcGRhdGEuY3N2IikNCmRhdGFfMTJfZGYgPC0gcmVhZC5jc3YoIm5ld19kYXRhLzIwMjMxMi1kaXZ2eS10cmlwZGF0YS5jc3YiKQ0KDQojIG1lcmdlIGRhdGEgZnJhbWVzIGludG8gb25lIGRhdGEgc2V0IGZvciB0aGUgd2hvbGUgeWVhcg0KdHJpcF9kYXRhX2RmIDwtIHJiaW5kKGRhdGFfMDFfZGYsIGRhdGFfMDJfZGYsIGRhdGFfMDNfZGYsIGRhdGFfMDRfZGYsIA0KICAgICAgICAgICAgICBkYXRhXzA1X2RmLCBkYXRhXzA2X2RmLCBkYXRhXzA3X2RmLCBkYXRhXzA4X2RmLCBkYXRhXzA5X2RmLCANCiAgICAgICAgICAgICAgZGF0YV8xMF9kZiwgZGF0YV8xMV9kZiwgZGF0YV8xMl9kZikNCmBgYA0KDQoNClRoZSBkYXRhIGlzIG9yZ2FuaXplZCBzdWNoIHRoYXQgZWFjaCByb3cgcmVwcmVzZW50cyBhIHNpbmdsZSBiaWtlIHRyaXAgY29udGFpbmluZyB0aGUgZm9sbG93aW5nIG1lYXN1cmVtZW50cyBhbmQgYXR0cmlidXRlczogDQoNCmBgYHtyfQ0KZ2xpbXBzZSh0cmlwX2RhdGFfZGYpDQpgYGANCg0KDQpUaGlzIGluY2x1ZGVzIGRhdGEgZm9yIHJpZGUgSURzLCBiaWtlIHR5cGVzLCBzdGFydCBhbmQgZW5kIHRpbWVzIGZvciB0cmlwcywgdG8gYW5kIGZyb20gc3RhdGlvbiBuYW1lcyBhbmQgSURzLCBzdGFydCBhbmQgZW5kIGxhdGl0dWRlcyBhbmQgbG9uZ2l0dWRlcywgYW5kIHVzZXIgdHlwZXMgKGVpdGhlciBhIG1lbWJlciBvciBjYXVzYWwgcmlkZXIpLg0KDQoNCg0KIyMjIENyZWRpYmlsaXR5IGFuZCBCaWFzIG9mIHRoZSBEYXRhDQoNCkJlaW5nIHRoYXQgdGhlIGRhdGEgaXMgZmlyc3QtcGFydHkgZGF0YSBhbmQgaXMgcHJvdmlkZWQgYnkgdGhlIGJpa2Utc2hhcmUgY29tcGFueSwgZm9yIHRoZSBzYWtlIG9mIHRoaXMgY2FzZSBzdHVkeSB3ZSBhcmUgZ29pbmcgdG8gYXNzdW1lIHRoYXQgdGhlcmUgaXMgbm8gYmlhcyBvciBjcmVkaWJpbGl0eSBjb25jZXJucyB3aXRoIG91ciBkYXRhIHNldC4gSG93ZXZlciwgaW4gYSByZWFsIHNldHRpbmcgYXMgYSBkYXRhIGFuYWx5c3Qgd2hlbiB3b3JraW5nIHdpdGggb3V0c2lkZSBkYXRhLCB5b3Ugc2hvdWxkIGFsd2F5cyBiZSBzdXJlIHRoYXQgdGhlcmUgaXMgbm8gYmlhcyBhbmQgdGhhdCB5b3VyIGRhdGEgaXMgcmVsaWFibGUgYmVmb3JlIGNvbnRpbnVpbmcuIA0KDQoNCg0KIyMjIExpY2Vuc2luZywgUHJpdmFjeSwgU2VjdXJpdHksIGFuZCBBY2Nlc3NpYmlsaXR5IA0KDQpUaGUgZGF0YSBzZXQgaXMgcHJvdmlkZWQgdW5kZXIgYSBEYXRhIExpY2Vuc2UgQWdyZWVtZW50LCB3aGljaCBwZXJtaXRzIHVzZXJzIHRvIGFjY2VzcywgcmVwcm9kdWNlLCBjb3B5LCBtb2RpZnksIGFuYWx5emUsIGFuZCBkaXN0cmlidXRlIHRoZSBkYXRhIHNvIGxvbmcgYXMgaXQgaXMgdXNlZCBhcyBzb3VyY2UgbWF0ZXJpYWwgaW4gYW5hbHlzZXMsIHJlcG9ydHMsIGFuZC9vciBzdHVkaWVzLiBJdCBwcm9oaWJpdHMgdXNlcnMgZnJvbSBob3N0LCBzdHJlYW0sIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIG9yIHNlbGxpbmcgdGhlIGRhdGEgYXMgYSBzdGFuZC1hbG9uZSBkYXRhc2V0IChhcyBzdGF0ZWQgW2hlcmVdKGh0dHBzOi8vZGl2dnliaWtlcy5jb20vZGF0YS1saWNlbnNlLWFncmVlbWVudCkpLiANCg0KUHJpdmFjeSBpc3N1ZXMgaGF2ZSBiZWVuIGFkZHJlc3NlZCBieSB0aGUgcmVtb3ZhbCBvZiBhbnkgcGVyc29uYWxseSBpZGVudGlmaWFibGUgaW5mb3JtYXRpb24gaW4gdGhlIGRhdGFzZXQuIA0KDQpUaGUgZGF0YXNldCBpcyBzZWN1cmVseSBtYW5hZ2VkIG15IE1vdGl2YXRlIEludGVybmF0aW9uYWwgSW5jLiBlbXBsb3llZXMgYW5kIGlzIHN0b3JlZCB1c2luZyBhIHdlbGwta25vd24gY2xvdWQgc2VydmljZXMgcHJvdmlkZXIgaW4gQW1hem9uIFdlYiBTZXJ2aWNlcy4gDQoNCkN5Y2xpc2ljcyBiaWtlLXNoYXJlIGRhdGEgaXMgb3BlbiB0byB0aGUgcHVibGljLCBtZWFuaW5nIGl0IGlzIGFjY2Vzc2libGUgdG8gYW55b25lIHNvIGxvbmcgYXMgdGhlIGxpY2Vuc2UgYWdyZWVtZW50IGlzIGJlaW5nIGFiaWRlZCBieS4gDQoNCg0KIyMjIERhdGEgSW50ZWdyaXR5DQoNClRoZSBkYXRhJ3MgaW50ZWdyaXR5IHdpbGwgYmUgdmVyaWZpZWQgaW4gdGhlIGNsZWFuaW5nIHByb2Nlc3Mgb2YgdGhpcyBhbmFseXNpcy4gSXNzdWVzIHN1Y2ggYXMgbWlzc2luZyBvciBkdXBsaWNhdGUgdmFsdWVzLCBpbmNvcnJlY3QgdmFsdWVzLCBhbmQgaW5jb25zaXN0ZW5jaWVzIHdpbGwgYmUgYWRkcmVzc2VkLiANCg0KDQojIyMgSG93IGRvZXMgdGhlIERhdGEgaGVscCBhbnN3ZXIgdGhlIGJ1c2luZXNzIHF1ZXN0aW9uPw0KDQpUaGUgZGF0YXNldCBjb250YWlucyBhbGwgb2YgdGhlIGluZm9ybWF0aW9uIHdlIHdpbGwgbmVlZCB0byBkZXRlcm1pbmUgaG93IGFubnVhbCBtZW1iZXJzIGFuZCBjYXN1YWwgcmlkZXJzIHVzZSBDeWNsaXN0aWMgYmlrZXMgZGlmZmVyZW50bHkuIERhdGEgc3VjaCBhcyByaWRlciB0eXBlLCBzdGFydCBhbmQgZW5kIHN0YXRpb25zLCBsYXRpdHVkZXMgYW5kIGxvbmdpdHVkZXMsIHN0YXJ0IGFuZCBlbmQgdGltZXMsIGFuZCBiaWtlIHR5cGVzIHdpbGwgaGVscCB1cyBkaWZmZXJlbnRpYXRlIHRoZSB1c2FnZSBpbiBtYW55IHdheXMgaW4gb3JkZXIgdG8gcHJvdmlkZSB2YWx1YWJsZSBpbnNpZ2h0cyB0byBvdXIgc3Rha2Vob2xkZXJzIGFzIHRvIGhvdyBDeWNsaXN0aWMgY2FuIG1heGltaXplIHRoZSBudW1iZXIgb2YgYW5udWFsIG1lbWJlcnNoaXBzIGJlaW5nIHB1cmNoYXNlZC4gDQoNCg0KIyMjIFBvdGVudGlhbCBQcm9ibGVtcw0KDQpQb3RlbnRpYWwgcHJvYmxlbXMgY291bGQgaW5jbHVkZSBpbmNvbnNpc3RlbmNpZXMgaW4gdGhlIGRhdGEsIG1pc3NpbmcgdmFsdWVzLCBvdXRsaWVycyBvZiByaWRlIHRpbWVzLCBvciBkdXBsaWNhdGUgcmVjb3JkcyBmb3IgcmlkZXMuICANCg0KPGJyPg0KPGJyPg0KDQoNCiMgJ1Byb2Nlc3MnIFBoYXNlIG9mIERhdGEgQW5hbHlzaXMgUHJvY2Vzcw0KDQoNCiMjIyBXaGF0IFRvb2xzIHRvIFVzZT8NCg0KSSBzdGFydGVkIG9mZiB1c2luZyBHb29nbGUgU2hlZXRzIGJ1dCBJIGJlZ2FuIGV4cGVyaWVuY2luZyBwZXJmb3JtYW5jZSBpc3N1ZXMgYmVjYXVzZSBvZiBob3cgbGFyZ2UgdGhlIGRhdGFzZXQgaXMuIEkgdGhlbiBzd2l0Y2hlZCB0byB1c2luZyBSc3R1ZGlvIGFuZCB0aGUgUiBwcm9ncmFtbWluZyBsYW5ndWFnZSB0byBjYXJyeSBvdXQgbXkgYW5hbHlzaXMuIFIgaXMgYSBncmVhdCB0b29sIGZvciB0aGlzIGNhc2Ugc3R1ZHkgYmVjYXVzZSBpdCBjYW4gaGFuZGxlIGxhcmdlIGRhdGFzZXRzIChvciBhcyBiaWcgd2hhdCBjYW4gZml0IGluIFJBTSBjb21mb3J0YWJseSkuIEl0IGFsc28gaGFzIGEgcGFja2FnZSBjYWxsZWQgInRpZHl2ZXJzZSIsIHdoaWNoIGlzIGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyBzcGVjaWZpY2FsbHkgZGVzaWduZWQgZm9yIHdvcmtpbmcgd2l0aCBkYXRhLiBUaGlzIGluY2x1ZGVzIGRhdGEgY2xlYW5pbmcsIG1hbmlwdWxhdGlvbiwgZXhwbG9yYXRpb24sIGFuZCB2aXN1YWxpemF0aW9uLiANCg0KIyMjIEVuc3VyaW5nIERhdGEgSW50ZWdyaXR5IChDbGVhbmluZyB0aGUgRGF0YSkNCg0KRmlyc3QsIHdlJ2xsIHN0YXJ0IGJ5IG1ha2luZyBhIGNvcHkgb2YgdGhlIGRhdGFzZXQgYW5kIHN0b3JpbmcgaXQgaW4gdGhlIGRhdGEgZnJhbWUgdHJpcF9kYXRhX2NsZWFuX2RmLiBJdCBpcyBhbHdheXMgZ29vZCBwcmFjdGljZSB0byBub3QgbW9kaWZ5IHRoZSBvcmlnaW5hbCBkYXRhc2V0IHVubGVzcyBnaXZlbiBzcGVjaWZpYyBpbnN0cnVjdGlvbnMgdG8gZG8gc28uIFRoaXMgaXMgc28gaWYgYW55dGhpbmcgZ29lcyB3cm9uZywgd2UgY2FuIGFsd2F5cyByZWZlciBiYWNrIHRvIHRoZSBvcmlnaW5hbCBkYXRhIHdpdGhvdXQgd29ycnlpbmcgYWJvdXQgY29tcHJvbWlzaW5nIGl0LiBXZSdsbCBhbHNvIHN0YXJ0IGJ5IHJlbW92aW5nIGFueSBudWxsIHZhbHVlcyB0aGF0IGEgcmVjb3JkIG1heSBjb250YWluLiBTaW5jZSBlYWNoIGNvbHVtbiBhdHRyaWJ1dGUgaXMgc29tZXRoaW5nIHdlIHdpbGwgdXNlIGluIG91ciBhbmFseXNpcywgd2UgbmVlZCB0byBtYWtlIHN1cmUgZWFjaCBjZWxsIGNvbnRhaW5zIGEgdmFsdWUuDQoNCmBgYHtyfQ0KIyBNYWtlIGNvcHkgb2Ygb3JpZ2luYWwgZGF0YSBzZXQgZm9yIGNsZWFuaW5nIGFuZCByZW1vdmluZyBudWxsIHZhbHVlcyBpbiByZWNvcmRzDQp0cmlwX2RhdGFfY2xlYW5fZGYgPC0gdHJpcF9kYXRhX2RmICU+JSANCiAgZHJvcF9uYSgpDQojIEV4YW1pbmUgZGF0YSBzZXQNCmhlYWQodHJpcF9kYXRhX2NsZWFuX2RmKQ0KYGBgDQoNCjxicj4NCg0KIyMjIyBDbGVhbmluZyB1cCBDb2x1bW4gTmFtZXMNCg0KQXQgZmlyc3QgZ2xhbmNlIEkgbm90aWNlZCBzb21lIG9mIHRoZSBjb2x1bW5zIG5hbWVzIGFyZSBub3QgdmVyeSBkZXNjcmlwdGl2ZSwgd2hpY2ggaW5jbHVkZSBzdGFydGVkX2F0LCBlbmRlZF9hdCwgYW5kIG1lbWJlcl9jYXN1YWwuIENoYW5naW5nIHRoZXNlIG5hbWVzIHRvIHN0YXJ0X3RpbWUsIGVuZF90aW1lLCBhbmQgdXNlcl90eXBlIHdvdWxkIG1ha2UgdGhlbSBsZXNzIGFtYmlndW91cyBhbmQgY2xlYXJlciBhcyB0byB3aGF0IHRoZXkgcmVwcmVzZW50Lg0KDQpgYGB7cn0NCnRyaXBfZGF0YV9jbGVhbl9kZiA8LSB0cmlwX2RhdGFfY2xlYW5fZGYgJT4lIA0KICByZW5hbWUoc3RhcnRfdGltZSA9IHN0YXJ0ZWRfYXQsIGVuZF90aW1lID0gZW5kZWRfYXQsIHVzZXJfdHlwZSA9IG1lbWJlcl9jYXN1YWwpDQoNCmNvbG5hbWVzKHRyaXBfZGF0YV9jbGVhbl9kZikNCmBgYA0KDQo8YnI+DQoNCiMjIyMgQ2hlY2sgZm9yIER1cGxpY2F0ZSBSZWNvcmRzDQoNClRvIG1ha2Ugd2UgYXJlIG5vdCB1c2luZyBkdXBsaWNhdGUgcmVjb3JkcyBpbiBvdXIgYW5hbHlzaXMgd2UgY2FuIHVzZSB0aGUgZHVwbGljYXRlZCBmdW5jdGlvbiBuZXN0ZWQgaW5zaWRlIHRoZSBzdW0gZnVuY3Rpb24gd2hpY2ggd2lsbCBnaXZlIHVzIHRoZSB0b3RhbCBudW1iZXIgb2YgZHVwbGljYXRlcyBpbiB0aGUgZGF0YXNldC4gDQpgYGB7cn0NCiMgTnVtYmVyIG9mIGR1cGxpY2F0ZSByZWNvcmRzDQpzdW0oZHVwbGljYXRlZCh0cmlwX2RhdGFfY2xlYW5fZGYpKQ0KYGBgDQoNCldlIGdvdCBhIHN1bSBvZiB6ZXJvIGR1cGxpY2F0ZXMgc28gdGhhdCBtZWFucyB3ZXJlIGFyZSBub3QgdXNpbmcgcmVwZWF0ZWQgZGF0YSBpbiBvdXIgYW5hbHlzaXMuIA0KDQo8YnI+DQoNCiMjIyMgQ29udmVyc2lvbiBvZiBTdGFydCBhbmQgRW5kIFRpbWVzDQoNClRoZSBzdGFydCBhbmQgZW5kIHRpbWVzIGFyZSBvZiBhIGNoYXJhY3RlciBmb3JtYXQgaW4gdGhlIGZvcm0gJ1lZWVktTU0tREQgSDpNOlMgVVRDJy4gVG8gbWFrZSB0aGVtIGVhc2llciB0byB3b3JrIHdpdGggaW4gdGVybXMgb2YgbWFuaXB1bGF0aW9uIGFuZCBjYWxjdWxhdGlvbiBvZiBkYXRlcyBhbmQgdGltZXMsIHdlIGFyZSBnb2luZyB0byBjb3ZlcnQgdGhlbSB0byBjbGFzcyBQT1NJWGN0LiBUaGlzIGRhdGEgdHlwZSBtYWtlcyB3b3JraW5nIHdpdGggZGF0ZS10aW1lIG9iamVjdHMgZWFzaWVyLCBhcyBvcHBvc2VkIHRvIGlmIHRoZXkgd2VyZSBsZWZ0IGFzIGNoYXJhY3RlciBzdHJpbmdzLg0KDQpgYGB7cn0NCiMgQ29udmVydGluZyBzdGFydCBhbmQgZW5kIHRpbWVzIHRvIFBPU0lYY3QgY2xhc3MNCnRyaXBfZGF0YV9jbGVhbl9kZiA8LSB0cmlwX2RhdGFfY2xlYW5fZGYgJT4lIA0KICBtdXRhdGUoc3RhcnRfdGltZSA9IGFzLlBPU0lYY3QodHJpcF9kYXRhX2NsZWFuX2RmJHN0YXJ0X3RpbWUsIGZvcm1hdCA9ICIlWS0lbS0lZCAlSDolTTolUyIpKSAlPiUgDQogIG11dGF0ZShlbmRfdGltZSA9IGFzLlBPU0lYY3QodHJpcF9kYXRhX2NsZWFuX2RmJGVuZF90aW1lLCBmb3JtYXQgPSAiJVktJW0tJWQgJUg6JU06JVMiKSkNCmBgYA0KDQoNCkZyb20gaGVyZSB3ZSBjYW4gbm93IGZpbHRlciBvdXQgcm93cyBpbiB3aGljaCB0aGUgc3RhcnQgdGltZSBvZiBiaWtlIHJpZGVzIGlzIGdyZWF0ZXIgdGhhbiBvciBlcXVhbCB0byBlbmQgdGltZXMuIFRoaXMgaXMgaW1wb3J0YW50IGJlY2F1c2Ugd2UgZG8gbm90IHdhbnQgcmlkZSB0aW1lcyB0aGF0IGFyZSBoYXZlIGEgdGltZSBvZiB6ZXJvIG9yIG5lZ2F0aXZlIHRpbWUsIHdoaWNoIGlzIGltcG9zc2libGUsIHdoZW4gdGhlIG5ldyBjb2x1bW4gZm9yIHJpZGUgdGltZXMgaW4gc2Vjb25kcyBpcyBjcmVhdGVkLiBXZSBjYW4gYWxzbyBjcmVhdGUgYSBjb2x1bW4gZm9yIHRoZSBkYXkgb2YgdGhlIHdlZWsgaW4gd2hpY2ggcmlkZXMgd2VyZSB0YWtlbiwgZXh0cmFjdGVkIGZyb20gdGhlIHN0YXJ0IHRpbWUgY29sdW1uLg0KDQpgYGB7cn0NCnRyaXBfZGF0YV9jbGVhbl9kZiA8LSB0cmlwX2RhdGFfY2xlYW5fZGYgJT4lIA0KICBmaWx0ZXIoZW5kX3RpbWUgPiBzdGFydF90aW1lKSAlPiUgICMgZmlsdGVyIG91dCBzdGFydF90aW1lID49IGVuZF90aW1lDQogIG11dGF0ZShyaWRlX3RpbWVfc2VjID0gZGlmZnRpbWUoZW5kX3RpbWUsIHN0YXJ0X3RpbWUpKSAlPiUgICMgY2FsY3VsYXRlIHJpZGUgdGltZSBpbiBzZWNvbmRzDQogIG11dGF0ZShkYXlfb2Zfd2VlayA9IHdlZWtkYXlzKHN0YXJ0X3RpbWUpKSAjIGdldCBkYXkgb2Ygd2VlayByaWRlIHdhcyB0YWtlbg0KYGBgDQoNCmBgYHtyfQ0KdHJpcF9kYXRhX2NsZWFuX2RmICU+JSANCiAgc2VsZWN0KHJpZGVfdGltZV9zZWMsIGRheV9vZl93ZWVrKQ0KYGBgDQoNCjxicj4NCg0KIyMjIEFkZGl0aW9uYWwgQ29uY2VybnMNCg0KV2hpbGUgc2VhcmNoaW5nIGZvciBlcnJvcnMgaW4gdGhlIGRhdGEsIEkgbm90aWNlZCB0aGF0IHNvbWUgc3RhdGlvbiBuYW1lcyBoYXZlIG1vcmUgdGhhbiBvbmUgSUQgYXNzb2NpYXRlZCB3aXRoIHRoZW0uIEkgdmVyaWZpZWQgd2hpY2ggc3RhdGlvbnMsIGhvdyBtYW55IElEcyB3ZXJlIGFzc29jaWF0ZWQgd2l0aCB0aGVtLCBhbmQgd2hhdCB0aGUgSURzIHdlcmUgd2l0aCB0aGlzOiANCg0KYGBge3J9DQojIGNoZWNrcyB3aGljaCBzdGFydF9zdGF0aW9uIGhhdmUgbW9yZSB0aGFuIG9uZSB1bmlxdWUgaWRzIGFuZCBsaXN0cyB0aGUgaWRzIGFzc29jaWF0ZWQgd2l0aCB0aGVtDQp0cmlwX2RhdGFfY2xlYW5fZGYgJT4lIA0KICBncm91cF9ieShzdGFydF9zdGF0aW9uX25hbWUpICU+JSANCiAgc3VtbWFyaXNlKG51bV91bmlxdWVfaWRzID0gbl9kaXN0aW5jdChzdGFydF9zdGF0aW9uX2lkKSwgaWRfbGlzdD1wYXN0ZSh1bmlxdWUoc3RhcnRfc3RhdGlvbl9pZCksIGNvbGxhcHNlID0gIiwgIikpICU+JSANCiAgZmlsdGVyKG51bV91bmlxdWVfaWRzID4gMSkNCmBgYA0KDQpUaGlzIGNvdWxkIGJlIGEgcHJvYmxlbSBpZiB3ZSBuZWVkZWQgdG8gZXh0cmFjdCBpbmZvcm1hdGlvbiBhYm91dCBjZXJ0YWluIHN0YXRpb25zIHdpdGggdGhlaXIgSUQsIGhvd2V2ZXIgZm9yIHRoaXMgYW5hbHlzaXMsIHdlIGFyZSBub3QgdG9vIGNvbmNlcm5lZCB3aXRoIGFjY2Vzc2luZyBhIHN0YXRpb24ncyBJRCBzbyBpdCB3aWxsIGJlIGxlZnQgdW5jaGFuZ2VkLiBJZiBJIHdlcmUgdG8gZml4IHRoZXNlIHN0YXRpb24ncyBJRHMsIEkgd291bGQgZnVydGhlciBpbnZlc3RpZ2F0ZSBlYWNoIHN0YXRpb24gdG8gZGV0ZXJtaW5lIHdoaWNoIElEIGlzIHVzZWQgbW9yZSBvZnRlbiBhbmQgZW5zdXJlIHRoYXQgZWFjaCByZWNvcmQgdGhhdCBjb250YWlucyB0aGF0IHN0YXRpb24gd2lsbCBoYXZlIHRoZSBtb3JlIGZyZXF1ZW50IElEIGFzc29jaWF0ZWQgd2l0aCBpdC4gDQoNCjxicj4NCjxicj4NCg0KDQojICdBbmFseXplJyBQaGFzZSBvZiBEYXRhIEFuYWx5c2lzIFByb2Nlc3MNCg0KDQpOb3cgdGhhdCB3ZSBoYXZlIGEgY2xlYW4gZGF0YXNldCwgd2UgY2FuIHBlcmZyb20gZXhwbG9yYXRvcnkgYW5hbHlzaXMgdG8gaWRlbnRpZnkgdHJlbmRzIGFuZCByZWxhdGlvbnNoaXBzIGFtb25nIHRoZSBkYXRhIHRvIGdpdmUgdXMgaW5zaWdodHMgdGhhdCBjb3VsZCBoZWxwIGFuc3dlciBvdXIgYnVzaW5lc3MgcXVlc3Rpb24uIA0KDQojIyMgS2V5IFRhc2tzDQoqIEFnZ3JlZ2F0ZSBkYXRhIHNvIHRoYXQgaXQgaXMgdXNlZnVsIGFuZCBhY2Nlc3NpYmxlDQoqIE9yZ2FuaXplIGFuZCBmb3JtYXQgZGF0YQ0KKiBQZXJmb3JtIGNhbGN1bGF0aW9ucw0KKiBJZGVudGlmeSB0cmVuZHMgYW5kIHJlbGF0aW9uc2hpcHMNCg0KPGJyPg0KPGJyPg0KDQojIyMjIFVzZXJ0eXBlIHZzLiBSaWRlIFRpbWUNCg0KVGhlIGFpbSBvZiB0aGlzIGFuYWx5c2lzIGlzIHRvIGlkZW50aWZ5IHRoZSBkaWZmZXJlbmNlIGluIHJpZGUgdGltZXMgb2YgY2FzdWFsIGFuZCBtZW1iZXIgcmlkZXJzOg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCiMgVXNlciB0eXBlIHZzIG1lYW4gcmlkZSB0aW1lDQptZWFuX3JpZGVfdGltZXMgPC0gdHJpcF9kYXRhX2NsZWFuX2RmICU+JSANCiAgZ3JvdXBfYnkodXNlcl90eXBlKSAlPiUgDQogIHN1bW1hcmlzZShtZWFuX3JpZGVfdGltZV9zZWMgPSBtZWFuKHJpZGVfdGltZV9zZWMpLCBtYXhfcmlkZV90aW1lPW1heChyaWRlX3RpbWVfc2VjKSwgDQogICAgICAgICAgICBtaW5fcmlkZV90aW1lPW1pbihyaWRlX3RpbWVfc2VjKSkNCg0KbWVhbl9yaWRlX3RpbWVzDQoNCiMgQ2F1c2FsIFJpZGVyOiBqdXN0IG92ZXIgMjAgbWluIDMwIHNlYyBhdmVyYWdlIHJpZGUgdGltZQ0KIyBNZW1iZXIgUmlkZXI6IGp1c3Qgb3ZlciAxMiBtaW4gYXZlcmFnZSByaWRlIHRpbWUNCmdncGxvdChkYXRhPW1lYW5fcmlkZV90aW1lcykgKyANCiAgZ2VvbV9jb2wobWFwcGluZz1hZXMoeD11c2VyX3R5cGUsIHk9bWVhbl9yaWRlX3RpbWVfc2VjLGZpbGw9dXNlcl90eXBlKSkgKw0KICBnZW9tX3RleHQoYWVzKHg9dXNlcl90eXBlLCB5PW1lYW5fcmlkZV90aW1lX3NlYywgbGFiZWw9cm91bmQobWVhbl9yaWRlX3RpbWVfc2VjLCAyKSksdmp1c3Q9MSkgKw0KICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgUmlkZSBUaW1lcyBieSBVc2VydHlwZSIsIHg9IlVzZXJ0eXBlIix5PSJBdmcuIFJpZGUgVGltZShTZWMpIikNCg0KYGBgDQoNCg0KPGI+QW5hbHlzaXM6PC9iPg0KDQoNCiogQ2FzdWFsIHJpZGVycyBoYXZlIGEgaGlnaGVyIGF2ZXJhZ2UgcmlkZSB0aW1lIHRoYW4gcmlkZXJzIHdpdGggYW5udWFsIG1lbWJlcnNoaXBzIGJ5IGFib3V0IDggbWludXRlcyBhbmQgMzAgc2Vjb25kcy4gDQoqIEFjY29yZGluZyB0byB0aGUgbWF4X3JpZGVfdGltZSBjb2x1bW4gaW4gdGhlIHBpdm90IHRhYmxlLCB0aGUgbWF4IHJpZGUgdGltZSBmb3IgY2FzdWFsIHJpZGVycyBpcyA3MjgsMTc4IHNlY29uZHMgd2hpY2ggZXF1YWwgdG8gYWJvdXQgZWlnaHQgYW5kIGhhbGYgZGF5cyBvZiByaWRlIHRpbWUuIEFzc3VtaW5nIHRoYXQgdGhpcyBpcyBhbiBvdXRsaWVyIHN0YXRpc3RpYywgbXkgYmVzdCBndWVzcyBpcyB0aGF0IHRoZSByaWRlciBkdXJpbmcgdGhpcyBiaWtlIHRyaXAgbWF5IGhhdmUgZm9yZ290dGVuIHRvIGRvY2sgdGhlaXIgYmlrZSBhdCBhIGJpa2Ugc3RhdGlvbiwgd2hpY2ggY291bGQgaGF2ZSBsZWQgdG8gc3VjaCBhIGhpZ2ggcmlkZSB0aW1lLiBUaGUgc2FtZSBnb2VzIGZvciB0aGUgbWF4IHJpZGUgdGltZSBmb3IgdGhlIG1lbWJlcnNoaXAgcmlkZXIsIGJ1dCBvbmx5IGVxdWFscyB0byBqdXN0IG92ZXIgYSAyNCBob3VycyBvZiByaWRlIHRpbWUuIA0KKiBIb3dldmVyLCB3aXRoIGNhc3VhbCByaWRlcnMgaGF2aW5nIGEgaGlnaGVyIGF2ZXJhZ2UgcmlkZSB0aW1lIGNvdWxkIGluZGljYXRlIHRoYXQgdGhleSB1c2UgQ3ljbGlzdGljIGJpa2UgbW9yZSBsZWlzdXJlbHkgdGhhbiBtZW1iZXJzLg0KDQo8YnI+DQo8YnI+DQoNCiMjIyMgV2hpY2ggZGF5cyBkbyBkaWZmZXJlbnQgcmlkZXJzIHVzZSBDeWNsaXN0aWMgYmlrZXM/DQoNClRvIGdldCBhIGJldHRlciBzZW5zZSBvZiB3aGVuIGNhc3VhbCBhbmQgbWVtYmVyIHJpZGVycyB1c2UgYmlrZXMsIHdlIGNhbiBjb3VudCB0aGUgbnVtYmVyIG9mIHJpZGVzIGVhY2ggdHlwZSBvZiByaWRlciB0b29rIGZvciBlYWNoIHdlZWtkYXkgb2YgdGhlIHllYXIuIFdoaWxlIGFnZ3JlZ2F0aW5nIG15IGRhdGEgaW50byBhIHVzZWZ1bCBkYXRhIGZyYW1lLCBJIGNoYW5nZWQgdGhlIHR5cGUgb2YgdGhlIGRheV9vZl93ZWVrIGNvbHVtbiBmcm9tIGEgY2hhcmFjdGVyIHN0cmluZyB0byBhbiBvcmRpbmFsIGZhY3Rvci4gVGhpcyBtYWRlIGl0IGVhc2llciB0byBzb3J0LCBvcmdhbml6ZSwgYW5kIGNvdW50IHRoZSBkYXRhLg0KDQpgYGB7cn0NCiMgQWdncmVnYXRlIGFuZCBvcmdhbml6ZSBkYXRhIGJ5IHdlZWsgZGF5IGNvdW50cyBmb3IgZWFjaCB0eXBlIG9mIHJpZGVyDQp3ZWVrZGF5X3JpZGVzIDwtIHRyaXBfZGF0YV9jbGVhbl9kZiAlPiUgDQogIG11dGF0ZShkYXlfb2Zfd2VlayA9IHdkYXkoc3RhcnRfdGltZSwgbGFiZWw9VFJVRSkpICU+JSANCiAgZ3JvdXBfYnkoZGF5X29mX3dlZWssIHVzZXJfdHlwZSkgJT4lIA0KICBzdW1tYXJpc2UoLmdyb3Vwcz0iZHJvcCIscmlkZV9jb3VudD1uKCkpICU+JSANCiAgYXJyYW5nZShkYXlfb2Zfd2VlaykNCg0KZ2dwbG90KGRhdGEgPSB3ZWVrZGF5X3JpZGVzKSArIA0KICBnZW9tX2NvbChtYXBwaW5nID0gYWVzKHg9ZGF5X29mX3dlZWssIHk9cmlkZV9jb3VudCxmaWxsPXVzZXJfdHlwZSkscG9zaXRpb24gPSAnZG9kZ2UnKSArDQogIGxhYnModGl0bGUgPSAiUmlkZSBDb3VudCBQZXIgRGF5IG9mIFdlZWsiLCB4PSJEYXkgb2YgV2VlayIseT0iTnVtYmVyIG9mIFJpZGVzIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkNCmBgYA0KDQoNCjxiPkFuYWx5c2lzOjwvYj4NCg0KDQoqIE1lbWJlcnNoaXAgcmlkZXJzIHRlbmQgdG8gdXNlIEN5Y2xpc3RpYyBiaWtlcyBtb3JlIGR1cmluZyB0aGUgd29yayB3ZWVrIChNb25kYXktRnJpZGF5KSwgd2hpbGUgY2FzdWFsIHJpZGVycyB1c2UgdGhlIGJpa2VzIG1vcmUgb24gd2Vla2VuZHMuIA0KKiBBZ2FpbiwgdGhpcyBjb3VsZCBpbmRpY2F0ZSBtb3JlIGxlaXN1cmUtb3JpZW50ZWQgcmlkaW5nIGZvciBjYXN1YWwgcmlkZXJzIGxlYWRpbmcgdG8gdGhlIGhpZ2hlciByaWRlIHRpbWVzLiANCiogSXQgc2VlbXMgdGhhdCBtZW1iZXIgcmlkZXJzIG1heSB1c2UgdGhlaXIgYmlrZSByaWRlcyBtb3JlIGZvciBjb21tdXRpbmcgb3IgcHVibGljIHRyYW5zcG9ydC4gTWF5YmUgdG8gYW5kIGZyb20gd29yayBvciBhbm90aGVyIHByZS1wbGFubmVkIHRyaXAuIA0KDQo8YnI+DQo8YnI+DQoNCiMjIyMgRG8gY2FzdWFsIGFuZCBtZW1iZXIgcmlkZXJzIGRpZmZlciBpbiB0aGUgdGltZSBvZiB5ZWFyIGluIHdoaWNoIHRoZXkgcmlkZT8NCg0KYGBge3J9DQojIHdoaWNoIG1vbnRocyBkbyByaWRlcyBvY2N1ciB0aGUgbW9zdA0KdHJpcF9kYXRhX2NsZWFuX2RmICU+JSANCiAgbXV0YXRlKG1vbnRoX29mX3JpZGUgPSBtb250aChzdGFydF90aW1lLCBsYWJlbD1UUlVFKSkgJT4lIA0KICBncm91cF9ieShtb250aF9vZl9yaWRlLCB1c2VyX3R5cGUpICU+JSANCiAgc3VtbWFyaXNlKC5ncm91cHM9ImRyb3AiLCByaWRlc19wZXJfbW9udGggPSBuKCkpICU+JSANCiAgYXJyYW5nZShtb250aF9vZl9yaWRlKSAlPiUgDQogIGdncGxvdCgpICsgDQogIGdlb21fbGluZShtYXBwaW5nPWFlcyh4PW1vbnRoX29mX3JpZGUseT1yaWRlc19wZXJfbW9udGgsIA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3I9dXNlcl90eXBlLCBncm91cD11c2VyX3R5cGUpKSArDQogIGxhYnModGl0bGU9IlJpZGVzIHBlciBNb250aCBieSBVc2VydHlwZSIseD0iTW9udGgiLHk9Ik51bWJlciBvZiBSaWRlcyIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6Y29tbWEpDQpgYGANCg0KDQo8Yj5BbmFseXNpczo8L2I+DQoNCiogQWJvdXQgd2hhdCBJIGV4cGVjdGVkLCBiaWtlIHJpZGVzIGZvciBib3RoIHR5cGVzIG9mIHJpZGVycyBvY2N1ciBtb3JlIGluIHRoZSB3YXJtZXIgbW9udGhzLiANCiogVGhlIHBlYWsgbnVtYmVyIG9mIGJpa2UgcmlkZXMgZm9yIG1lbWJlcnMgaGFwcGVuZWQgaW4gQXVndXN0IGFuZCBmb3IgY2FzdWFsIHJpZGVycyBpbiBKdWx5DQoqIEJpa2UgcmlkZXMgc3RhcnQgdG8gaW5jcmVhc2UgaW4gdGhlIHNwcmluZyBhcyB0ZW1wZXJhdHVyZXMgc3RhcnQgdG8gcmlzZSBhbmQgZGVjcmVhc2UgaW4gdGhlIGZhbGwgYXMgdGVtcGVyYXR1cmVzIGRlY3JlYXNlLg0KDQo8YnI+DQo8YnI+DQoNCiMjIyMgUmlkZWFibGUgVHlwZSBQcmVmZXJlbmNlIGZvciBDYXN1YWwgYW5kIE1lbWJlciBSaWRlcnMNCg0KVG8gZnVydGhlciBpbnZlc3RpZ2F0ZSBob3cgY2FzdWFsIGFuZCBtZW1iZXIgcmlkZXJzIHVzZSBiaWtlcyBkaWZmZXJlbnRseSwgd2UgY2FuIHNlZSB3aGljaCB0eXBlIG9mIGJpa2UgZWFjaCBwcmVmZXIgYmFzZWQgb24gd2hpY2ggdHlwZSBvZiBiaWtlcyB3ZXJlIHVzZWQgZm9yIHJpZGVzIGluIHRoZSBwYXN0IDEyIG1vbnRocy4gKE5vdGU6IFdoZW4gb3JnYW5pemluZyBkYXRhIGZvciBjYXN1YWwgcmlkZXJzLCBJIG5vdGljZWQgdGhlcmUgd2FzIGEgdGhpcmQgdHlwZSBvZiBiaWtlLCBkb2NrZWRfYmlrZSwgd2hpY2ggaXMganVzdCBhIGJpa2UgZG9ja2VkIGF0IGEgc3RhdGlvbiBzbyBJIGV4Y2x1ZGVkIGl0IGZyb20gbXkgYW5hbHlzaXMpDQoNCmBgYHtyfQ0KIyBBbmFseXNpbmcgcmlkZWFibGUgYmlrZSB0eXBlcyBwZXIgdXNlcnR5cGUNCg0KIyBDYWxjdWxhdGluZyB0aGUgbnVtYmVyIG9mIHJpZGVzIGFuZCBwZXJjZW50IG9mIHRvdGFsIHJpZGVzIHRha2VuIHdpdGggZWFjaA0KIyB0eXBlIG9mIGJpa2UgZm9yIGNhc3VhbCByaWRlcnMNCmNfcmlkZWFibGUgPC0gdHJpcF9kYXRhX2NsZWFuX2RmICU+JSANCiAgZmlsdGVyKHVzZXJfdHlwZSA9PSAiY2FzdWFsIikgJT4lIA0KICBmaWx0ZXIocmlkZWFibGVfdHlwZSAhPSAiZG9ja2VkX2Jpa2UiKSAlPiUgDQogIGdyb3VwX2J5KHJpZGVhYmxlX3R5cGUpICU+JSANCiAgc3VtbWFyaXNlKG51bV9yaWRlcyA9IG4oKSkgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBtdXRhdGUocGVyY2VudF90b3RhbF9yaWRlcyA9IG51bV9yaWRlcy9zdW0obnVtX3JpZGVzKSAqIDEwMCkNCg0KIyBQaWUgY2hhcnQgZGlzcGxheWluZyBwZXJjZW50YWdlcyBvZiBlYWNoIGJpa2UgdXNlZCBmb3IgY2FzdWFsIHJpZGVycw0KY2FzdWFsX3BpZSA8LSBnZ3Bsb3QoZGF0YSA9IGNfcmlkZWFibGUsIGFlcyh4ID0gIiIsIHkgPSBwZXJjZW50X3RvdGFsX3JpZGVzLCBmaWxsID0gcmlkZWFibGVfdHlwZSkpICsNCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMSkgKw0KICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IHBhc3RlMChyb3VuZChwZXJjZW50X3RvdGFsX3JpZGVzLCAxKSwgIiUiKSksIA0KICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLHNob3cubGVnZW5kID0gRkFMU0UpICsNCiAgY29vcmRfcG9sYXIodGhldGEgPSAieSIpICsNCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgUmlkZXMgYnkgUmlkZWFibGUgVHlwZSAoQ2FzdWFsKSIsDQogICAgICAgZmlsbCA9ICJSaWRlYWJsZSBUeXBlIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQ0KICANCg0KIyBDYWxjdWxhdGluZyB0aGUgbnVtYmVyIG9mIHJpZGVzIGFuZCBwZXJjZW50IG9mIHRvdGFsIHJpZGVzIHRha2VuIHdpdGggZWFjaA0KIyB0eXBlIG9mIGJpa2UgZm9yIGNhc3VhbCByaWRlcnMNCm1fcmlkZWFibGUgPC0gdHJpcF9kYXRhX2NsZWFuX2RmICU+JSANCiAgZmlsdGVyKHVzZXJfdHlwZSA9PSAibWVtYmVyIikgJT4lIA0KICBncm91cF9ieShyaWRlYWJsZV90eXBlKSAlPiUgDQogIHN1bW1hcmlzZShudW1fcmlkZXMgPSBuKCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKHBlcmNlbnRfdG90YWxfcmlkZXMgPSBudW1fcmlkZXMvc3VtKG51bV9yaWRlcykqMTAwKQ0KDQojIFBpZSBjaGFydCBkaXNwbGF5aW5nIHBlcmNlbnRhZ2VzIG9mIGVhY2ggYmlrZSB1c2VkIGZvciBtZW1iZXIgcmlkZXJzDQptZW1iZXJfcGllIDwtIGdncGxvdChkYXRhID0gbV9yaWRlYWJsZSwgYWVzKHggPSAiIiwgeSA9IHBlcmNlbnRfdG90YWxfcmlkZXMsIGZpbGwgPSByaWRlYWJsZV90eXBlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgd2lkdGggPSAxKSArDQogIGdlb21fbGFiZWwoYWVzKGxhYmVsID0gcGFzdGUwKHJvdW5kKHBlcmNlbnRfdG90YWxfcmlkZXMsIDEpLCAiJSIpKSwgDQogICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSksc2hvdy5sZWdlbmQgPSBGQUxTRSkgKw0KICBjb29yZF9wb2xhcih0aGV0YSA9ICJ5IikgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBSaWRlcyBieSBSaWRlYWJsZSBUeXBlIChNZW1iZXIpIiwNCiAgICAgICBmaWxsID0gIlJpZGVhYmxlIFR5cGUiKSArDQogIHRoZW1lX3ZvaWQoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpDQoNCmNhc3VhbF9waWUNCm1lbWJlcl9waWUNCmBgYA0KDQoNCjxiPkFuYWx5c2lzOjwvYj4NCg0KDQoqIEFubnVhbCBtZW1iZXJzIGRvIG5vdCByZWFsbHkgc2hvdyBhIHByZWZlcmVuY2UgZm9yIG9uZSBiaWtlIHR5cGUgb3IgdGhlIG90aGVyLCBhcyB0aGV5IHVzZSBib3RoIGVsZWN0cmljIGFuZCBjbGFzc2ljIGJpa2VzIHByZXR0eSBldmVubHkuIA0KKiBDYXN1YWwgcmlkZXJzIHNob3cgYSBzbGlnaHQgcHJlZmVyZW5jZSBmb3IgZWxlY3RyaWMgYmlrZXMgb3ZlciBjbGFzc2ljIGJpa2VzLiBBY2NvcmRpbmcgdG8gdGhlIHBpZSBjaGFydCwgYWJvdXQgMTEuNiUgbW9yZSByaWRlcyB3ZXJlIHRha2VuIHdpdGggZWxlY3RyaWMgYmlrZXMgYnkgY2FzdXNhbCByaWRlcnMuIA0KDQo8YnI+DQo8YnI+DQoNCiMjIyMgSWRlbnRpZnlpbmcgdGhlIFRvcCA1IFN0YXJ0aW5nIFN0YXRpb25zIGZvciBDYXN1YWwgYW5kIE1lbWJlciBSaWRlcnMNCg0KSXQgaXMgaW1wb3J0YW50IHRvIGlkZW50aWZ5IHRoZSBtb3N0IHBvcHVsYXIgc3RhcnQgc3RhdGlvbnMgcmlkZXJzIGdvIHRvIGZvciB0aGVpciBiaWtlIHJpZGVzLiBLbm93aW5nIHRoZSBkaWZmZXJlbmNlIGluIHdoZXJlIGNhc3VhbCByaWRlcnMgbGlrZSB0byBnbyBmb3IgdGhlaXIgYmlrZXMgYW5kIHdoZXJlIG1lbWJlcnMgZ28gY2FuIGdpdmUgaW5zaWdodHMgdG8gc3Rha2Vob2xkZXJzIGluIGRldGVybWluaW5nIHdoaWNoIGFyZWEgd291bGQgYmUgYmVzdCB0byB0YXJnZXQgd2hlbiB0cnlpbmcgdG8gY29udmVydCBjYXN1YWwgcmlkZXJzIHRvIGFubnVhbCBtZW1iZXJzLiANCg0KYGBge3J9DQojIyMjIyMjIyBJZGVudGlmeSB0aGUgdG9wIDUgc3RhcnRpbmcgc3RhdGlvbnMgZm9yIGVhY2ggdXNlcnR5cGUNCg0KIyBzZWxlY3RpbmcgdXNlcl90eXBlLCBzdGFydF9zdGF0aW9uX25hbWUsIGFuZCByaWRlYWJsZV90eXBlIGZyb20gYW5kIHN0b3JpbmcgDQojIGluIGEgZGF0YSBmcmFtZSBzdGF0aW9uX2RhdGENCnN0YXRpb25fZGF0YSA8LSB0cmlwX2RhdGFfY2xlYW5fZGYgJT4lIA0KICBmaWx0ZXIocmlkZWFibGVfdHlwZSAhPSAiZG9ja2VkX2Jpa2UiKSAlPiUgDQogIHNlbGVjdCh1c2VyX3R5cGUsIHN0YXJ0X3N0YXRpb25fbmFtZSwgcmlkZWFibGVfdHlwZSkgJT4lIA0KICBkcm9wX25hKCkNCg0KIyBHcm91cCBkYXRhIGJ5IHVzZXJfdHlwZSwgc3RhcnRfc3RhdGlvbl9uYW1lLCBhbmQgcmlkZWFibGUgdHlwZSwgY2FsY3VsYXRlIA0KIyBudW1iZXIgb2YgcmlkZXMgZm9yIGVhY2ggY29tYmluYXRpb24NCnN0YXRpb25fc3VtbWFyeSA8LSBzdGF0aW9uX2RhdGEgJT4lIA0KICBmaWx0ZXIoc3RhcnRfc3RhdGlvbl9uYW1lICE9ICIiKSAlPiUgDQogIGdyb3VwX2J5KHVzZXJfdHlwZSwgc3RhcnRfc3RhdGlvbl9uYW1lLCByaWRlYWJsZV90eXBlKSAlPiUgDQogIHN1bW1hcmlzZSguZ3JvdXBzPSJkcm9wIixudW1fcmlkZXMgPSBuKCkpICU+JSANCiAgYXJyYW5nZSh1c2VyX3R5cGUsIHN0YXJ0X3N0YXRpb25fbmFtZSkNCg0KIyBUcmFuc2Zvcm0gZGF0YSB0byBhIHdpZGUgZm9ybWF0IHRvIGNyZWF0ZSBzZXBhcmF0ZSBjb2x1bW5zIGZvciBlYWNoIHJpZGVhYmxlIA0KIyBiaWtlIHR5cGUNCnN0YXRpb25fc3VtbWFyeV9waXZvdCA8LSBzdGF0aW9uX3N1bW1hcnkgJT4lDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSByaWRlYWJsZV90eXBlLCB2YWx1ZXNfZnJvbSA9IG51bV9yaWRlcywgdmFsdWVzX2ZpbGwgPSAwKQ0KDQojIENhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHJpZGVzIGFuZCBob3cgbXVjaCBudW1iZXIgb2YgcmlkZXMgd2l0aCBjbGFzc2ljIA0KIyBhbmQgZWxlY3RyaWMgYmlrZSB0eXBlcyBmb3IgZWFjaCBjb21iaW5hdGlvbiBvciB1c2VyX3R5cGUgYW5kIA0KIyBzdGFydF9zdGF0aW9uX25hbWUNCnN0YXRpb25fc3VtbWFyeV9maW5hbCA8LSBzdGF0aW9uX3N1bW1hcnlfcGl2b3QgJT4lDQogIGdyb3VwX2J5KHVzZXJfdHlwZSwgc3RhcnRfc3RhdGlvbl9uYW1lKSAlPiUNCiAgc3VtbWFyaXNlKC5ncm91cHM9ImRyb3AiLG51bV9yaWRlcyA9IHN1bShjbGFzc2ljX2Jpa2UpK3N1bShlbGVjdHJpY19iaWtlKSwNCiAgICAgICAgICAgIG51bV9yaWRlc193X2NsYXNzaWMgPSBzdW0oY2xhc3NpY19iaWtlKSwNCiAgICAgICAgICAgIG51bV9yaWRlc193X2VsZWN0cmljID0gc3VtKGVsZWN0cmljX2Jpa2UpKQ0KDQojIEdldCB0aGUgdG9wIDUgc3RhdGlvbnMgd2l0aCB0aGUgbW9zdCBudW1fcmlkZXMgZm9yIGVhY2ggdXNlcl90eXBlDQp0b3BfNV9zdGF0aW9ucyA8LSBzdGF0aW9uX3N1bW1hcnlfZmluYWwgJT4lDQogIGdyb3VwX2J5KHVzZXJfdHlwZSkgJT4lDQogIHRvcF9uKDUsIG51bV9yaWRlcykgJT4lIA0KICBhcnJhbmdlKHVzZXJfdHlwZSwgZGVzYyhudW1fcmlkZXMpKQ0KDQp0b3BfNV9zdGF0aW9ucw0KYGBgDQoNCjxicj4NCg0KPGI+Q2FzdWFsIFJpZGVycyBUb3AgU3RhcnQgU3RhdGlvbnM6PC9iPg0KDQpgYGB7cn0NCiMjIEJhciBncmFwaCBmb3IgVG9wIDUgc3RhcnQgc3RhdGlvbnMgYnkgbnVtIHJpZGVzIChjYXN1YWwgcmlkZXIpDQp0b3BfNV9zdGF0aW9ucyAlPiUgDQogIGZpbHRlcih1c2VyX3R5cGUgPT0gImNhc3VhbCIpICU+JSANCiAgZ2dwbG90KGFlcyh4PXN0YXJ0X3N0YXRpb25fbmFtZSwgeT1udW1fcmlkZXMsZmlsbD0iQWxsIFN0YXRpb25zIikpICsgDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCkgKw0KICBsYWJzKHRpdGxlID0gIlRvcCA1IFN0YXJ0IFN0YXRpb25zIGJ5IE51bWJlciBvZiBSaWRlcyAoQ2FzdWFsKSIsDQogICAgICAgeCA9ICJTdGFydCBTdGF0aW9uIiwgeSA9ICJUb3RhbCBOdW1iZXIgb2YgUmlkZXMiKSArDQogIGdlb21fdGV4dChhZXMoeD1zdGFydF9zdGF0aW9uX25hbWUseT1udW1fcmlkZXMsbGFiZWw9bnVtX3JpZGVzKSx2anVzdD0xLjUsDQogICAgICAgICAgICBjb2xvcj0id2hpdGUiLCBzaXplPTYpKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSA3KSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSkgKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSAiYnJvd24iKQ0KYGBgDQoNCjxicj4NCg0KPGI+QW5udWFsIE1lbWJlcnMgVG9wIFN0YXJ0IFN0YXRpb25zOjwvYj4NCg0KYGBge3J9DQojIyBCYXIgZ3JhcGggZm9yIFRvcCA1IHN0YXJ0IHN0YXRpb25zIGJ5IG51bSByaWRlcyAobWVtYmVyIHJpZGVyKQ0KdG9wXzVfc3RhdGlvbnMgJT4lIA0KICBmaWx0ZXIodXNlcl90eXBlID09ICJtZW1iZXIiKSAlPiUgDQogIGdncGxvdChhZXMoeD1zdGFydF9zdGF0aW9uX25hbWUsIHk9bnVtX3JpZGVzLGZpbGw9IkFsbCBTdGF0aW9ucyIpKSArIA0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwpICsNCiAgbGFicyh0aXRsZSA9ICJUb3AgNSBTdGFydCBTdGF0aW9ucyBieSBOdW1iZXIgb2YgUmlkZXMgKE1lbWJlcikiLA0KICAgICAgIHggPSAiU3RhcnQgU3RhdGlvbiIsIHkgPSAiVG90YWwgTnVtYmVyIG9mIFJpZGVzIikgKw0KICBnZW9tX3RleHQoYWVzKHg9c3RhcnRfc3RhdGlvbl9uYW1lLHk9bnVtX3JpZGVzLGxhYmVsPW51bV9yaWRlcyksdmp1c3Q9MS41LA0KICAgICAgICAgICAgY29sb3I9IndoaXRlIiwgc2l6ZT02KSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDcpLA0KICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9ICJicm93biIpDQpgYGANCg0KDQo8Yj5BbmFseXNpczo8L2I+DQoNCiogQ2FzdWFsIHJpZGVycyBhbmQgbWVtYmVycyBoYXZlIGRpZmZlcmVudCBwcmVmZXJlbmNlcyBmb3IgdGhlaXIgdG9wIGZpdmUgc3RhcnRpbmcgc3RhdGlvbnMuDQoqIFRoZSBzdGF0aW9uICdTdHJlZXRlciBEciAmIEdyYW5kIEF2ZScgaGFzIGhhZCBhYm91dCAxNSwwMDAgbW9yZSBjYXN1YWwgcmlkZXJzIHN0YXJ0IHRoZXJlIHRoYW4gdGhlIG5leHQgY2xvc2VzdCBzdGFydGluZyBzdGF0aW9uLg0KDQo8YnI+DQoNCiMjIyBTdW1tYXJpemF0aW9uIG9mIEZpbmRpbmdzDQoNCjxiPkJvdGggQ2FzdWFsIGFuZCBNZW1iZXIgUmlkZXJzOjwvYj4NCg0KKiBCb3RoIHVzZSBDeWNsaXN0aWMgYmlrZXMgbW9yZSBpbiB0aGUgd2FybWVyIG1vbnRocyBhbmQgcmFyZWx5IHRha2UgcmlkZXMgaW4gdGhlIHdpbnRlci4gDQoNCg0KPGI+QW5udWFsIE1lbWJlcnNoaXAgUmlkZXJzOjwvYj4NCg0KKiBIYXZlIGEgbG93ZXIgYXZlcmFnZSByaWRlIHRpbWUgYnkgYWJvdXQgOCBtaW51dGVzIDMwIHNlY29uZHMuDQoqIFRha2UgbW9zdCBvZiB0aGVpciByaWRlcyBkdXJpbmcgdGhlIHdvcmsgd2VlayAoTW9uLUZyaSkgYW5kIGRvIG5vdCByaWRlIGFzIG11Y2ggZHVyaW5nIHRoZSB3ZWVrZW5kLg0KKiBTaG93IGxpdHRsZSB0byBubyBwcmVmZXJlbmNlIGluIHRoZSB0eXBlIG9mIGJpa2UgdXNlZCBmb3IgdGhlaXIgcmlkZXMNCiogSGF2ZSBkaWZmZXJlbnQgc3RhcnQgc3RhdGlvbiBwcmVmZXJlbmNlcyB0aGFuIGNhc3VhbCByaWRlcnMgYnV0IGhhdmUgbW9yZSBvZiBhbiBldmVuIGRpc3RyaWJ1dGlvbiBiZXR3ZWVuIHRoZWlyIHRvcCBzdGF0aW9ucy4gDQoNCg0KPGI+Q2FzdWFsIFJpZGVyczo8L2I+DQoNCiogSGF2ZSBhIGhpZ2hlciBhdmVyYWdlIHJpZGUgdGltZS4NCiogVGVuZCB0byB0YWtlIG1vcmUgcmlkZXMgb24gdGhlIHdlZWtlbmQgYXMgb3Bwb3NlZCB0byBkdXJpbmcgdGhlIHdlZWsuDQoqIFNob3cgYSBwcmVmZXJlbmNlIGZvciB1c2luZyBlbGVjdHJpYyBiaWtlcyBtb3JlIHRoYW4gY2xhc3NpYyBiaWtlcy4gDQoqIEhhdmUgZGlmZmVyZW50IHN0YXJ0IHN0YXRpb24gcHJlZmVyZW5jZXMgdGhhbiBtZW1iZXJzLiBUaGUgaGlnaGVzdCBiZWluZyAnU3RyZWV0ZXIgRHIgJiBHcmFuZCBBdmUnIHN0YXRpb24gd2l0aCA0MSwyNjAgcmlkZXMgYW5kIHRoZSBuZXh0IGNsb3Nlc3Qgc3RhdGlvbiBpbiB0ZXJtcyBvZiByaWRlcyBpcyAnRHVTYWJsZSBMYWtlIFNob3JlIERyICYgTW9ucm9lIFN0JyB3aXRoIDI2LDkyMiByaWRlcy4gQWxtb3N0IGEgZGlmZmVyZW5jZSBvZiAxNSwwMDAgcmlkZXMgZHVyaW5nIHRoZSB5ZWFyLiANCg0KPGJyPg0KPGJyPg0KDQoNCiMgJ1NoYXJlIGFuZCBBY3QnIFBoYXNlIG9mIERhdGEgQW5hbHlzaXMgUHJvY2Vzcw0KDQpUaHJvdWdob3V0IHRoaXMgYW5hbHlzaXMsIHdlIGhhdmUgaWRlbnRpZmllZCBrZXkgZGlmZmVyZW5jZXMgaW4gaG93IGNhc3VhbCByaWRlcnMgYW5kIHJpZGVycyB3aXRoIGFubnVhbCBtZW1iZXJzaGlwcyB1c2UgQ3ljbGlzdGljIGJpa2VzLiBXaXRoIHRoZXNlIGluc2lnaHRzLCBoZXJlIGFyZSBteSB0b3AgdGhyZWUgcmVjb21tZW5kYXRpb25zIGZvciB3YXlzIHRoZSBtYXJrZXRpbmcgdGVhbSBjYW4gY2FtcGFpZ24gZm9yIGNvbnZlcnRpbmcgbW9yZSBjYXN1YWwgcmlkZXJzIHRvIG1lbWJlcnM6IA0KDQoxLiA8Yj5FbXBoYXNpemUgV2Vla2RheSBSaWRpbmcgQmVuZWZpdHM6PC9iPiBBbm51YWwgbWVtYmVycyB0ZW5kIHRvIHRha2UgbW9zdCBvZiB0aGVpciByaWRlcyBkdXJpbmcgdGhlIHdvcmsgd2Vlaywgd2hpbGUgY2FzdWFsIHJpZGVycyBwcmVmZXIgd2Vla2VuZHMuIFRvIGVuY291cmFnZSBjYXN1YWwgcmlkZXJzIHRvIGNvbnNpZGVyIG1lbWJlcnNoaXAsIG1hcmtldGluZyBjb3VsZCBwcm9tb3RlIHRoZSBhZHZhbnRhZ2VzIG9mIHdlZWtkYXkgcmlkaW5nLCBzdWNoIGFzIGF2b2lkaW5nIHJ1c2ggaG91ciB0cmFmZmljLCBpbmNvcnBvcmF0aW5nIGV4ZXJjaXNlIG9yIGJlaW5nIG91dHNpZGUgaW50byBkYWlseSByb3V0aW5lcywgYW5kIGNvbnZlbmllbnQgdHJhbnNwb3J0IHRvIHdvcmsgb3IgZXJyYW5kcy4gDQoNCjIuIDxiPkhpZ2hsaWdodCBFbGVjdHJpYyBCaWtlIFByZWZlcmVuY2U6PC9iPiBDYXN1YWwgcmlkZXJzIHNob3cgYSBwcmVmZXJlbmNlIGZvciB1c2luZyBlbGVjdHJpYyBiaWtlcyBtb3JlIHRoYW4gY2xhc3NpYyBiaWtlcywgd2hpY2ggY291bGQgaW5kaWNhdGUgYSBwcmVmZXJlbmNlIGZvciBtb3JlIGNvbWZvcnRhYmxlIGFuZCBsZWlzdXJlbHkgcmlkaW5nIGV4cGVyaWVuY2UuIEN5Y2xpc3RpYydzIG1hcmtldGluZyBjYW1wYWlnbiBjb3VsZCBzcGVjaWZpY2FsbHkgaGlnaGxpZ2h0IHRoZSBiZW5lZml0cyBvZiBlbGVjdHJpYyBiaWtlcyBmb3IgbGVpc3VyZSByaWRpbmcsIHN1Y2ggYXMgZWZmb3J0bGVzcyBuYXZpZ2F0aW9uIHRocm91Z2ggY2l0eSBzdHJlZXRzIHdpdGhvdXQgZXhlcnRpbmcgZXhjZXNzIGVmZm9ydC4gQnkgZW1waGFzaXppbmcgdGhlIHN1aXRhYmlsaXR5IG9mIGVsZWN0cmljIGJpa2VzIGZvciBsZWlzdXJlbHkgcmlkZXMsIEN5Y2xpc3RpYyBjYW4gcG9zaXRpb24gbWVtYmVyc2hpcCBhcyBhIHNvbHV0aW9uIGZvciBjYXN1YWwgcmlkZXJzIGxvb2tpbmcgdG8gZW5qb3kgcmVsYXhlZCBhbmQgY29tZm9ydGFibGUgb3V0aW5ncyBhcm91bmQgdGhlIGNpdHkgcmVndWxhcmx5LiANCg0KMy4gPGI+T2ZmZXIgTWVtYmVyc2hpcCBCZW5lZml0cyB0byBIaWdoLVZvbHVtZSBTdGFydGluZyBTdGF0aW9uczo8L2I+IEdpdmVuIHRoZSBkaWZmZXJlbmNlcyBpbiBzdGFydCBzdGF0aW9uIHByZWZlcmVuY2VzIGJldHdlZW4gY2FzdWFsIGFuZCBtZW1iZXIgcmlkZXJzLCBDeWNsaXN0aWMgY2FuIGNvbnNpZGVyIG9mZmVyaW5nIG1lbWJlcnNoaXAgYmVuZWZpdHMgdG8gYWxpZ24gd2l0aCB0aGUgbmVlZHMgYW5kIHByZWZlcmVuY2VzIG9mIGNhc3VhbCByaWRlcnMuIFRoaXMgY291bGQgaW5jbHVkZSBvZmZlcmluZyBleGNsdXNpdmUgaW5jZW50aXZlcyBvciBkaXNjb3VudHMgZm9yIHJpZGVzIHN0YXJ0aW5nIGZyb20gdGhlIG1vc3QgcG9wdWxhciBzdGF0aW9ucyBmb3IgY2FzdWFsIHJpZGVycy4gVGhpcyBhY2tub3dsZWRnZXMgdGhlIHByZWZlcmVuY2VzIG9mIGNhc3VhbCByaWRlcnMgYW5kIHByb3ZpZGVzIGFkZGl0aW9uYWwgdmFsdWUgdG8gZW5jb3VyYWdlIHRoZW0gdG8gYmVjb21lIG1lbWJlcnMuIEFkZGl0aW9uYWxseSwgdG8gZnVydGhlciB0YWlsb3IgdG8gY2FzdWFsIHJpZGVyIHByZWZlcmVuY2UgYW5kIGFkZCBtb3JlIGluY2VudGl2ZSB0byB0cmFuc2l0aW9uIHRvIG1lbWJlcnNoaXAsIEN5Y2xpc3RpYyBjYW4gb3B0aW1pemUgYmlrZSBhdmFpbGFiaWxpdHksIG1haW50ZW5hbmNlLCBhbmQgaW5mcmFzdHJ1Y3R1cmUgYXQgdGhlc2Ugc3RhdGlvbnMgdG8gZW5oYW5jZSB0aGUgb3ZlcmFsbCByaWRpbmcgZXhwZXJpZW5jZS4gDQoNCg==