1.Introduction

In order to efficiently store data, we often spread related information across multiple tables.

For instance, imagine that we own an e-commerce business and we want to track the products that have been ordered from our website.

We could have one table with all of the following information:

1.order_id

2.customer_id

3.customer_name

4.customer_address

5.customer_phone_number

6.product_id

7.product_description

8.product_price

9.quantity

10.timestamp

However, a lot of this information would be repeated. If the same customer makes multiple orders, that customer’s name, address, and phone number will be reported multiple times. If the same product is ordered by multiple customers, then the product price and description will be repeated. This will make our orders table big and unmanageable.

So instead, we can split our data into three tables:

1.orders would contain the information necessary to describe an order: order_id, customer_id, product_id, quantity, and timestamp

2.products would contain the information to describe each product: product_id, product_description and product_price

3.customers would contain the information for each customer: customer_id, customer_name, customer_address, and customer_phone_number

In this lesson, we will learn the dplyr commands that help us work with data stored in multiple tables.

Instructions

1.In notebook.Rmd, we’ve loaded in three data frames: orders, products, and customers.

Begin by inspecting orders using head(). What columns are in the data frame?

2.Now inspect products using head().

Are there any columns in common with orders?

3.Finally inspect customers using head().

Are there any columns in common with orders or products?

# load packages
library(readr)
library(dplyr)
# load orders data
orders <- read_csv("orders.csv")
customers <- read_csv("customers.csv")
products <- read_csv("products.csv")
# inspect orders, customers and products here:

head(orders)
head(customers)
head(products)

2.Joining

Suppose we have the following three tables that describe our eCommerce business:

1.orders — a table with information on each transaction:

orders

2.customers — a table with customer names and contact information:

customers

3.products — a table with product IDs, descriptions, and prices:

products

If we just look at the orders table, we can’t really tell what’s happened in each order. However, if we refer to the other tables, we can get a more complete picture.

Let’s examine the order with an order_id of 1. It was purchased by Customer 2. To find out the customer’s name, we look at the customers table and look for the item with a customer_id value of 2. We can see that Customer 2’s name is Jane Doe and that she lives at 456 Park Ave.

Doing this kind of matching is called joining two data frames.

Instructions

1.Examine the orders and products tables.

What is the description of the product that was ordered in Order 3?

Give your answer as a string assigned to the variable order_3_description.

# define order_3_description here:
order_3_description <- 'thing-a-ma-jig'

2.Examine the orders and customers tables.

What is the phone_number of the customer in Order 5?

Give your answer as a string assigned to the variable order_5_phone_number.

# define order_5_phone_number here:
    order_5_phone_number <- '112-358-1321'

3.Inner Join I

It is easy to do this kind of matching for one row, but hard to do it for multiple rows.

Luckily, dplyr can efficiently do this for the entire table using the inner_join() method.

The inner_join() method looks for columns that are common between two Preview: Docs Data frames are objects that store data into two dimensions of columns and rows. data frames and then looks for rows where those columns’ values are the same. It then combines the matching rows into a single row in a new table.

We can call the inner_join() method with two data frames like this:

joined_df <- orders %>%
  inner_join(customers)
joined_df

This will match up all of the customer information to the orders that each customer made.

Instructions

1.You are an analyst at Cool T-Shirts Inc. You are going to help them analyze some of their sales data.

There are two data frames defined in the file notebook.Rmd:

(1)sales contains the monthly revenue for Cool T-Shirts Inc. It has two columns: month and revenue.

(2)targets contains the goals for monthly revenue for each month. It has two columns: month and target.

Create a new data frame sales_vs_targets which contains the inner_join() of sales and targets.

# load packages
library(readr)
library(dplyr)
# load sales and targets data
sales <- read_csv("sales.csv")
targets <- read_csv("targets.csv")
# inspect orders, customers and products
sales
targets
# define sales_vs_targets here:
sales_vs_targets <- sales %>%
  inner_join(targets)

sales_vs_targets

2.Cool T-Shirts Inc. wants to know the months when they crushed their targets.

Filter sales_vs_targets to only include the rows where revenue is greater than target. Save these rows to the variable crushing_it.

# define crushing_it here:
crushing_it <- sales_vs_targets %>%
  filter(revenue > target)

crushing_it

4.Inner Join II

In addition to using inner_join() to join two together, we can use the pipe %>% to join multiple data frames together at once. The following command would join orders with customers, and then join the resulting data frame with products:

big_df <- orders %>%
  inner_join(customers) %>%
  inner_join(products)

big_df

Instructions

1.We have some more data from Cool T-Shirts Inc. The number of men’s and women’s t-shirts sold per month is in a file called men_women_sales.csv. Load this data into a data frame called men_women, and inspect it using head().

# load men_women data here:
men_women <- read_csv("men_women_sales.csv")

# inspect men_women here:
head(men_women)

2.Join all three data frames (sales, targets, and men_women) into one big data frame called all_data. View all_data.

# define all_data here:
all_data <- sales %>%
  inner_join(targets) %>%
  inner_join(men_women)

all_data

3.Cool T-Shirts Inc. thinks that they have more revenue in months where they sell more women’s t-shirts.

Filter the rows of all_data to only include rows where:

1.revenue is greater than target

AND

2.women is greater than men

Save your answer to the variable results, and view it.

# define results here:
results <- all_data %>%
  filter(revenue > target) %>%
  filter(women > men)

results

5.Join on Specific Columns I

In the previous example, the inner_join() function “knew” how to combine tables based on the columns that were the same between two tables. For instance, orders and customers both had a column called customer_id. This won’t always be true when we want to perform a join.

Generally, the orders data frame would not have the column order_id and the customers data frame would not have the column customer_id. Instead, they would both have a column id and it would be implied that the id was the order_id for the orders table and the customer_id for the customers table. They would look like this:

knitr::include_graphics("C:/Users/kuoan/Desktop/R Code/Join1.png")

knitr::include_graphics("C:/Users/kuoan/Desktop/R Code/Join2.png")

How would this affect our joins?

Because the id columns would mean something different in each table, our default joins would be wrong.

One way that we could address this problem is to use the dplyr function rename() to rename the columns for our joins. In the example below, we will rename the column id in the customers data frame to customer_id, so that orders and customers now have a common column to join on.

” customers <- customers %>% rename(customer_id = id) inner_join(orders, customers) ”

Instructions

1.The id column of products stores the same information as the product_id column of orders. Rename the id column of products to product_id. Save the updated data frame to products.

# load orders and products data
orders <- read_csv("orders1.csv")
products <- read_csv("products1.csv")
# inspect orders and products
head(orders)
head(products)
# rename the id column of products here:
products <- products %>%
  rename(product_id = id)

products

2.Join orders and products. Save the result to the variable orders_products, and view it.

# define orders_products here:
orders_products <- inner_join(orders, products)

orders_products

6.Join on Specific Columns II

In the previous exercise, we learned how to use rename() to join two data frames whose columns don’t match.

A better option, however, exists. We can add the by argument when calling inner_join() to specify which columns we want to join on. In the example below, the “left” table is the one that comes first (orders), and the “right” table is the one that comes second (customers). This syntax says that we should match the customer_id from orders to the id in customers.

customers <- read_csv("customers1.csv")
orders <- read_csv("orders1.csv")
orders %>% 
  inner_join(customers,
             by = c('customer_id' = 'id'))

If we use this syntax, we’ll end up with two columns called id, one from the first table and one from the second. R won’t let you have two columns with the same name, so it will change them to id_x and id_y.

customers <- read_csv("customers1.csv")
orders <- read_csv("orders1.csv")
customers
orders
join <- orders %>% 
  inner_join(customers,
             by = c('customer_id' = 'id'))

join

The new column names id_x and id_y aren’t very helpful for us when we read the table. We can help make them more useful by using the keyword suffix. We can provide a vector of suffixes to use instead of “_x” and “_y”.

For example, we could use the following code to make the suffixes reflect the table names:

join <- orders %>% 
  inner_join(customers,
             by = c('customer_id' = 'id'),
             suffix = c('_order','_customer'))

join

Instructions

1.Join the orders and products data frames using an inner_join(), with orders as the first argument and products as the second argument. Also include the by argument to indicate which columns to join on. Save your results to the variable orders_products, and view it.

# load orders and products data
orders <- read_csv("orders1.csv")
products <- read_csv("products1.csv")
# inspect orders and products
head(orders)
head(products)
# define orders_products here:
orders_products <- inner_join(orders, products, by = c('product_id' = 'id'))

orders_products
NA

2.Now join the products and orders data frames using an inner_join(), with products as the first argument and orders as the second argument. Also include the by argument to indicate which columns to join on, as well as a suffix argument c(’_product’,’_order’). Save your results to the variable products_orders, and view it.

# define products_orders here:
products_orders <- inner_join(products, orders, by = c('id' = 'product_id'), suffix = c('_product','_order'))

products_orders

7.Mismatched Joins

In our previous examples, there were always matching values when we were performing our joins. What happens when that isn’t true?

Let’s imagine that our products table is out of date and is missing the newest product: Product 5. What happens when someone orders it?

Instructions

1.We’ve just released a new product with product_id equal to 5. People are ordering this product, but we haven’t updated the products table.

In notebook.Rmd, you’ll find two data frames: orders and products. Inspect these data frames using head().

Notice that the third order in orders is for the mysterious new product, but that there is no product_id 5 in products.

# load orders and products data
orders <- read_csv("orders2.csv")
products <- read_csv("products2.csv")
# inspect orders and products here:
head(orders)
head(products)

2.Join the orders and products data frames with inner_join() and save the resulting data frame to the variable orders_products. View orders_products.

What happened to order_id 3?

# define orders_products here:
orders_products <- inner_join(orders, products)
orders_products

8.Full Join

In the previous exercise, we saw that when we join two data frames whose rows don’t match perfectly, we lose the unmatched rows.

This type of join (where we only include matching rows) is called an inner join. There are other types of joins that we can use when we want to keep information from the unmatched rows.

Suppose that two companies, Company A and Company B have just merged. They each have a list of customers, but they keep slightly different data. Company A has each customer’s name and email. Company B has each customer’s name and phone number. They have some customers in common, but some are different.

company_a <- read_csv("company_a.csv")
company_b <- read_csv("company_b.csv")
company_a
company_b

If we wanted to combine the data from both companies without losing the customers who are missing from one of the tables, we could use a Full Join. A Full Join would include all rows from both tables, even if they don’t match. Any missing values are filled in with NA.

full_joined_dfs <- company_a %>%
  full_join(company_b)

full_joined_dfs

Instructions

1.There are two hardware stores in town: Store A and Store B. Store A’s inventory is in data frame store_a and Store B’s inventory is in data frame store_b. They have decided to merge into one big Super Store!

Combine the inventories of Store A and Store B using a full join. Save the results to the variable store_a_b_full.

# load store_a and store_b data
store_a <- read_csv("store_a.csv")
store_b <- read_csv("store_b.csv")
# inspect store_a and store_b
store_a
store_b
# define store_a_b_full here:
store_a_b_full <- full_join(store_a, store_b)

store_a_b_full

9.Left and Right Joins

Let’s return to the join of Company A and Company B.

Left Join

Suppose we want to identify which customers are missing phone information. We would want a list of all customers who have email, but don’t have phone.

We could get this by performing a Left Join. A Left Join includes all rows from the first (left) table, but only rows from the second (right) table that match the first table.

For this command, the order of the arguments matters. If the first data frame is company_a and we do a left join, we’ll only end up with rows that appear in company_a.

By listing company_a first, we get all customers from Company A, and only customers from Company B who are also customers of Company A.

left_joined_df <- company_a %>%
  left_join(company_b)

The result would look like this:

left_joined_df

Now let’s say we want a list of all customers who have phone but no email. We can do this by performing a Right Join.

Right Join

A Right Join is the exact opposite of left join. Here, the joined table will include all rows from the second (right) table, but only rows from the first (left) table that match the second table.

By listing company_a first and company_b second, we get all customers from Company B, and only customers from Company A who are also customers of Company B.

right_joined_df <- company_a %>%
  right_join(company_b)

The result would look like this:

right_joined_df

Instrctions

1.Let’s return to the two hardware stores, Store A and Store B. They’re not quite sure if they want to merge into a big Super Store just yet.

Store A wants to find out what products they carry that Store B does not carry. Using a left join, combine store_a to store_b and save the results to left_a_b.

The items with NA in left_a_b are carried by Store A, but not by Store B.

# define left_a_b here:
left_a_b <- store_a %>%
  left_join(store_b)

left_a_b

2.Now, Store B wants to find out what products they carry that Store A does not carry. Use a left join, to combine the two data frames but in the reverse order (i.e., store_b followed by store_a) and save the results to the variable left_b_a.

Which items are not carried by Store A, but are carried by Store B?

What do you notice about these two data frames?

How are they different?

How are they the same?

# define left_b_a here:
left_b_a <- store_b %>%
  left_join(store_a)

left_b_a

10.Concatenate Data Frames

Sometimes, a dataset is broken into multiple tables. For instance, data is often split into multiple CSV files so that each download is smaller.

When we need to reconstruct a single data frame from multiple smaller data frames, we can use the dplyr bind_rows() method. This method only works if all of the columns are the same in all of the data frames.

For instance, suppose that we have two data frames:

df1 <- read_csv("df1.csv")
df2 <- read_csv("df2.csv")
df1
df2

If we want to combine these two data frames, we can use the following command:

concatenated_dfs <- df1 %>%
  bind_rows(df2)

That would result in the following data frame:

concatenated_dfs

Instructions

1.An ice cream parlor and a bakery have decided to merge.

The bakery’s menu is stored in the data frame bakery, and the ice cream parlor’s menu is stored in the data frame ice_cream.

Create their new menu by concatenating the two data frames into a data frame called menu.

# load bakery and ice_cream data
bakery <- read_csv('bakery.csv')
ice_cream <- read_csv('ice_cream.csv')
# inspect bakery and ice_cream
head(bakery)
head(ice_cream)
# define menu here:
menu <- bind_rows(bakery, ice_cream)

menu

11.Review

This lesson introduced some methods for combining multiple data frames:

1.Creating a data frame made by matching the common columns of two is called a join

2.We can specify which columns should be matched by using the by argument

3.We can combine data frames whose rows don’t all match using left, right, and full joins

4.We can stack or concatenate data frames with the same columns using bind_rows()

Instructions

1.Cool T-Shirts Inc. just created a website for ordering their products. They want you to analyze two datasets for them:

(1)visits contains information on all visits to their landing page

(2)checkouts contains all users who began to checkout on their website

Use head() to inspect each data frame.

# load visits and checkouts data
visits <- read_csv('visits.csv')
checkouts <- read_csv('checkouts.csv')

2.We want to know the amount of time from a user’s initial visit to the website to when they start to check out.

Use inner_join to combine visits and checkouts and save it to the variable v_to_c. View v_to_c.

# inspect visits and checkouts here:
head(visits)
head(checkouts)
# define v_to_c here:

v_to_c <- inner_join(visits, checkouts)

v_to_c

3.In order to calculate the time between visiting and checking out, define a column of v_to_c called time by pasting the following code into notebook.Rmd:

# define avg_time_to_check here:
v_to_c <- v_to_c %>% 
  mutate(time = checkout_time - visit_time)
v_to_c

4.To get the average time to checkout, paste the following code into notebook.Rmd:

avg_time_to_check <- v_to_c %>% 
  summarize(mean_time = mean(time))

avg_time_to_check
LS0tDQp0aXRsZTogIkpvaW5pbmcgVGFibGVzIGluIFIiDQphdXRob3I6ICJBbm5hYmVsIEt1byINCmRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVZLSVtLSVkICVIOiVNJylgIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyAxLkludHJvZHVjdGlvbg0KDQpJbiBvcmRlciB0byBlZmZpY2llbnRseSBzdG9yZSBkYXRhLCB3ZSBvZnRlbiBzcHJlYWQgcmVsYXRlZCBpbmZvcm1hdGlvbiBhY3Jvc3MgbXVsdGlwbGUgdGFibGVzLg0KDQpGb3IgaW5zdGFuY2UsIGltYWdpbmUgdGhhdCB3ZSBvd24gYW4gZS1jb21tZXJjZSBidXNpbmVzcyBhbmQgd2Ugd2FudCB0byB0cmFjayB0aGUgcHJvZHVjdHMgdGhhdCBoYXZlIGJlZW4gb3JkZXJlZCBmcm9tIG91ciB3ZWJzaXRlLg0KDQpXZSBjb3VsZCBoYXZlIG9uZSB0YWJsZSB3aXRoIGFsbCBvZiB0aGUgZm9sbG93aW5nIGluZm9ybWF0aW9uOg0KDQoxLm9yZGVyX2lkDQoNCjIuY3VzdG9tZXJfaWQNCg0KMy5jdXN0b21lcl9uYW1lDQoNCjQuY3VzdG9tZXJfYWRkcmVzcw0KDQo1LmN1c3RvbWVyX3Bob25lX251bWJlcg0KDQo2LnByb2R1Y3RfaWQNCg0KNy5wcm9kdWN0X2Rlc2NyaXB0aW9uDQoNCjgucHJvZHVjdF9wcmljZQ0KDQo5LnF1YW50aXR5DQoNCjEwLnRpbWVzdGFtcA0KDQpIb3dldmVyLCBhIGxvdCBvZiB0aGlzIGluZm9ybWF0aW9uIHdvdWxkIGJlIHJlcGVhdGVkLiBJZiB0aGUgc2FtZSBjdXN0b21lciBtYWtlcyBtdWx0aXBsZSBvcmRlcnMsIHRoYXQgY3VzdG9tZXLigJlzIG5hbWUsIGFkZHJlc3MsIGFuZCBwaG9uZSBudW1iZXIgd2lsbCBiZSByZXBvcnRlZCBtdWx0aXBsZSB0aW1lcy4gSWYgdGhlIHNhbWUgcHJvZHVjdCBpcyBvcmRlcmVkIGJ5IG11bHRpcGxlIGN1c3RvbWVycywgdGhlbiB0aGUgcHJvZHVjdCBwcmljZSBhbmQgZGVzY3JpcHRpb24gd2lsbCBiZSByZXBlYXRlZC4gVGhpcyB3aWxsIG1ha2Ugb3VyIG9yZGVycyB0YWJsZSBiaWcgYW5kIHVubWFuYWdlYWJsZS4NCg0KU28gaW5zdGVhZCwgd2UgY2FuIHNwbGl0IG91ciBkYXRhIGludG8gdGhyZWUgdGFibGVzOg0KDQoxLm9yZGVycyB3b3VsZCBjb250YWluIHRoZSBpbmZvcm1hdGlvbiBuZWNlc3NhcnkgdG8gZGVzY3JpYmUgYW4gb3JkZXI6IG9yZGVyX2lkLCBjdXN0b21lcl9pZCwgcHJvZHVjdF9pZCwgcXVhbnRpdHksIGFuZCB0aW1lc3RhbXANCg0KMi5wcm9kdWN0cyB3b3VsZCBjb250YWluIHRoZSBpbmZvcm1hdGlvbiB0byBkZXNjcmliZSBlYWNoIHByb2R1Y3Q6IHByb2R1Y3RfaWQsIHByb2R1Y3RfZGVzY3JpcHRpb24gYW5kIHByb2R1Y3RfcHJpY2UNCg0KMy5jdXN0b21lcnMgd291bGQgY29udGFpbiB0aGUgaW5mb3JtYXRpb24gZm9yIGVhY2ggY3VzdG9tZXI6IGN1c3RvbWVyX2lkLCBjdXN0b21lcl9uYW1lLCBjdXN0b21lcl9hZGRyZXNzLCBhbmQgY3VzdG9tZXJfcGhvbmVfbnVtYmVyDQoNCkluIHRoaXMgbGVzc29uLCB3ZSB3aWxsIGxlYXJuIHRoZSBkcGx5ciBjb21tYW5kcyB0aGF0IGhlbHAgdXMgd29yayB3aXRoIGRhdGEgc3RvcmVkIGluIG11bHRpcGxlIHRhYmxlcy4NCg0KIyMgSW5zdHJ1Y3Rpb25zDQoNCjEuSW4gbm90ZWJvb2suUm1kLCB3ZeKAmXZlIGxvYWRlZCBpbiB0aHJlZSBkYXRhIGZyYW1lczogb3JkZXJzLCBwcm9kdWN0cywgYW5kIGN1c3RvbWVycy4NCg0KQmVnaW4gYnkgaW5zcGVjdGluZyBvcmRlcnMgdXNpbmcgaGVhZCgpLiBXaGF0IGNvbHVtbnMgYXJlIGluIHRoZSBkYXRhIGZyYW1lPw0KDQoyLk5vdyBpbnNwZWN0IHByb2R1Y3RzIHVzaW5nIGhlYWQoKS4NCg0KQXJlIHRoZXJlIGFueSBjb2x1bW5zIGluIGNvbW1vbiB3aXRoIG9yZGVycz8NCg0KMy5GaW5hbGx5IGluc3BlY3QgY3VzdG9tZXJzIHVzaW5nIGhlYWQoKS4NCg0KQXJlIHRoZXJlIGFueSBjb2x1bW5zIGluIGNvbW1vbiB3aXRoIG9yZGVycyBvciBwcm9kdWN0cz8NCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFfQ0KIyBsb2FkIHBhY2thZ2VzDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQpgYGB7ciBtZXNzYWdlID0gRkFMU0V9DQojIGxvYWQgb3JkZXJzIGRhdGENCm9yZGVycyA8LSByZWFkX2Nzdigib3JkZXJzLmNzdiIpDQpjdXN0b21lcnMgPC0gcmVhZF9jc3YoImN1c3RvbWVycy5jc3YiKQ0KcHJvZHVjdHMgPC0gcmVhZF9jc3YoInByb2R1Y3RzLmNzdiIpDQpgYGANCg0KYGBge3J9DQojIGluc3BlY3Qgb3JkZXJzLCBjdXN0b21lcnMgYW5kIHByb2R1Y3RzIGhlcmU6DQoNCmhlYWQob3JkZXJzKQ0KaGVhZChjdXN0b21lcnMpDQpoZWFkKHByb2R1Y3RzKQ0KYGBgDQoNCiMgMi5Kb2luaW5nDQoNClN1cHBvc2Ugd2UgaGF2ZSB0aGUgZm9sbG93aW5nIHRocmVlIHRhYmxlcyB0aGF0IGRlc2NyaWJlIG91ciBlQ29tbWVyY2UgYnVzaW5lc3M6DQoNCjEub3JkZXJzIOKAlCBhIHRhYmxlIHdpdGggaW5mb3JtYXRpb24gb24gZWFjaCB0cmFuc2FjdGlvbjoNCg0KYGBge3J9DQpvcmRlcnMNCmBgYA0KDQoyLmN1c3RvbWVycyDigJQgYSB0YWJsZSB3aXRoIGN1c3RvbWVyIG5hbWVzIGFuZCBjb250YWN0IGluZm9ybWF0aW9uOg0KDQpgYGB7cn0NCmN1c3RvbWVycw0KYGBgDQoNCjMucHJvZHVjdHMg4oCUIGEgdGFibGUgd2l0aCBwcm9kdWN0IElEcywgZGVzY3JpcHRpb25zLCBhbmQgcHJpY2VzOg0KDQpgYGB7cn0NCnByb2R1Y3RzDQpgYGANCg0KSWYgd2UganVzdCBsb29rIGF0IHRoZSBvcmRlcnMgdGFibGUsIHdlIGNhbuKAmXQgcmVhbGx5IHRlbGwgd2hhdOKAmXMgaGFwcGVuZWQgaW4gZWFjaCBvcmRlci4gSG93ZXZlciwgaWYgd2UgcmVmZXIgdG8gdGhlIG90aGVyIHRhYmxlcywgd2UgY2FuIGdldCBhIG1vcmUgY29tcGxldGUgcGljdHVyZS4NCg0KTGV04oCZcyBleGFtaW5lIHRoZSBvcmRlciB3aXRoIGFuIG9yZGVyX2lkIG9mIDEuIEl0IHdhcyBwdXJjaGFzZWQgYnkgQ3VzdG9tZXIgMi4gVG8gZmluZCBvdXQgdGhlIGN1c3RvbWVy4oCZcyBuYW1lLCB3ZSBsb29rIGF0IHRoZSBjdXN0b21lcnMgdGFibGUgYW5kIGxvb2sgZm9yIHRoZSBpdGVtIHdpdGggYSBjdXN0b21lcl9pZCB2YWx1ZSBvZiAyLiBXZSBjYW4gc2VlIHRoYXQgQ3VzdG9tZXIgMuKAmXMgbmFtZSBpcyBKYW5lIERvZSBhbmQgdGhhdCBzaGUgbGl2ZXMgYXQgNDU2IFBhcmsgQXZlLg0KDQpEb2luZyB0aGlzIGtpbmQgb2YgbWF0Y2hpbmcgaXMgY2FsbGVkIGpvaW5pbmcgdHdvIGRhdGEgZnJhbWVzLg0KDQojIyBJbnN0cnVjdGlvbnMNCg0KMS5FeGFtaW5lIHRoZSBvcmRlcnMgYW5kIHByb2R1Y3RzIHRhYmxlcy4NCg0KV2hhdCBpcyB0aGUgZGVzY3JpcHRpb24gb2YgdGhlIHByb2R1Y3QgdGhhdCB3YXMgb3JkZXJlZCBpbiBPcmRlciAzPw0KDQpHaXZlIHlvdXIgYW5zd2VyIGFzIGEgc3RyaW5nIGFzc2lnbmVkIHRvIHRoZSB2YXJpYWJsZSBvcmRlcl8zX2Rlc2NyaXB0aW9uLg0KDQpgYGB7cn0NCiMgZGVmaW5lIG9yZGVyXzNfZGVzY3JpcHRpb24gaGVyZToNCm9yZGVyXzNfZGVzY3JpcHRpb24gPC0gJ3RoaW5nLWEtbWEtamlnJw0KYGBgDQoNCjIuRXhhbWluZSB0aGUgb3JkZXJzIGFuZCBjdXN0b21lcnMgdGFibGVzLg0KDQpXaGF0IGlzIHRoZSBwaG9uZV9udW1iZXIgb2YgdGhlIGN1c3RvbWVyIGluIE9yZGVyIDU/DQoNCkdpdmUgeW91ciBhbnN3ZXIgYXMgYSBzdHJpbmcgYXNzaWduZWQgdG8gdGhlIHZhcmlhYmxlIG9yZGVyXzVfcGhvbmVfbnVtYmVyLg0KDQpgYGB7cn0NCiMgZGVmaW5lIG9yZGVyXzVfcGhvbmVfbnVtYmVyIGhlcmU6DQoJb3JkZXJfNV9waG9uZV9udW1iZXIgPC0gJzExMi0zNTgtMTMyMScNCmBgYA0KDQojIDMuSW5uZXIgSm9pbiBJDQoNCkl0IGlzIGVhc3kgdG8gZG8gdGhpcyBraW5kIG9mIG1hdGNoaW5nIGZvciBvbmUgcm93LCBidXQgaGFyZCB0byBkbyBpdCBmb3IgbXVsdGlwbGUgcm93cy4NCg0KTHVja2lseSwgZHBseXIgY2FuIGVmZmljaWVudGx5IGRvIHRoaXMgZm9yIHRoZSBlbnRpcmUgdGFibGUgdXNpbmcgdGhlIGlubmVyX2pvaW4oKSBtZXRob2QuDQoNClRoZSBpbm5lcl9qb2luKCkgbWV0aG9kIGxvb2tzIGZvciBjb2x1bW5zIHRoYXQgYXJlIGNvbW1vbiBiZXR3ZWVuIHR3byANClByZXZpZXc6IERvY3MgRGF0YSBmcmFtZXMgYXJlIG9iamVjdHMgdGhhdCBzdG9yZSBkYXRhIGludG8gdHdvIGRpbWVuc2lvbnMgb2YgY29sdW1ucyBhbmQgcm93cy4NCmRhdGEgZnJhbWVzDQogYW5kIHRoZW4gbG9va3MgZm9yIHJvd3Mgd2hlcmUgdGhvc2UgY29sdW1uc+KAmSB2YWx1ZXMgYXJlIHRoZSBzYW1lLiBJdCB0aGVuIGNvbWJpbmVzIHRoZSBtYXRjaGluZyByb3dzIGludG8gYSBzaW5nbGUgcm93IGluIGEgbmV3IHRhYmxlLg0KDQpXZSBjYW4gY2FsbCB0aGUgaW5uZXJfam9pbigpIG1ldGhvZCB3aXRoIHR3byBkYXRhIGZyYW1lcyBsaWtlIHRoaXM6DQpgYGB7cn0NCmpvaW5lZF9kZiA8LSBvcmRlcnMgJT4lDQogIGlubmVyX2pvaW4oY3VzdG9tZXJzKQ0Kam9pbmVkX2RmDQpgYGANCg0KVGhpcyB3aWxsIG1hdGNoIHVwIGFsbCBvZiB0aGUgY3VzdG9tZXIgaW5mb3JtYXRpb24gdG8gdGhlIG9yZGVycyB0aGF0IGVhY2ggY3VzdG9tZXIgbWFkZS4NCg0KIyMgSW5zdHJ1Y3Rpb25zDQoNCjEuWW91IGFyZSBhbiBhbmFseXN0IGF0IENvb2wgVC1TaGlydHMgSW5jLiBZb3UgYXJlIGdvaW5nIHRvIGhlbHAgdGhlbSBhbmFseXplIHNvbWUgb2YgdGhlaXIgc2FsZXMgZGF0YS4NCg0KVGhlcmUgYXJlIHR3byBkYXRhIGZyYW1lcyBkZWZpbmVkIGluIHRoZSBmaWxlIG5vdGVib29rLlJtZDoNCg0KKDEpc2FsZXMgY29udGFpbnMgdGhlIG1vbnRobHkgcmV2ZW51ZSBmb3IgQ29vbCBULVNoaXJ0cyBJbmMuIEl0IGhhcyB0d28gY29sdW1uczogbW9udGggYW5kIHJldmVudWUuDQoNCigyKXRhcmdldHMgY29udGFpbnMgdGhlIGdvYWxzIGZvciBtb250aGx5IHJldmVudWUgZm9yIGVhY2ggbW9udGguIEl0IGhhcyB0d28gY29sdW1uczogbW9udGggYW5kIHRhcmdldC4NCg0KQ3JlYXRlIGEgbmV3IGRhdGEgZnJhbWUgc2FsZXNfdnNfdGFyZ2V0cyB3aGljaCBjb250YWlucyB0aGUgaW5uZXJfam9pbigpIG9mIHNhbGVzIGFuZCB0YXJnZXRzLg0KDQpgYGB7ciBtZXNzYWdlID0gRkFMU0V9DQojIGxvYWQgcGFja2FnZXMNCmxpYnJhcnkocmVhZHIpDQpsaWJyYXJ5KGRwbHlyKQ0KYGBgDQoNCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRX0NCiMgbG9hZCBzYWxlcyBhbmQgdGFyZ2V0cyBkYXRhDQpzYWxlcyA8LSByZWFkX2Nzdigic2FsZXMuY3N2IikNCnRhcmdldHMgPC0gcmVhZF9jc3YoInRhcmdldHMuY3N2IikNCmBgYA0KDQpgYGB7cn0NCiMgaW5zcGVjdCBvcmRlcnMsIGN1c3RvbWVycyBhbmQgcHJvZHVjdHMNCnNhbGVzDQp0YXJnZXRzDQpgYGANCg0KYGBge3J9DQojIGRlZmluZSBzYWxlc192c190YXJnZXRzIGhlcmU6DQpzYWxlc192c190YXJnZXRzIDwtIHNhbGVzICU+JQ0KICBpbm5lcl9qb2luKHRhcmdldHMpDQoNCnNhbGVzX3ZzX3RhcmdldHMNCmBgYA0KDQoyLkNvb2wgVC1TaGlydHMgSW5jLiB3YW50cyB0byBrbm93IHRoZSBtb250aHMgd2hlbiB0aGV5IGNydXNoZWQgdGhlaXIgdGFyZ2V0cy4NCg0KRmlsdGVyIHNhbGVzX3ZzX3RhcmdldHMgdG8gb25seSBpbmNsdWRlIHRoZSByb3dzIHdoZXJlIHJldmVudWUgaXMgZ3JlYXRlciB0aGFuIHRhcmdldC4gU2F2ZSB0aGVzZSByb3dzIHRvIHRoZSB2YXJpYWJsZSBjcnVzaGluZ19pdC4NCg0KYGBge3J9DQojIGRlZmluZSBjcnVzaGluZ19pdCBoZXJlOg0KY3J1c2hpbmdfaXQgPC0gc2FsZXNfdnNfdGFyZ2V0cyAlPiUNCiAgZmlsdGVyKHJldmVudWUgPiB0YXJnZXQpDQoNCmNydXNoaW5nX2l0DQpgYGANCg0KIyA0LklubmVyIEpvaW4gSUkNCg0KSW4gYWRkaXRpb24gdG8gdXNpbmcgaW5uZXJfam9pbigpIHRvIGpvaW4gdHdvIHRvZ2V0aGVyLCB3ZSBjYW4gdXNlIHRoZSBwaXBlICU+JSB0byBqb2luIG11bHRpcGxlIGRhdGEgZnJhbWVzIHRvZ2V0aGVyIGF0IG9uY2UuIFRoZSBmb2xsb3dpbmcgY29tbWFuZCB3b3VsZCBqb2luIG9yZGVycyB3aXRoIGN1c3RvbWVycywgYW5kIHRoZW4gam9pbiB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2l0aCBwcm9kdWN0czoNCiANCmBgYHtyfQ0KYmlnX2RmIDwtIG9yZGVycyAlPiUNCiAgaW5uZXJfam9pbihjdXN0b21lcnMpICU+JQ0KICBpbm5lcl9qb2luKHByb2R1Y3RzKQ0KDQpiaWdfZGYNCmBgYA0KDQojIyBJbnN0cnVjdGlvbnMNCg0KMS5XZSBoYXZlIHNvbWUgbW9yZSBkYXRhIGZyb20gQ29vbCBULVNoaXJ0cyBJbmMuIFRoZSBudW1iZXIgb2YgbWVu4oCZcyBhbmQgd29tZW7igJlzIHQtc2hpcnRzIHNvbGQgcGVyIG1vbnRoIGlzIGluIGEgZmlsZSBjYWxsZWQgbWVuX3dvbWVuX3NhbGVzLmNzdi4gTG9hZCB0aGlzIGRhdGEgaW50byBhIGRhdGEgZnJhbWUgY2FsbGVkIG1lbl93b21lbiwgYW5kIGluc3BlY3QgaXQgdXNpbmcgaGVhZCgpLg0KDQpgYGB7cn0NCiMgbG9hZCBtZW5fd29tZW4gZGF0YSBoZXJlOg0KbWVuX3dvbWVuIDwtIHJlYWRfY3N2KCJtZW5fd29tZW5fc2FsZXMuY3N2IikNCg0KIyBpbnNwZWN0IG1lbl93b21lbiBoZXJlOg0KaGVhZChtZW5fd29tZW4pDQpgYGANCg0KMi5Kb2luIGFsbCB0aHJlZSBkYXRhIGZyYW1lcyAoc2FsZXMsIHRhcmdldHMsIGFuZCBtZW5fd29tZW4pIGludG8gb25lIGJpZyBkYXRhIGZyYW1lIGNhbGxlZCBhbGxfZGF0YS4gVmlldyBhbGxfZGF0YS4NCg0KDQpgYGB7cn0NCiMgZGVmaW5lIGFsbF9kYXRhIGhlcmU6DQphbGxfZGF0YSA8LSBzYWxlcyAlPiUNCiAgaW5uZXJfam9pbih0YXJnZXRzKSAlPiUNCiAgaW5uZXJfam9pbihtZW5fd29tZW4pDQoNCmFsbF9kYXRhDQpgYGANCg0KMy5Db29sIFQtU2hpcnRzIEluYy4gdGhpbmtzIHRoYXQgdGhleSBoYXZlIG1vcmUgcmV2ZW51ZSBpbiBtb250aHMgd2hlcmUgdGhleSBzZWxsIG1vcmUgd29tZW7igJlzIHQtc2hpcnRzLg0KDQpGaWx0ZXIgdGhlIHJvd3Mgb2YgYWxsX2RhdGEgdG8gb25seSBpbmNsdWRlIHJvd3Mgd2hlcmU6DQoNCjEucmV2ZW51ZSBpcyBncmVhdGVyIHRoYW4gdGFyZ2V0DQoNCkFORA0KDQoyLndvbWVuIGlzIGdyZWF0ZXIgdGhhbiBtZW4NCg0KU2F2ZSB5b3VyIGFuc3dlciB0byB0aGUgdmFyaWFibGUgcmVzdWx0cywgYW5kIHZpZXcgaXQuDQoNCmBgYHtyfQ0KIyBkZWZpbmUgcmVzdWx0cyBoZXJlOg0KcmVzdWx0cyA8LSBhbGxfZGF0YSAlPiUNCiAgZmlsdGVyKHJldmVudWUgPiB0YXJnZXQpICU+JQ0KICBmaWx0ZXIod29tZW4gPiBtZW4pDQoNCnJlc3VsdHMNCmBgYA0KDQojIDUuSm9pbiBvbiBTcGVjaWZpYyBDb2x1bW5zIEkNCg0KSW4gdGhlIHByZXZpb3VzIGV4YW1wbGUsIHRoZSBpbm5lcl9qb2luKCkgZnVuY3Rpb24g4oCca25ld+KAnSBob3cgdG8gY29tYmluZSB0YWJsZXMgYmFzZWQgb24gdGhlIGNvbHVtbnMgdGhhdCB3ZXJlIHRoZSBzYW1lIGJldHdlZW4gdHdvIHRhYmxlcy4gRm9yIGluc3RhbmNlLCBvcmRlcnMgYW5kIGN1c3RvbWVycyBib3RoIGhhZCBhIGNvbHVtbiBjYWxsZWQgY3VzdG9tZXJfaWQuIFRoaXMgd29u4oCZdCBhbHdheXMgYmUgdHJ1ZSB3aGVuIHdlIHdhbnQgdG8gcGVyZm9ybSBhIGpvaW4uDQoNCkdlbmVyYWxseSwgdGhlIG9yZGVycyBkYXRhIGZyYW1lIHdvdWxkIG5vdCBoYXZlIHRoZSBjb2x1bW4gb3JkZXJfaWQgYW5kIHRoZSBjdXN0b21lcnMgZGF0YSBmcmFtZSB3b3VsZCBub3QgaGF2ZSB0aGUgY29sdW1uIGN1c3RvbWVyX2lkLiBJbnN0ZWFkLCB0aGV5IHdvdWxkIGJvdGggaGF2ZSBhIGNvbHVtbiBpZCBhbmQgaXQgd291bGQgYmUgaW1wbGllZCB0aGF0IHRoZSBpZCB3YXMgdGhlIG9yZGVyX2lkIGZvciB0aGUgb3JkZXJzIHRhYmxlIGFuZCB0aGUgY3VzdG9tZXJfaWQgZm9yIHRoZSBjdXN0b21lcnMgdGFibGUuIFRoZXkgd291bGQgbG9vayBsaWtlIHRoaXM6DQpgYGB7ciBKb2luMSwgb3V0LndpZHRoPSI1MCUifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIkM6L1VzZXJzL2t1b2FuL0Rlc2t0b3AvUiBDb2RlL0pvaW4xLnBuZyIpDQpgYGANCg0KYGBge3IgSm9pbjIsIG91dC53aWR0aD0iNTAlIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJDOi9Vc2Vycy9rdW9hbi9EZXNrdG9wL1IgQ29kZS9Kb2luMi5wbmciKQ0KYGBgDQoNCkhvdyB3b3VsZCB0aGlzIGFmZmVjdCBvdXIgam9pbnM/DQoNCkJlY2F1c2UgdGhlIGlkIGNvbHVtbnMgd291bGQgbWVhbiBzb21ldGhpbmcgZGlmZmVyZW50IGluIGVhY2ggdGFibGUsIG91ciBkZWZhdWx0IGpvaW5zIHdvdWxkIGJlIHdyb25nLg0KDQpPbmUgd2F5IHRoYXQgd2UgY291bGQgYWRkcmVzcyB0aGlzIHByb2JsZW0gaXMgdG8gdXNlIHRoZSBkcGx5ciBmdW5jdGlvbiByZW5hbWUoKSB0byByZW5hbWUgdGhlIGNvbHVtbnMgZm9yIG91ciBqb2lucy4gSW4gdGhlIGV4YW1wbGUgYmVsb3csIHdlIHdpbGwgcmVuYW1lIHRoZSBjb2x1bW4gaWQgaW4gdGhlIGN1c3RvbWVycyBkYXRhIGZyYW1lIHRvIGN1c3RvbWVyX2lkLCBzbyB0aGF0IG9yZGVycyBhbmQgY3VzdG9tZXJzIG5vdyBoYXZlIGEgY29tbW9uIGNvbHVtbiB0byBqb2luIG9uLg0KDQoiDQpjdXN0b21lcnMgPC0gY3VzdG9tZXJzICU+JQ0KICByZW5hbWUoY3VzdG9tZXJfaWQgPSBpZCkNCmlubmVyX2pvaW4ob3JkZXJzLCBjdXN0b21lcnMpDQoiDQoNCiMjIEluc3RydWN0aW9ucw0KDQoxLlRoZSBpZCBjb2x1bW4gb2YgcHJvZHVjdHMgc3RvcmVzIHRoZSBzYW1lIGluZm9ybWF0aW9uIGFzIHRoZSBwcm9kdWN0X2lkIGNvbHVtbiBvZiBvcmRlcnMuIFJlbmFtZSB0aGUgaWQgY29sdW1uIG9mIHByb2R1Y3RzIHRvIHByb2R1Y3RfaWQuIFNhdmUgdGhlIHVwZGF0ZWQgZGF0YSBmcmFtZSB0byBwcm9kdWN0cy4NCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFfQ0KIyBsb2FkIG9yZGVycyBhbmQgcHJvZHVjdHMgZGF0YQ0Kb3JkZXJzIDwtIHJlYWRfY3N2KCJvcmRlcnMxLmNzdiIpDQpwcm9kdWN0cyA8LSByZWFkX2NzdigicHJvZHVjdHMxLmNzdiIpDQpgYGANCg0KYGBge3J9DQojIGluc3BlY3Qgb3JkZXJzIGFuZCBwcm9kdWN0cw0KaGVhZChvcmRlcnMpDQpoZWFkKHByb2R1Y3RzKQ0KYGBgDQoNCmBgYHtyfQ0KIyByZW5hbWUgdGhlIGlkIGNvbHVtbiBvZiBwcm9kdWN0cyBoZXJlOg0KcHJvZHVjdHMgPC0gcHJvZHVjdHMgJT4lDQogIHJlbmFtZShwcm9kdWN0X2lkID0gaWQpDQoNCnByb2R1Y3RzDQpgYGANCg0KMi5Kb2luIG9yZGVycyBhbmQgcHJvZHVjdHMuIFNhdmUgdGhlIHJlc3VsdCB0byB0aGUgdmFyaWFibGUgb3JkZXJzX3Byb2R1Y3RzLCBhbmQgdmlldyBpdC4NCg0KYGBge3J9DQojIGRlZmluZSBvcmRlcnNfcHJvZHVjdHMgaGVyZToNCm9yZGVyc19wcm9kdWN0cyA8LSBpbm5lcl9qb2luKG9yZGVycywgcHJvZHVjdHMpDQoNCm9yZGVyc19wcm9kdWN0cw0KYGBgDQoNCiMgNi5Kb2luIG9uIFNwZWNpZmljIENvbHVtbnMgSUkNCg0KSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCB3ZSBsZWFybmVkIGhvdyB0byB1c2UgcmVuYW1lKCkgdG8gam9pbiB0d28gZGF0YSBmcmFtZXMgd2hvc2UgY29sdW1ucyBkb27igJl0IG1hdGNoLg0KDQpBIGJldHRlciBvcHRpb24sIGhvd2V2ZXIsIGV4aXN0cy4gV2UgY2FuIGFkZCB0aGUgYnkgYXJndW1lbnQgd2hlbiBjYWxsaW5nIGlubmVyX2pvaW4oKSB0byBzcGVjaWZ5IHdoaWNoIGNvbHVtbnMgd2Ugd2FudCB0byBqb2luIG9uLiBJbiB0aGUgZXhhbXBsZSBiZWxvdywgdGhlIOKAnGxlZnTigJ0gdGFibGUgaXMgdGhlIG9uZSB0aGF0IGNvbWVzIGZpcnN0IChvcmRlcnMpLCBhbmQgdGhlIOKAnHJpZ2h04oCdIHRhYmxlIGlzIHRoZSBvbmUgdGhhdCBjb21lcyBzZWNvbmQgKGN1c3RvbWVycykuIFRoaXMgc3ludGF4IHNheXMgdGhhdCB3ZSBzaG91bGQgbWF0Y2ggdGhlIGN1c3RvbWVyX2lkIGZyb20gb3JkZXJzIHRvIHRoZSBpZCBpbiBjdXN0b21lcnMuDQoNCmBgYHtyfQ0KY3VzdG9tZXJzIDwtIHJlYWRfY3N2KCJjdXN0b21lcnMxLmNzdiIpDQpvcmRlcnMgPC0gcmVhZF9jc3YoIm9yZGVyczEuY3N2IikNCm9yZGVycyAlPiUgDQogIGlubmVyX2pvaW4oY3VzdG9tZXJzLA0KICAgICAgICAgICAgIGJ5ID0gYygnY3VzdG9tZXJfaWQnID0gJ2lkJykpDQpgYGANCg0KSWYgd2UgdXNlIHRoaXMgc3ludGF4LCB3ZeKAmWxsIGVuZCB1cCB3aXRoIHR3byBjb2x1bW5zIGNhbGxlZCBpZCwgb25lIGZyb20gdGhlIGZpcnN0IHRhYmxlIGFuZCBvbmUgZnJvbSB0aGUgc2Vjb25kLiBSIHdvbuKAmXQgbGV0IHlvdSBoYXZlIHR3byBjb2x1bW5zIHdpdGggdGhlIHNhbWUgbmFtZSwgc28gaXQgd2lsbCBjaGFuZ2UgdGhlbSB0byBpZF94IGFuZCBpZF95Lg0KYGBge3J9DQpjdXN0b21lcnMgPC0gcmVhZF9jc3YoImN1c3RvbWVyczEuY3N2IikNCm9yZGVycyA8LSByZWFkX2Nzdigib3JkZXJzMS5jc3YiKQ0KY3VzdG9tZXJzDQpvcmRlcnMNCmpvaW4gPC0gb3JkZXJzICU+JSANCiAgaW5uZXJfam9pbihjdXN0b21lcnMsDQogICAgICAgICAgICAgYnkgPSBjKCdjdXN0b21lcl9pZCcgPSAnaWQnKSkNCg0Kam9pbg0KYGBgDQoNCg0KVGhlIG5ldyBjb2x1bW4gbmFtZXMgaWRfeCBhbmQgaWRfeSBhcmVu4oCZdCB2ZXJ5IGhlbHBmdWwgZm9yIHVzIHdoZW4gd2UgcmVhZCB0aGUgdGFibGUuIFdlIGNhbiBoZWxwIG1ha2UgdGhlbSBtb3JlIHVzZWZ1bCBieSB1c2luZyB0aGUga2V5d29yZCBzdWZmaXguIFdlIGNhbiBwcm92aWRlIGEgdmVjdG9yIG9mIHN1ZmZpeGVzIHRvIHVzZSBpbnN0ZWFkIG9mIOKAnF944oCdIGFuZCDigJxfeeKAnS4NCg0KRm9yIGV4YW1wbGUsIHdlIGNvdWxkIHVzZSB0aGUgZm9sbG93aW5nIGNvZGUgdG8gbWFrZSB0aGUgc3VmZml4ZXMgcmVmbGVjdCB0aGUgdGFibGUgbmFtZXM6DQoNCmBgYHtyfQ0Kam9pbiA8LSBvcmRlcnMgJT4lIA0KICBpbm5lcl9qb2luKGN1c3RvbWVycywNCiAgICAgICAgICAgICBieSA9IGMoJ2N1c3RvbWVyX2lkJyA9ICdpZCcpLA0KICAgICAgICAgICAgIHN1ZmZpeCA9IGMoJ19vcmRlcicsJ19jdXN0b21lcicpKQ0KDQpqb2luDQpgYGANCg0KDQojIyBJbnN0cnVjdGlvbnMNCg0KMS5Kb2luIHRoZSBvcmRlcnMgYW5kIHByb2R1Y3RzIGRhdGEgZnJhbWVzIHVzaW5nIGFuIGlubmVyX2pvaW4oKSwgd2l0aCBvcmRlcnMgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBwcm9kdWN0cyBhcyB0aGUgc2Vjb25kIGFyZ3VtZW50LiBBbHNvIGluY2x1ZGUgdGhlIGJ5IGFyZ3VtZW50IHRvIGluZGljYXRlIHdoaWNoIGNvbHVtbnMgdG8gam9pbiBvbi4gU2F2ZSB5b3VyIHJlc3VsdHMgdG8gdGhlIHZhcmlhYmxlIG9yZGVyc19wcm9kdWN0cywgYW5kIHZpZXcgaXQuDQoNCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFfQ0KIyBsb2FkIG9yZGVycyBhbmQgcHJvZHVjdHMgZGF0YQ0Kb3JkZXJzIDwtIHJlYWRfY3N2KCJvcmRlcnMxLmNzdiIpDQpwcm9kdWN0cyA8LSByZWFkX2NzdigicHJvZHVjdHMxLmNzdiIpDQpgYGANCg0KYGBge3J9DQojIGluc3BlY3Qgb3JkZXJzIGFuZCBwcm9kdWN0cw0KaGVhZChvcmRlcnMpDQpoZWFkKHByb2R1Y3RzKQ0KYGBgDQoNCmBgYHtyfQ0KIyBkZWZpbmUgb3JkZXJzX3Byb2R1Y3RzIGhlcmU6DQpvcmRlcnNfcHJvZHVjdHMgPC0gaW5uZXJfam9pbihvcmRlcnMsIHByb2R1Y3RzLCBieSA9IGMoJ3Byb2R1Y3RfaWQnID0gJ2lkJykpDQoNCm9yZGVyc19wcm9kdWN0cw0KDQpgYGANCg0KMi5Ob3cgam9pbiB0aGUgcHJvZHVjdHMgYW5kIG9yZGVycyBkYXRhIGZyYW1lcyB1c2luZyBhbiBpbm5lcl9qb2luKCksIHdpdGggcHJvZHVjdHMgYXMgdGhlIGZpcnN0IGFyZ3VtZW50IGFuZCBvcmRlcnMgYXMgdGhlIHNlY29uZCBhcmd1bWVudC4gQWxzbyBpbmNsdWRlIHRoZSBieSBhcmd1bWVudCB0byBpbmRpY2F0ZSB3aGljaCBjb2x1bW5zIHRvIGpvaW4gb24sIGFzIHdlbGwgYXMgYSBzdWZmaXggYXJndW1lbnQgYygnX3Byb2R1Y3QnLCdfb3JkZXInKS4gU2F2ZSB5b3VyIHJlc3VsdHMgdG8gdGhlIHZhcmlhYmxlIHByb2R1Y3RzX29yZGVycywgYW5kIHZpZXcgaXQuDQoNCmBgYHtyfQ0KIyBkZWZpbmUgcHJvZHVjdHNfb3JkZXJzIGhlcmU6DQpwcm9kdWN0c19vcmRlcnMgPC0gaW5uZXJfam9pbihwcm9kdWN0cywgb3JkZXJzLCBieSA9IGMoJ2lkJyA9ICdwcm9kdWN0X2lkJyksIHN1ZmZpeCA9IGMoJ19wcm9kdWN0JywnX29yZGVyJykpDQoNCnByb2R1Y3RzX29yZGVycw0KYGBgDQoNCiMgNy5NaXNtYXRjaGVkIEpvaW5zDQoNCkluIG91ciBwcmV2aW91cyBleGFtcGxlcywgdGhlcmUgd2VyZSBhbHdheXMgbWF0Y2hpbmcgdmFsdWVzIHdoZW4gd2Ugd2VyZSBwZXJmb3JtaW5nIG91ciBqb2lucy4gV2hhdCBoYXBwZW5zIHdoZW4gdGhhdCBpc27igJl0IHRydWU/DQoNCkxldOKAmXMgaW1hZ2luZSB0aGF0IG91ciBwcm9kdWN0cyB0YWJsZSBpcyBvdXQgb2YgZGF0ZSBhbmQgaXMgbWlzc2luZyB0aGUgbmV3ZXN0IHByb2R1Y3Q6IFByb2R1Y3QgNS4gV2hhdCBoYXBwZW5zIHdoZW4gc29tZW9uZSBvcmRlcnMgaXQ/DQoNCiMjIEluc3RydWN0aW9ucw0KDQoxLldl4oCZdmUganVzdCByZWxlYXNlZCBhIG5ldyBwcm9kdWN0IHdpdGggcHJvZHVjdF9pZCBlcXVhbCB0byA1LiBQZW9wbGUgYXJlIG9yZGVyaW5nIHRoaXMgcHJvZHVjdCwgYnV0IHdlIGhhdmVu4oCZdCB1cGRhdGVkIHRoZSBwcm9kdWN0cyB0YWJsZS4NCg0KSW4gbm90ZWJvb2suUm1kLCB5b3XigJlsbCBmaW5kIHR3byBkYXRhIGZyYW1lczogb3JkZXJzIGFuZCBwcm9kdWN0cy4gSW5zcGVjdCB0aGVzZSBkYXRhIGZyYW1lcyB1c2luZyBoZWFkKCkuDQoNCk5vdGljZSB0aGF0IHRoZSB0aGlyZCBvcmRlciBpbiBvcmRlcnMgaXMgZm9yIHRoZSBteXN0ZXJpb3VzIG5ldyBwcm9kdWN0LCBidXQgdGhhdCB0aGVyZSBpcyBubyBwcm9kdWN0X2lkIDUgaW4gcHJvZHVjdHMuDQpgYGB7ciBtZXNzYWdlID0gRkFMU0V9DQojIGxvYWQgb3JkZXJzIGFuZCBwcm9kdWN0cyBkYXRhDQpvcmRlcnMgPC0gcmVhZF9jc3YoIm9yZGVyczIuY3N2IikNCnByb2R1Y3RzIDwtIHJlYWRfY3N2KCJwcm9kdWN0czIuY3N2IikNCmBgYA0KYGBge3J9DQojIGluc3BlY3Qgb3JkZXJzIGFuZCBwcm9kdWN0cyBoZXJlOg0KaGVhZChvcmRlcnMpDQpoZWFkKHByb2R1Y3RzKQ0KYGBgDQoNCg0KMi5Kb2luIHRoZSBvcmRlcnMgYW5kIHByb2R1Y3RzIGRhdGEgZnJhbWVzIHdpdGggaW5uZXJfam9pbigpIGFuZCBzYXZlIHRoZSByZXN1bHRpbmcgZGF0YSBmcmFtZSB0byB0aGUgdmFyaWFibGUgb3JkZXJzX3Byb2R1Y3RzLiBWaWV3IG9yZGVyc19wcm9kdWN0cy4NCg0KV2hhdCBoYXBwZW5lZCB0byBvcmRlcl9pZCAzPw0KDQpgYGB7cn0NCiMgZGVmaW5lIG9yZGVyc19wcm9kdWN0cyBoZXJlOg0Kb3JkZXJzX3Byb2R1Y3RzIDwtIGlubmVyX2pvaW4ob3JkZXJzLCBwcm9kdWN0cykNCm9yZGVyc19wcm9kdWN0cw0KYGBgDQoNCiMgOC5GdWxsIEpvaW4NCg0KSW4gdGhlIHByZXZpb3VzIGV4ZXJjaXNlLCB3ZSBzYXcgdGhhdCB3aGVuIHdlIGpvaW4gdHdvIGRhdGEgZnJhbWVzIHdob3NlIHJvd3MgZG9u4oCZdCBtYXRjaCBwZXJmZWN0bHksIHdlIGxvc2UgdGhlIHVubWF0Y2hlZCByb3dzLg0KDQpUaGlzIHR5cGUgb2Ygam9pbiAod2hlcmUgd2Ugb25seSBpbmNsdWRlIG1hdGNoaW5nIHJvd3MpIGlzIGNhbGxlZCBhbiBpbm5lciBqb2luLiBUaGVyZSBhcmUgb3RoZXIgdHlwZXMgb2Ygam9pbnMgdGhhdCB3ZSBjYW4gdXNlIHdoZW4gd2Ugd2FudCB0byBrZWVwIGluZm9ybWF0aW9uIGZyb20gdGhlIHVubWF0Y2hlZCByb3dzLg0KDQpTdXBwb3NlIHRoYXQgdHdvIGNvbXBhbmllcywgQ29tcGFueSBBIGFuZCBDb21wYW55IEIgaGF2ZSBqdXN0IG1lcmdlZC4gVGhleSBlYWNoIGhhdmUgYSBsaXN0IG9mIGN1c3RvbWVycywgYnV0IHRoZXkga2VlcCBzbGlnaHRseSBkaWZmZXJlbnQgZGF0YS4gQ29tcGFueSBBIGhhcyBlYWNoIGN1c3RvbWVy4oCZcyBuYW1lIGFuZCBlbWFpbC4gQ29tcGFueSBCIGhhcyBlYWNoIGN1c3RvbWVy4oCZcyBuYW1lIGFuZCBwaG9uZSBudW1iZXIuIFRoZXkgaGF2ZSBzb21lIGN1c3RvbWVycyBpbiBjb21tb24sIGJ1dCBzb21lIGFyZSBkaWZmZXJlbnQuDQoNCmBgYHtyfQ0KY29tcGFueV9hIDwtIHJlYWRfY3N2KCJjb21wYW55X2EuY3N2IikNCmNvbXBhbnlfYiA8LSByZWFkX2NzdigiY29tcGFueV9iLmNzdiIpDQpgYGANCmBgYHtyfQ0KY29tcGFueV9hDQpjb21wYW55X2INCmBgYA0KDQpJZiB3ZSB3YW50ZWQgdG8gY29tYmluZSB0aGUgZGF0YSBmcm9tIGJvdGggY29tcGFuaWVzIHdpdGhvdXQgbG9zaW5nIHRoZSBjdXN0b21lcnMgd2hvIGFyZSBtaXNzaW5nIGZyb20gb25lIG9mIHRoZSB0YWJsZXMsIHdlIGNvdWxkIHVzZSBhIEZ1bGwgSm9pbi4gQSBGdWxsIEpvaW4gd291bGQgaW5jbHVkZSBhbGwgcm93cyBmcm9tIGJvdGggdGFibGVzLCBldmVuIGlmIHRoZXkgZG9u4oCZdCBtYXRjaC4gQW55IG1pc3NpbmcgdmFsdWVzIGFyZSBmaWxsZWQgaW4gd2l0aCBOQS4NCg0KYGBge3J9DQpmdWxsX2pvaW5lZF9kZnMgPC0gY29tcGFueV9hICU+JQ0KICBmdWxsX2pvaW4oY29tcGFueV9iKQ0KDQpmdWxsX2pvaW5lZF9kZnMNCmBgYA0KDQojIyBJbnN0cnVjdGlvbnMNCg0KMS5UaGVyZSBhcmUgdHdvIGhhcmR3YXJlIHN0b3JlcyBpbiB0b3duOiBTdG9yZSBBIGFuZCBTdG9yZSBCLiBTdG9yZSBB4oCZcyBpbnZlbnRvcnkgaXMgaW4gZGF0YSBmcmFtZSBzdG9yZV9hIGFuZCBTdG9yZSBC4oCZcyBpbnZlbnRvcnkgaXMgaW4gZGF0YSBmcmFtZSBzdG9yZV9iLiBUaGV5IGhhdmUgZGVjaWRlZCB0byBtZXJnZSBpbnRvIG9uZSBiaWcgU3VwZXIgU3RvcmUhDQoNCkNvbWJpbmUgdGhlIGludmVudG9yaWVzIG9mIFN0b3JlIEEgYW5kIFN0b3JlIEIgdXNpbmcgYSBmdWxsIGpvaW4uIFNhdmUgdGhlIHJlc3VsdHMgdG8gdGhlIHZhcmlhYmxlIHN0b3JlX2FfYl9mdWxsLg0KDQpgYGB7ciBtZXNzYWdlID0gRkFMU0V9DQojIGxvYWQgc3RvcmVfYSBhbmQgc3RvcmVfYiBkYXRhDQpzdG9yZV9hIDwtIHJlYWRfY3N2KCJzdG9yZV9hLmNzdiIpDQpzdG9yZV9iIDwtIHJlYWRfY3N2KCJzdG9yZV9iLmNzdiIpDQpgYGANCg0KYGBge3J9DQojIGluc3BlY3Qgc3RvcmVfYSBhbmQgc3RvcmVfYg0Kc3RvcmVfYQ0Kc3RvcmVfYg0KYGBgDQoNCmBgYHtyfQ0KIyBkZWZpbmUgc3RvcmVfYV9iX2Z1bGwgaGVyZToNCnN0b3JlX2FfYl9mdWxsIDwtIGZ1bGxfam9pbihzdG9yZV9hLCBzdG9yZV9iKQ0KDQpzdG9yZV9hX2JfZnVsbA0KYGBgDQoNCiMgOS5MZWZ0IGFuZCBSaWdodCBKb2lucw0KDQpMZXTigJlzIHJldHVybiB0byB0aGUgam9pbiBvZiBDb21wYW55IEEgYW5kIENvbXBhbnkgQi4NCg0KIyMjIExlZnQgSm9pbg0KDQpTdXBwb3NlIHdlIHdhbnQgdG8gaWRlbnRpZnkgd2hpY2ggY3VzdG9tZXJzIGFyZSBtaXNzaW5nIHBob25lIGluZm9ybWF0aW9uLiBXZSB3b3VsZCB3YW50IGEgbGlzdCBvZiBhbGwgY3VzdG9tZXJzIHdobyBoYXZlIGVtYWlsLCBidXQgZG9u4oCZdCBoYXZlIHBob25lLg0KDQpXZSBjb3VsZCBnZXQgdGhpcyBieSBwZXJmb3JtaW5nIGEgTGVmdCBKb2luLiBBIExlZnQgSm9pbiBpbmNsdWRlcyBhbGwgcm93cyBmcm9tIHRoZSBmaXJzdCAobGVmdCkgdGFibGUsIGJ1dCBvbmx5IHJvd3MgZnJvbSB0aGUgc2Vjb25kIChyaWdodCkgdGFibGUgdGhhdCBtYXRjaCB0aGUgZmlyc3QgdGFibGUuDQoNCkZvciB0aGlzIGNvbW1hbmQsIHRoZSBvcmRlciBvZiB0aGUgYXJndW1lbnRzIG1hdHRlcnMuIElmIHRoZSBmaXJzdCBkYXRhIGZyYW1lIGlzIGNvbXBhbnlfYSBhbmQgd2UgZG8gYSBsZWZ0IGpvaW4sIHdl4oCZbGwgb25seSBlbmQgdXAgd2l0aCByb3dzIHRoYXQgYXBwZWFyIGluIGNvbXBhbnlfYS4NCg0KQnkgbGlzdGluZyBjb21wYW55X2EgZmlyc3QsIHdlIGdldCBhbGwgY3VzdG9tZXJzIGZyb20gQ29tcGFueSBBLCBhbmQgb25seSBjdXN0b21lcnMgZnJvbSBDb21wYW55IEIgd2hvIGFyZSBhbHNvIGN1c3RvbWVycyBvZiBDb21wYW55IEEuDQoNCmBgYHtyfQ0KbGVmdF9qb2luZWRfZGYgPC0gY29tcGFueV9hICU+JQ0KICBsZWZ0X2pvaW4oY29tcGFueV9iKQ0KDQpgYGANClRoZSByZXN1bHQgd291bGQgbG9vayBsaWtlIHRoaXM6DQpgYGB7cn0NCmxlZnRfam9pbmVkX2RmDQpgYGANCg0KTm93IGxldOKAmXMgc2F5IHdlIHdhbnQgYSBsaXN0IG9mIGFsbCBjdXN0b21lcnMgd2hvIGhhdmUgcGhvbmUgYnV0IG5vIGVtYWlsLiBXZSBjYW4gZG8gdGhpcyBieSBwZXJmb3JtaW5nIGEgUmlnaHQgSm9pbi4NCg0KIyMjIFJpZ2h0IEpvaW4NCg0KQSBSaWdodCBKb2luIGlzIHRoZSBleGFjdCBvcHBvc2l0ZSBvZiBsZWZ0IGpvaW4uIEhlcmUsIHRoZSBqb2luZWQgdGFibGUgd2lsbCBpbmNsdWRlIGFsbCByb3dzIGZyb20gdGhlIHNlY29uZCAocmlnaHQpIHRhYmxlLCBidXQgb25seSByb3dzIGZyb20gdGhlIGZpcnN0IChsZWZ0KSB0YWJsZSB0aGF0IG1hdGNoIHRoZSBzZWNvbmQgdGFibGUuDQoNCkJ5IGxpc3RpbmcgY29tcGFueV9hIGZpcnN0IGFuZCBjb21wYW55X2Igc2Vjb25kLCB3ZSBnZXQgYWxsIGN1c3RvbWVycyBmcm9tIENvbXBhbnkgQiwgYW5kIG9ubHkgY3VzdG9tZXJzIGZyb20gQ29tcGFueSBBIHdobyBhcmUgYWxzbyBjdXN0b21lcnMgb2YgQ29tcGFueSBCLg0KDQpgYGB7cn0NCnJpZ2h0X2pvaW5lZF9kZiA8LSBjb21wYW55X2EgJT4lDQogIHJpZ2h0X2pvaW4oY29tcGFueV9iKQ0KDQpgYGANCg0KVGhlIHJlc3VsdCB3b3VsZCBsb29rIGxpa2UgdGhpczoNCg0KYGBge3J9DQpyaWdodF9qb2luZWRfZGYNCmBgYA0KDQojIyBJbnN0cmN0aW9ucw0KMS5MZXTigJlzIHJldHVybiB0byB0aGUgdHdvIGhhcmR3YXJlIHN0b3JlcywgU3RvcmUgQSBhbmQgU3RvcmUgQi4gVGhleeKAmXJlIG5vdCBxdWl0ZSBzdXJlIGlmIHRoZXkgd2FudCB0byBtZXJnZSBpbnRvIGEgYmlnIFN1cGVyIFN0b3JlIGp1c3QgeWV0Lg0KDQpTdG9yZSBBIHdhbnRzIHRvIGZpbmQgb3V0IHdoYXQgcHJvZHVjdHMgdGhleSBjYXJyeSB0aGF0IFN0b3JlIEIgZG9lcyBub3QgY2FycnkuIFVzaW5nIGEgbGVmdCBqb2luLCBjb21iaW5lIHN0b3JlX2EgdG8gc3RvcmVfYiBhbmQgc2F2ZSB0aGUgcmVzdWx0cyB0byBsZWZ0X2FfYi4NCg0KVGhlIGl0ZW1zIHdpdGggTkEgaW4gbGVmdF9hX2IgYXJlIGNhcnJpZWQgYnkgU3RvcmUgQSwgYnV0IG5vdCBieSBTdG9yZSBCLg0KDQpgYGB7cn0NCiMgZGVmaW5lIGxlZnRfYV9iIGhlcmU6DQpsZWZ0X2FfYiA8LSBzdG9yZV9hICU+JQ0KICBsZWZ0X2pvaW4oc3RvcmVfYikNCg0KbGVmdF9hX2INCmBgYA0KDQoyLk5vdywgU3RvcmUgQiB3YW50cyB0byBmaW5kIG91dCB3aGF0IHByb2R1Y3RzIHRoZXkgY2FycnkgdGhhdCBTdG9yZSBBIGRvZXMgbm90IGNhcnJ5LiBVc2UgYSBsZWZ0IGpvaW4sIHRvIGNvbWJpbmUgdGhlIHR3byBkYXRhIGZyYW1lcyBidXQgaW4gdGhlIHJldmVyc2Ugb3JkZXIgKGkuZS4sIHN0b3JlX2IgZm9sbG93ZWQgYnkgc3RvcmVfYSkgYW5kIHNhdmUgdGhlIHJlc3VsdHMgdG8gdGhlIHZhcmlhYmxlIGxlZnRfYl9hLg0KDQpXaGljaCBpdGVtcyBhcmUgbm90IGNhcnJpZWQgYnkgU3RvcmUgQSwgYnV0IGFyZSBjYXJyaWVkIGJ5IFN0b3JlIEI/DQoNCldoYXQgZG8geW91IG5vdGljZSBhYm91dCB0aGVzZSB0d28gZGF0YSBmcmFtZXM/DQoNCkhvdyBhcmUgdGhleSBkaWZmZXJlbnQ/DQoNCkhvdyBhcmUgdGhleSB0aGUgc2FtZT8NCmBgYHtyfQ0KIyBkZWZpbmUgbGVmdF9iX2EgaGVyZToNCmxlZnRfYl9hIDwtIHN0b3JlX2IgJT4lDQogIGxlZnRfam9pbihzdG9yZV9hKQ0KDQpsZWZ0X2JfYQ0KYGBgDQoNCiMgMTAuQ29uY2F0ZW5hdGUgRGF0YSBGcmFtZXMNClNvbWV0aW1lcywgYSBkYXRhc2V0IGlzIGJyb2tlbiBpbnRvIG11bHRpcGxlIHRhYmxlcy4gRm9yIGluc3RhbmNlLCBkYXRhIGlzIG9mdGVuIHNwbGl0IGludG8gbXVsdGlwbGUgQ1NWIGZpbGVzIHNvIHRoYXQgZWFjaCBkb3dubG9hZCBpcyBzbWFsbGVyLg0KDQpXaGVuIHdlIG5lZWQgdG8gcmVjb25zdHJ1Y3QgYSBzaW5nbGUgZGF0YSBmcmFtZSBmcm9tIG11bHRpcGxlIHNtYWxsZXIgZGF0YSBmcmFtZXMsIHdlIGNhbiB1c2UgdGhlIGRwbHlyIGJpbmRfcm93cygpIG1ldGhvZC4gVGhpcyBtZXRob2Qgb25seSB3b3JrcyBpZiBhbGwgb2YgdGhlIGNvbHVtbnMgYXJlIHRoZSBzYW1lIGluIGFsbCBvZiB0aGUgZGF0YSBmcmFtZXMuDQoNCkZvciBpbnN0YW5jZSwgc3VwcG9zZSB0aGF0IHdlIGhhdmUgdHdvIGRhdGEgZnJhbWVzOg0KYGBge3J9DQpkZjEgPC0gcmVhZF9jc3YoImRmMS5jc3YiKQ0KZGYyIDwtIHJlYWRfY3N2KCJkZjIuY3N2IikNCmBgYA0KDQpgYGB7cn0NCmRmMQ0KZGYyDQpgYGANCg0KSWYgd2Ugd2FudCB0byBjb21iaW5lIHRoZXNlIHR3byBkYXRhIGZyYW1lcywgd2UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNvbW1hbmQ6DQoNCmBgYHtyfQ0KY29uY2F0ZW5hdGVkX2RmcyA8LSBkZjEgJT4lDQogIGJpbmRfcm93cyhkZjIpDQoNCmBgYA0KDQpUaGF0IHdvdWxkIHJlc3VsdCBpbiB0aGUgZm9sbG93aW5nIGRhdGEgZnJhbWU6DQoNCmBgYHtyfQ0KY29uY2F0ZW5hdGVkX2Rmcw0KYGBgDQoNCiMjIEluc3RydWN0aW9ucw0KDQoxLkFuIGljZSBjcmVhbSBwYXJsb3IgYW5kIGEgYmFrZXJ5IGhhdmUgZGVjaWRlZCB0byBtZXJnZS4NCg0KVGhlIGJha2VyeeKAmXMgbWVudSBpcyBzdG9yZWQgaW4gdGhlIGRhdGEgZnJhbWUgYmFrZXJ5LCBhbmQgdGhlIGljZSBjcmVhbSBwYXJsb3LigJlzIG1lbnUgaXMgc3RvcmVkIGluIHRoZSBkYXRhIGZyYW1lIGljZV9jcmVhbS4NCg0KQ3JlYXRlIHRoZWlyIG5ldyBtZW51IGJ5IGNvbmNhdGVuYXRpbmcgdGhlIHR3byBkYXRhIGZyYW1lcyBpbnRvIGEgZGF0YSBmcmFtZSBjYWxsZWQgbWVudS4NCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFfQ0KIyBsb2FkIGJha2VyeSBhbmQgaWNlX2NyZWFtIGRhdGENCmJha2VyeSA8LSByZWFkX2NzdignYmFrZXJ5LmNzdicpDQppY2VfY3JlYW0gPC0gcmVhZF9jc3YoJ2ljZV9jcmVhbS5jc3YnKQ0KYGBgDQoNCmBgYHtyfQ0KIyBpbnNwZWN0IGJha2VyeSBhbmQgaWNlX2NyZWFtDQpoZWFkKGJha2VyeSkNCmhlYWQoaWNlX2NyZWFtKQ0KYGBgDQoNCmBgYHtyfQ0KIyBkZWZpbmUgbWVudSBoZXJlOg0KbWVudSA8LSBiaW5kX3Jvd3MoYmFrZXJ5LCBpY2VfY3JlYW0pDQoNCm1lbnUNCmBgYA0KDQojIDExLlJldmlldw0KDQpUaGlzIGxlc3NvbiBpbnRyb2R1Y2VkIHNvbWUgbWV0aG9kcyBmb3IgY29tYmluaW5nIG11bHRpcGxlIGRhdGEgZnJhbWVzOg0KDQoxLkNyZWF0aW5nIGEgZGF0YSBmcmFtZSBtYWRlIGJ5IG1hdGNoaW5nIHRoZSBjb21tb24gY29sdW1ucyBvZiB0d28gaXMgY2FsbGVkIGEgam9pbg0KDQoyLldlIGNhbiBzcGVjaWZ5IHdoaWNoIGNvbHVtbnMgc2hvdWxkIGJlIG1hdGNoZWQgYnkgdXNpbmcgdGhlIGJ5IGFyZ3VtZW50DQoNCjMuV2UgY2FuIGNvbWJpbmUgZGF0YSBmcmFtZXMgd2hvc2Ugcm93cyBkb27igJl0IGFsbCBtYXRjaCB1c2luZyBsZWZ0LCByaWdodCwgYW5kIGZ1bGwgam9pbnMNCg0KNC5XZSBjYW4gc3RhY2sgb3IgY29uY2F0ZW5hdGUgZGF0YSBmcmFtZXMgd2l0aCB0aGUgc2FtZSBjb2x1bW5zIHVzaW5nIGJpbmRfcm93cygpDQoNCg0KIyMgSW5zdHJ1Y3Rpb25zDQoxLkNvb2wgVC1TaGlydHMgSW5jLiBqdXN0IGNyZWF0ZWQgYSB3ZWJzaXRlIGZvciBvcmRlcmluZyB0aGVpciBwcm9kdWN0cy4gVGhleSB3YW50IHlvdSB0byBhbmFseXplIHR3byBkYXRhc2V0cyBmb3IgdGhlbToNCg0KKDEpdmlzaXRzIGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIGFsbCB2aXNpdHMgdG8gdGhlaXIgbGFuZGluZyBwYWdlDQoNCigyKWNoZWNrb3V0cyBjb250YWlucyBhbGwgdXNlcnMgd2hvIGJlZ2FuIHRvIGNoZWNrb3V0IG9uIHRoZWlyIHdlYnNpdGUNCg0KVXNlIGhlYWQoKSB0byBpbnNwZWN0IGVhY2ggZGF0YSBmcmFtZS4NCg0KYGBge3IgbWVzc2FnZSA9IEZBTFNFfQ0KIyBsb2FkIHZpc2l0cyBhbmQgY2hlY2tvdXRzIGRhdGENCnZpc2l0cyA8LSByZWFkX2NzdigndmlzaXRzLmNzdicpDQpjaGVja291dHMgPC0gcmVhZF9jc3YoJ2NoZWNrb3V0cy5jc3YnKQ0KYGBgDQoNCjIuV2Ugd2FudCB0byBrbm93IHRoZSBhbW91bnQgb2YgdGltZSBmcm9tIGEgdXNlcuKAmXMgaW5pdGlhbCB2aXNpdCB0byB0aGUgd2Vic2l0ZSB0byB3aGVuIHRoZXkgc3RhcnQgdG8gY2hlY2sgb3V0Lg0KDQpVc2UgaW5uZXJfam9pbiB0byBjb21iaW5lIHZpc2l0cyBhbmQgY2hlY2tvdXRzIGFuZCBzYXZlIGl0IHRvIHRoZSB2YXJpYWJsZSB2X3RvX2MuIFZpZXcgdl90b19jLg0KDQpgYGB7cn0NCiMgaW5zcGVjdCB2aXNpdHMgYW5kIGNoZWNrb3V0cyBoZXJlOg0KaGVhZCh2aXNpdHMpDQpoZWFkKGNoZWNrb3V0cykNCmBgYA0KDQpgYGB7cn0NCiMgZGVmaW5lIHZfdG9fYyBoZXJlOg0KDQp2X3RvX2MgPC0gaW5uZXJfam9pbih2aXNpdHMsIGNoZWNrb3V0cykNCg0Kdl90b19jDQpgYGANCjMuSW4gb3JkZXIgdG8gY2FsY3VsYXRlIHRoZSB0aW1lIGJldHdlZW4gdmlzaXRpbmcgYW5kIGNoZWNraW5nIG91dCwgZGVmaW5lIGEgY29sdW1uIG9mIHZfdG9fYyBjYWxsZWQgdGltZSBieSBwYXN0aW5nIHRoZSBmb2xsb3dpbmcgY29kZSBpbnRvIG5vdGVib29rLlJtZDoNCg0KYGBge3J9DQojIGRlZmluZSBhdmdfdGltZV90b19jaGVjayBoZXJlOg0Kdl90b19jIDwtIHZfdG9fYyAlPiUgDQogIG11dGF0ZSh0aW1lID0gY2hlY2tvdXRfdGltZSAtIHZpc2l0X3RpbWUpDQp2X3RvX2MNCmBgYA0KDQo0LlRvIGdldCB0aGUgYXZlcmFnZSB0aW1lIHRvIGNoZWNrb3V0LCBwYXN0ZSB0aGUgZm9sbG93aW5nIGNvZGUgaW50byBub3RlYm9vay5SbWQ6DQoNCmBgYHtyfQ0KYXZnX3RpbWVfdG9fY2hlY2sgPC0gdl90b19jICU+JSANCiAgc3VtbWFyaXplKG1lYW5fdGltZSA9IG1lYW4odGltZSkpDQoNCmF2Z190aW1lX3RvX2NoZWNrDQpgYGA=