Introduction
Subqueries and the WITH statement are useful query formulation mechanisms for writing complex queries. This lesson demonstrates, using examples, how to use both in queries and how to simplify database access using the construct.
Sample Database
For many of the examples we will use the Sakila Media and Film database. An ERD of the database is shown below. The examples are executed on SQLite using sakiladb.sqlitedb.
Subqueries
A subquery, also known as a nested query or inner query, is an SQL query that is embedded within another SQL query. It can be used in various parts of a query, including SELECT
, FROM
, WHERE
, and HAVING
clauses. Subqueries are primarily used for performing operations that require multiple steps in data processing. They allow for more flexible and efficient data retrieval, enabling complex operations to be executed in a single query.
Benefits of Subqueries
Subqueries offer several benefits, including:
- Enhancing Data Retrieval: Subqueries provide a powerful tool for retrieving data in complex situations where the use of standard SQL queries might be limiting or inefficient.
- Complex Data Relationships: They are particularly useful in handling scenarios where data relationships are complex and require multiple layers of filtering or selection.
- Nested Logic: Subqueries offer a way to implement nested logic in SQL queries, allowing for sophisticated data analysis and manipulation.
Characteristics of Subqueries
- Return Types: Subqueries can return individual values, a single row, multiple rows, or even a table-like structure.
- Independent vs. Correlated: They can be independent of the outer query (standalone) or correlated, where the subquery depends on data from the outer query.
- Placement Flexibility: Subqueries can be used in various places within a query, such as:
- In the
SELECT
clause, to add columns to the main query.
- In the
FROM
clause, to create a derived table that the main query can use.
- In the
WHERE
or HAVING
clauses, to filter records based on complex conditions.
Advantages and Limitations
- Advantages:
- Modularity: Breaking down complex queries into simpler, more manageable parts.
- Readability: Improving the readability of SQL scripts by encapsulating specific logic.
- Reusability: Facilitating the reuse of code and logic within the database.
- Limitations:
- Performance Concerns: Nested queries can sometimes lead to performance issues, particularly if not optimized correctly.
- Complexity: Overuse or misuse of subqueries can make SQL statements more complex and harder to maintain.
Simple Example
How many payments have a below average amount?
This query requires us to know the average payment amount, but that amount varies with the data, so it cannot be hard coded; a subquery that calculates the account and used where the average is needed resolves the issue. Of course, since average is a scalar value, the subquery must return one value, but because the result must be a table, the subquery actually returns a table with one row and one column.
SELECT COUNT(payment_id) AS numGrtAvg
FROM Payment
WHERE amount < (SELECT AVG(amount) FROM Payment);
This section lays the foundation for understanding subqueries, setting the stage for more detailed exploration in the following sections of the lesson.
Expanding on the types of subqueries in SQL:
Types of Subqueries
Single-Row Subqueries
Definition: These return a single row from the inner query. They’re typically used with single-row comparison operators like =
, >
, <
, <=
, >=
, and <>
.
Use Cases: Ideal for situations where you expect the subquery to return exactly one row. For instance, finding an employee whose salary is equal to the maximum salary in a department or the value of a column is above or below average.
Example:
SELECT payment_id, amount
FROM Payment
WHERE amount = (SELECT MAX(amount) FROM Payment)
Table 2: Displaying records 1 - 10
342 |
11.99 |
3146 |
11.99 |
5280 |
11.99 |
5281 |
11.99 |
5550 |
11.99 |
6409 |
11.99 |
8272 |
11.99 |
9803 |
11.99 |
15821 |
11.99 |
15850 |
11.99 |
The example above finds those payments that are for the biggest (maximum) amount.
Exercise
Try the query below for yourself on the sample database.
Query
Which customer (last name, first name, and ID) purchased the most in terms of total amount?
Multiple-Row Subqueries
Definition: These return multiple rows and are used with existential or set membership operators like IN
, ANY
, ALL
, or comparisons combined with ANY
or ALL
.
Use Cases: Useful when the subquery is expected to return more than one row. For example, retrieving all orders that match any of the order IDs in a list.
Alternative: A simple inner-join or a theta-join can often be used instead.
Example:
The query below finds how many cities are located in a country that starts with a ‘B’.
SELECT count(*) AS `NumCities`
FROM City
WHERE country_id IN
(SELECT country_id
FROM Country
WHERE country LIKE 'B%')
Subquery as a Derived Table
Definition: This involves using a subquery in the FROM
clause of the main query. The result of the subquery acts as a temporary table.
Use Cases: Useful for creating complex joins and aggregations. For example, summarizing data in a subquery before joining it to another table.
Example:
SELECT C.customer_id, C.last_name, P.total_amount
FROM Customer AS C JOIN (SELECT customer_id, sum(amount) AS `total_amount`
FROM Payment
GROUP BY customer_id) AS P
ON (C.customer_id = P.customer_id)
Table 5: Displaying records 1 - 10
1 |
SMITH |
118.68 |
2 |
JOHNSON |
128.73 |
3 |
WILLIAMS |
135.74 |
4 |
JONES |
81.78 |
5 |
BROWN |
144.62 |
6 |
DAVIS |
93.72 |
7 |
MILLER |
151.67 |
8 |
WILSON |
92.76 |
9 |
MOORE |
89.77 |
10 |
TAYLOR |
99.75 |
Note how the subquery was provided with an alias using AS.
Existential Subqueries
Definition: These are used to test for the existence of rows in a subquery.
Use Cases: Commonly used in conditional logic, like checking if a certain condition is met in another table before performing an operation.
Example:
SELECT DISTINCT A.last_name, A.first_name
FROM Film_Actor AS FA JOIN Actor AS A USING (actor_id)
WHERE EXISTS
(SELECT * FROM Film
WHERE film_id = FA.film_id AND length > 180)
LIMIT 3;
Table 6: 3 records
GUINESS |
PENELOPE |
CHASE |
ED |
DAVIS |
JENNIFER |
Considerations
Unnecessary Complexity: Be careful not to overuse subqueries, as they can make the query complex and difficult to understand.
Subquery Optimization: It’s important to understand how subqueries can impact performance and to optimize them accordingly. For example, minimizing the number of rows processed by using appropriate WHERE clauses.
Nested Subqueries: Subqueries can be nested inside other subqueries, but this should be done judiciously to avoid excessive complexity and potential performance issues. While often simpler for some programmers to express a query, joins and group by are preferable when an option.
Linear Scans: Subqueries often result in query plans that use linear table scans rather than available indexes and are thus often vastly less efficient.
Independent Testing: Subqueries should be tested independently to ensure that the correct results are returned before incorporating them into other queries.
Each type of subquery offers unique functionalities and is suited for different scenarios in SQL querying. Understanding these types will help data analysts effectively use subqueries to solve complex data retrieval problems.
Common Table Expressions
The SQL WITH
statement, also known as Common Table Expressions (CTEs), is a useful feature for creating temporary result sets that can be referenced within a SELECT
, INSERT
, UPDATE
, or DELETE
statement. CTEs are similar to the ρ (rename) operator in relational algebra.
Basic Syntax
The basic syntax of the WITH
statement is as follows:
WITH CTE_Name AS (
-- Your SQL query here
)
SELECT * FROM CTE_Name;
Here, CTE_Name
is a temporary result set that you define within the WITH
clause. This result set can then be used in the main SQL query that follows in place of any table.
Advantages of Using WITH
Readability and Maintenance: CTEs can make complex queries more readable and maintainable by breaking them into simpler, modular parts.
Recursive Queries: CTEs enable recursive queries, which are useful for hierarchical data processing, like tree structures.
Performance Optimization: In some cases, CTEs can improve performance by simplifying complex joins and filters.
Reusability: A single CTE can be referenced multiple times in the main query, avoiding the repetition of the same subquery.
Examples
The Sakila sample database is a good resource for demonstrating real-world applications of CTEs. Here are a few use cases:
1. Simplifying Complex Joins
Suppose you want to find films with the highest rental rates in each rating category. This might involve complex joins and aggregations. Using a CTE, you can simplify it:
WITH HighestRentalRates AS (
SELECT rating, MAX(rental_rate) AS MaxRate
FROM Film
GROUP BY rating
)
SELECT f.film_id, f.title, f.rating, f.rental_rate
FROM Film f INNER JOIN HighestRentalRates h
ON (f.rating = h.rating AND f.rental_rate = h.MaxRate);
Table 7: Displaying records 1 - 10
2 |
ACE GOLDFINGER |
G |
4.99 |
7 |
AIRPLANE SIERRA |
PG-13 |
4.99 |
8 |
AIRPORT POLLOCK |
R |
4.99 |
10 |
ALADDIN CALENDAR |
NC-17 |
4.99 |
13 |
ALI FOREVER |
PG |
4.99 |
20 |
AMELIE HELLFIGHTERS |
R |
4.99 |
21 |
AMERICAN CIRCUS |
R |
4.99 |
28 |
ANTHEM LUKE |
PG-13 |
4.99 |
31 |
APACHE DIVINE |
NC-17 |
4.99 |
32 |
APOCALYPSE FLAMINGOS |
R |
4.99 |
2. Recursive Queries
If the Sakila database had hierarchical data, like a table representing film categories and subcategories, you could use recursive CTEs to traverse this hierarchy. But it’s important to note that the Sakila database does not inherently include hierarchical data like categories and subcategories. For the purpose of this example, I’ll assume such a structure exists or you’re willing to adapt the example to your specific use case.
Assuming we have a table film_category
with a hierarchical structure (which is not actually present in the Sakila database), the query might look like this:
WITH RECURSIVE SubCategory AS (
SELECT category_id, parent_category_id
FROM film_category
WHERE parent_category_id IS NULL
UNION ALL
SELECT fc.category_id, sc.parent_category_id
FROM film_category fc INNER JOIN SubCategory sc
ON sc.parent_category_id = sc.category_id
)
SELECT * FROM SubCategory;
3. Analyzing Patterns
Let’s say you want to analyze rental patterns, like finding customers who rented more than five movies in a month. A CTE can be used to aggregate rental data before joining it with customer data:
WITH MonthlyRentals AS (
SELECT customer_id, strftime('%m', rental_date) AS rental_month, COUNT(*) AS total_rentals
FROM rental
GROUP BY customer_id, strftime('%m', rental_date)
HAVING COUNT(*) > 5
)
SELECT c.customer_id, c.first_name, c.last_name, mr.rental_month, mr.total_rentals
FROM customer c
JOIN MonthlyRentals mr ON c.customer_id = mr.customer_id;
Table 8: Displaying records 1 - 10
1 |
MARY |
SMITH |
06 |
7 |
1 |
MARY |
SMITH |
07 |
12 |
1 |
MARY |
SMITH |
08 |
11 |
2 |
PATRICIA |
JOHNSON |
07 |
14 |
2 |
PATRICIA |
JOHNSON |
08 |
11 |
3 |
LINDA |
WILLIAMS |
07 |
13 |
3 |
LINDA |
WILLIAMS |
08 |
7 |
4 |
BARBARA |
JONES |
06 |
6 |
4 |
BARBARA |
JONES |
08 |
11 |
5 |
ELIZABETH |
BROWN |
07 |
16 |
SQLite has slightly different syntax and capabilities compared to other SQL databases like Oracle’s version used in Sakila.
In SQLite, the EXTRACT
function isn’t available, so we’ll use strftime
to extract the month from the rental_date.
Considerations
In summary, the SQL WITH
statement is a versatile tool in the SQL developer’s arsenal, enabling the creation of more readable, maintainable, and often more efficient SQL queries. The examples using the Sakila film database illustrate how CTEs can be used for simplifying complex queries, working with recursive data, and performing detailed data analysis.
Partitions
The PARTITION BY
clause in SQL is a powerful feature used in conjunction with window functions. It allows you to partition a result set into groups and perform calculations or apply functions to each group (partition) independently. Here’s a detailed explanation:
1. Purpose
Purpose: PARTITION BY
divides the result set into partitions to which the window function is applied. Instead of treating the entire result set as a single group, it enables calculations across a set of rows that share common properties.
Use with Window Functions: It’s used with window functions like ROW_NUMBER()
, RANK()
, SUM()
, AVG()
, etc. These functions perform a calculation across a set of table rows related to the current row.
2. Use Cases
- Data Analysis: For analyzing trends within subgroups of data. For instance, finding the average sales per department in a store.
- Ranking: Assigning ranks within categories, such as ranking students by score within each class.
- Running Totals: Calculating running totals, averages, or other cumulative measures within each partition.
- Segmentation: Segmenting data into groups for more focused analysis, like monthly expenses per department.
3. Benefits
- Improved Data Organization: It enables more granular data analysis without the need for complex subqueries or temporary tables.
- Efficiency: More efficient than self-joins or subqueries for certain types of calculations.
- Flexibility: Offers flexibility in data analysis, allowing for complex calculations across different segments of data.
4. Limitations
- Performance: On large datasets, partitioning can lead to performance issues if not indexed or optimized properly.
- Complexity: The syntax can be complex, especially for beginners or for complex analytical queries.
- Database Support: Not all databases support all window functions, and there can be differences in implementation.
5. Examples
Assuming the same Sakila database and SQLite, let’s consider some examples:
Example 1: Ranking Customers by Amount Spent
SELECT customer_id, amount,
RANK() OVER (PARTITION BY customer_id ORDER BY amount DESC) as rank
FROM payment
LIMIT 5;
Table 9: 5 records
1 |
9.99 |
1 |
1 |
7.99 |
2 |
1 |
5.99 |
3 |
1 |
5.99 |
3 |
1 |
5.99 |
3 |
- Description: This ranks customers based on the amount spent, partitioning by
customer_id
. The LIMIT is arbitrary and only serves to reduce the result set for display in this lesson.
Example 2: Running Total of Payments by Customer
SELECT customer_id, payment_date, amount,
SUM(amount) OVER (PARTITION BY customer_id ORDER BY payment_date) as running_total
FROM payment
Table 10: Displaying records 1 - 10
1 |
2005-05-25 11:30:37 |
2.99 |
2.99 |
1 |
2005-05-28 10:35:23 |
0.99 |
3.98 |
1 |
2005-06-15 00:54:12 |
5.99 |
9.97 |
1 |
2005-06-15 18:02:53 |
0.99 |
10.96 |
1 |
2005-06-15 21:08:46 |
9.99 |
20.95 |
1 |
2005-06-16 15:18:57 |
4.99 |
25.94 |
1 |
2005-06-18 08:41:48 |
4.99 |
30.93 |
1 |
2005-06-18 13:33:59 |
0.99 |
31.92 |
1 |
2005-06-21 06:24:45 |
3.99 |
35.91 |
1 |
2005-07-08 03:17:05 |
5.99 |
41.90 |
- Description: Calculates a running total of payments for each customer over time.
Conclusion
PARTITION BY
is a versatile tool in SQL that enhances data analysis capabilities by enabling segment-specific calculations. While it has limitations, particularly in performance and complexity, its benefits in organizing and processing data for analytical queries are significant.
PARTION BY vs GROUP BY
PARTITION BY
and GROUP BY
are both SQL clauses used to organize data into subsets, but they serve different purposes and are used in different contexts.
PARTITION BY
- Context: Used with window functions.
- Functionality: It organizes data into partitions, allowing you to perform calculations across each partition. Each partition can be considered a ‘window’ over which SQL functions operate.
- Row Retention: It does not change the number of rows in the query’s result. Each row in the original dataset remains in the output, along with any additional columns created by the window function.
- Use Cases: Commonly used for ranking, calculating running totals, moving averages, and other complex analytics that require access to multiple rows of the dataset at once, without grouping them into a single output row.
Example of PARTITION BY
SELECT id, department, salary,
AVG(salary) OVER (PARTITION BY department) as avg_department_salary
FROM employees;
Here, PARTITION BY
calculates the average salary per department, but every row from the original employees
table is retained in the output.
GROUP BY
- Context: Used in aggregate queries.
- Functionality: It groups rows that have the same values in specified columns into summary rows, like “find the total salary per department”.
- Row Reduction: It reduces the number of rows in the result, providing one row per group. You can only select the grouped columns and the results of aggregate functions.
- Use Cases: Ideal for summaries, such as totals, averages, counts, min, max, etc., where you need a single result row per group.
Example of GROUP BY
SELECT department, AVG(salary) as avg_department_salary
FROM employees
GROUP BY department;
Here, GROUP BY
provides the average salary per department, but the output will only have one row per department.
Key Differences
- Row Count:
PARTITION BY
retains the original row count, whereas GROUP BY
consolidates rows into groups, reducing the row count.
- Use with Functions:
PARTITION BY
is used with window functions, while GROUP BY
is used with aggregate functions.
- Result Set:
PARTITION BY
adds additional information to each row, whereas GROUP BY
provides a summarized result set.
- Scope of Calculation: In
PARTITION BY
, calculations are done within each partition and don’t affect other partitions. In GROUP BY
, calculations are done on grouped data, producing a single result for each group.
Examples
Let’s use the Sakila database with a focus on a scenario where we might want to analyze the film rental data. We will compare the usage of PARTITION BY
and GROUP BY
in SQL queries using this context. Note that the structure of the Sakila database might differ slightly based on the adaptation for SQLite, but the general concept remains the same.
Scenario: Analyzing Film Rentals
We want to analyze film rentals, looking at both the total number of rentals per category and the rank of each film within its category based on the number of times it has been rented.
1. Using GROUP BY
Objective: To find the total number of rentals for each film category.
SQL Query:
SELECT category.name AS category_name, COUNT(rental.rental_id) AS total_rentals
FROM category
JOIN film_category ON category.category_id = film_category.category_id
JOIN film ON film_category.film_id = film.film_id
JOIN inventory ON film.film_id = inventory.film_id
JOIN rental ON inventory.inventory_id = rental.inventory_id
GROUP BY category.name;
Table 11: Displaying records 1 - 10
Action |
1112 |
Animation |
1166 |
Children |
945 |
Classics |
939 |
Comedy |
941 |
Documentary |
1050 |
Drama |
1060 |
Family |
1096 |
Foreign |
1033 |
Games |
969 |
Explanation: - This query groups films by their category and counts the total number of rentals for each category. - The result will be a summary table with one row per category, showing the total rentals.
2. Using PARTITION BY
Objective: To rank each film within its category based on the number of times it has been rented.
SQL Query:
SELECT film.title, category.name AS category_name,
RANK() OVER (PARTITION BY category.name ORDER BY COUNT(rental.rental_id) DESC) AS rental_rank
FROM category
JOIN film_category ON category.category_id = film_category.category_id
JOIN film ON film_category.film_id = film.film_id
JOIN inventory ON film.film_id = inventory.film_id
JOIN rental ON inventory.inventory_id = rental.inventory_id
GROUP BY film.title, category.name;
Table 12: Displaying records 1 - 10
RUGRATS SHAKESPEARE |
Action |
1 |
SUSPECTS QUILLS |
Action |
1 |
HANDICAP BOONDOCK |
Action |
3 |
STORY SIDE |
Action |
3 |
TRIP NEWTON |
Action |
3 |
PRIMARY GLASS |
Action |
6 |
FANTASY TROOPERS |
Action |
7 |
STAGECOACH ARMAGEDDON |
Action |
7 |
CLUELESS BUCKET |
Action |
9 |
HILLS NEIGHBORS |
Action |
9 |
Explanation: - This query ranks films within each category based on their rental count. - The PARTITION BY
clause is used to create partitions for each film category, and within each partition, films are ranked by their rental counts. - Unlike GROUP BY
, this query does not reduce the number of rows in the output. Each film retains its row, but with additional information (rank) added.
Key Differences Illustrated:
- Aggregation vs. Windowing: The
GROUP BY
query aggregates data, reducing the result to one row per category, while the PARTITION BY
query applies a window function within each category, retaining individual film rows.
- Output:
GROUP BY
produces a summary table, whereas PARTITION BY
adds additional ranking information to each film within its category.
These examples highlight how GROUP BY
is used for aggregating data into summary form, whereas PARTITION BY
is used for performing calculations within subsets of data while retaining the original data structure.
Summary
This lesson covered the use of subqueries and the WITH
statement in SQL for formulating complex queries, and the use of PARTITION BY
to apply window functions to groups of data, using the Sakila Media and Film database for examples.
Subqueries
- Definition: Subqueries are SQL queries embedded within another SQL query, used in
SELECT
, FROM
, WHERE
, and HAVING
clauses.
- Benefits: They enhance data retrieval in complex scenarios, handle complex data relationships, and implement nested logic.
- Characteristics: Subqueries can return various types of results and can be independent or correlated with the main query.
- Advantages and Limitations: They offer modularity and improved readability but can cause performance issues and add complexity.
- Types of Subqueries: These include single-row, multiple-row, correlated, subqueries as derived tables, and existential subqueries, each with specific use cases and examples.
Common Table Expressions (CTEs)
- Basics: The
WITH
statement creates temporary result sets for use in SQL queries.
- Advantages: CTEs enhance readability, allow for recursive queries, and can improve performance.
- Use Cases in Sakila Database: Examples are provided, like finding films with the highest rental rates and analyzing rental patterns, although it’s noted that the Sakila database doesn’t inherently include hierarchical data for some of the more complex examples.
Partitions
The purpose of partitions and the PARTITION BY
clause is to apply window functions to partitions of data. The is often necessary for data analysis and ranking and provides improved data organization, but its use can lead to performance issues on large datasets.
Difference between PARTITION BY
and GROUP BY
: The distinction between these two clauses was clarified. PARTITION BY
, used with window functions, retains the original row count and allows calculations within partitions. Conversely, GROUP BY
, used for aggregate queries, reduces the number of rows to one per group and is ideal for summaries.
Tutorial
In this advanced tutorial video, James Oliver explains unions, partitions, recursion, and hierarchies for constructing complex and efficient SQL queries.
LS0tCnRpdGxlOiAiQWR2YW5jZWQgU1FMOiBTdWJxdWVyaWVzLCBQYXJpdGlvbnMsIGFuZCBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMiCnBhcmFtczoKICBjYXRlZ29yeTogNzAKICBudW1iZXI6IDEwNwogIHRpbWU6IDQ1CiAgbGV2ZWw6IGJlZ2lubmVyCiAgdGFnczogInNxbCxTRUxFQ1QsQ1RFLFdJVEgsUEFSVElUSU9OIEJZLCBHUk9VUCBCWSxOVElMRSIKICBkZXNjcmlwdGlvbjogIkV4cGxhaW5zIGhvdyB0byBkZWZpbmUgY29tcGxleCBxdWVyaWVzIGluIFNRTCB1c2luZyBzdWJxdWVyaWVzLAogICAgICAgICAgICAgICAgYXMgd2VsbCBhcyBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMgKENURSkgdXNpbmcgV0lUSCwKICAgICAgICAgICAgICAgIGFuZCBob3cgdG8gYXBwbHkgd2luZG93IGZ1bmN0aW9ucyB0byBkYXRhIHBhcnRpdGlvbnMgdXNpbmcKICAgICAgICAgICAgICAgIFBBUlRJVElPbiBCWS4gSWxsdXN0cmF0ZXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBHUk9VUCBCWQogICAgICAgICAgICAgICAgYW5kIFBBUlRJVElPTiBCWS4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKU3VicXVlcmllcyBhbmQgdGhlIFdJVEggc3RhdGVtZW50IGFyZSB1c2VmdWwgcXVlcnkgZm9ybXVsYXRpb24gbWVjaGFuaXNtcyBmb3Igd3JpdGluZyBjb21wbGV4IHF1ZXJpZXMuIFRoaXMgbGVzc29uIGRlbW9uc3RyYXRlcywgdXNpbmcgZXhhbXBsZXMsIGhvdyB0byB1c2UgYm90aCBpbiBxdWVyaWVzIGFuZCBob3cgdG8gc2ltcGxpZnkgZGF0YWJhc2UgYWNjZXNzIHVzaW5nIHRoZSBjb25zdHJ1Y3QuCgojIyBTYW1wbGUgRGF0YWJhc2UKCkZvciBtYW55IG9mIHRoZSBleGFtcGxlcyB3ZSB3aWxsIHVzZSB0aGUgU2FraWxhIE1lZGlhIGFuZCBGaWxtIGRhdGFiYXNlLiBBbiBFUkQgb2YgdGhlIGRhdGFiYXNlIGlzIHNob3duIGJlbG93LiBUaGUgZXhhbXBsZXMgYXJlIGV4ZWN1dGVkIG9uIFNRTGl0ZSB1c2luZyBbc2FraWxhZGIuc3FsaXRlZGJdKHNha2lsYWRiLnNxbGl0ZWRiKVteMV0uCgpbXjFdOiBTYWtpbGEgRGF0YWJhc2UgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE5ldyBCU0QgTGljZW5zZSBhbmQgbWF5IGJlIHVzZWQgZm9yIGFueSBwdXJwb3NlIHdpdGggdGhlIHByb3Zpc2lvbiB0aGF0IG5vdGljZSBvZiDCqSAyMDIwIE9yYWNsZSBDb3Jwb3JhdGlvbiBhcHBlYXJzLgoKIVtdKFNha2lsYURCLnBuZyl7d2lkdGg9IjcwJSJ9CgpgYGB7ciBvcGVuU2FraWxhREIsIGV2YWw9VCwgZWNobz1GfQpsaWJyYXJ5KFJTUUxpdGUpCmRiY29uIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgInNha2lsYS5zcWxpdGVkYiIpCmBgYAoKIyMgU3VicXVlcmllcwoKQSBzdWJxdWVyeSwgYWxzbyBrbm93biBhcyBhIG5lc3RlZCBxdWVyeSBvciBpbm5lciBxdWVyeSwgaXMgYW4gU1FMIHF1ZXJ5IHRoYXQgaXMgZW1iZWRkZWQgd2l0aGluIGFub3RoZXIgU1FMIHF1ZXJ5LiBJdCBjYW4gYmUgdXNlZCBpbiB2YXJpb3VzIHBhcnRzIG9mIGEgcXVlcnksIGluY2x1ZGluZyBgU0VMRUNUYCwgYEZST01gLCBgV0hFUkVgLCBhbmQgYEhBVklOR2AgY2xhdXNlcy4gU3VicXVlcmllcyBhcmUgcHJpbWFyaWx5IHVzZWQgZm9yIHBlcmZvcm1pbmcgb3BlcmF0aW9ucyB0aGF0IHJlcXVpcmUgbXVsdGlwbGUgc3RlcHMgaW4gZGF0YSBwcm9jZXNzaW5nLiBUaGV5IGFsbG93IGZvciBtb3JlIGZsZXhpYmxlIGFuZCBlZmZpY2llbnQgZGF0YSByZXRyaWV2YWwsIGVuYWJsaW5nIGNvbXBsZXggb3BlcmF0aW9ucyB0byBiZSBleGVjdXRlZCBpbiBhIHNpbmdsZSBxdWVyeS4KCiMjIyBCZW5lZml0cyBvZiBTdWJxdWVyaWVzCgpTdWJxdWVyaWVzIG9mZmVyIHNldmVyYWwgYmVuZWZpdHMsIGluY2x1ZGluZzoKCi0gICAqKkVuaGFuY2luZyBEYXRhIFJldHJpZXZhbCoqOiBTdWJxdWVyaWVzIHByb3ZpZGUgYSBwb3dlcmZ1bCB0b29sIGZvciByZXRyaWV2aW5nIGRhdGEgaW4gY29tcGxleCBzaXR1YXRpb25zIHdoZXJlIHRoZSB1c2Ugb2Ygc3RhbmRhcmQgU1FMIHF1ZXJpZXMgbWlnaHQgYmUgbGltaXRpbmcgb3IgaW5lZmZpY2llbnQuCi0gICAqKkNvbXBsZXggRGF0YSBSZWxhdGlvbnNoaXBzKio6IFRoZXkgYXJlIHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gaGFuZGxpbmcgc2NlbmFyaW9zIHdoZXJlIGRhdGEgcmVsYXRpb25zaGlwcyBhcmUgY29tcGxleCBhbmQgcmVxdWlyZSBtdWx0aXBsZSBsYXllcnMgb2YgZmlsdGVyaW5nIG9yIHNlbGVjdGlvbi4KLSAgICoqTmVzdGVkIExvZ2ljKio6IFN1YnF1ZXJpZXMgb2ZmZXIgYSB3YXkgdG8gaW1wbGVtZW50IG5lc3RlZCBsb2dpYyBpbiBTUUwgcXVlcmllcywgYWxsb3dpbmcgZm9yIHNvcGhpc3RpY2F0ZWQgZGF0YSBhbmFseXNpcyBhbmQgbWFuaXB1bGF0aW9uLgoKIyMjIENoYXJhY3RlcmlzdGljcyBvZiBTdWJxdWVyaWVzCgotICAgKipSZXR1cm4gVHlwZXMqKjogU3VicXVlcmllcyBjYW4gcmV0dXJuIGluZGl2aWR1YWwgdmFsdWVzLCBhIHNpbmdsZSByb3csIG11bHRpcGxlIHJvd3MsIG9yIGV2ZW4gYSB0YWJsZS1saWtlIHN0cnVjdHVyZS4KLSAgICoqSW5kZXBlbmRlbnQgdnMuIENvcnJlbGF0ZWQqKjogVGhleSBjYW4gYmUgaW5kZXBlbmRlbnQgb2YgdGhlIG91dGVyIHF1ZXJ5IChzdGFuZGFsb25lKSBvciBjb3JyZWxhdGVkLCB3aGVyZSB0aGUgc3VicXVlcnkgZGVwZW5kcyBvbiBkYXRhIGZyb20gdGhlIG91dGVyIHF1ZXJ5LgotICAgKipQbGFjZW1lbnQgRmxleGliaWxpdHkqKjogU3VicXVlcmllcyBjYW4gYmUgdXNlZCBpbiB2YXJpb3VzIHBsYWNlcyB3aXRoaW4gYSBxdWVyeSwgc3VjaCBhczoKICAgIC0gICBJbiB0aGUgYFNFTEVDVGAgY2xhdXNlLCB0byBhZGQgY29sdW1ucyB0byB0aGUgbWFpbiBxdWVyeS4KICAgIC0gICBJbiB0aGUgYEZST01gIGNsYXVzZSwgdG8gY3JlYXRlIGEgZGVyaXZlZCB0YWJsZSB0aGF0IHRoZSBtYWluIHF1ZXJ5IGNhbiB1c2UuCiAgICAtICAgSW4gdGhlIGBXSEVSRWAgb3IgYEhBVklOR2AgY2xhdXNlcywgdG8gZmlsdGVyIHJlY29yZHMgYmFzZWQgb24gY29tcGxleCBjb25kaXRpb25zLgoKIyMjIEFkdmFudGFnZXMgYW5kIExpbWl0YXRpb25zCgotICAgKipBZHZhbnRhZ2VzKio6CiAgICAtICAgKipNb2R1bGFyaXR5Kio6IEJyZWFraW5nIGRvd24gY29tcGxleCBxdWVyaWVzIGludG8gc2ltcGxlciwgbW9yZSBtYW5hZ2VhYmxlIHBhcnRzLgogICAgLSAgICoqUmVhZGFiaWxpdHkqKjogSW1wcm92aW5nIHRoZSByZWFkYWJpbGl0eSBvZiBTUUwgc2NyaXB0cyBieSBlbmNhcHN1bGF0aW5nIHNwZWNpZmljIGxvZ2ljLgogICAgLSAgICoqUmV1c2FiaWxpdHkqKjogRmFjaWxpdGF0aW5nIHRoZSByZXVzZSBvZiBjb2RlIGFuZCBsb2dpYyB3aXRoaW4gdGhlIGRhdGFiYXNlLgotICAgKipMaW1pdGF0aW9ucyoqOgogICAgLSAgICoqUGVyZm9ybWFuY2UgQ29uY2VybnMqKjogTmVzdGVkIHF1ZXJpZXMgY2FuIHNvbWV0aW1lcyBsZWFkIHRvIHBlcmZvcm1hbmNlIGlzc3VlcywgcGFydGljdWxhcmx5IGlmIG5vdCBvcHRpbWl6ZWQgY29ycmVjdGx5LgogICAgLSAgICoqQ29tcGxleGl0eSoqOiBPdmVydXNlIG9yIG1pc3VzZSBvZiBzdWJxdWVyaWVzIGNhbiBtYWtlIFNRTCBzdGF0ZW1lbnRzIG1vcmUgY29tcGxleCBhbmQgaGFyZGVyIHRvIG1haW50YWluLgoKIyMjIFNpbXBsZSBFeGFtcGxlCgoqSG93IG1hbnkgcGF5bWVudHMgaGF2ZSBhIGJlbG93IGF2ZXJhZ2UgYW1vdW50PyoKClRoaXMgcXVlcnkgcmVxdWlyZXMgdXMgdG8ga25vdyB0aGUgYXZlcmFnZSBwYXltZW50IGFtb3VudCwgYnV0IHRoYXQgYW1vdW50IHZhcmllcyB3aXRoIHRoZSBkYXRhLCBzbyBpdCBjYW5ub3QgYmUgaGFyZCBjb2RlZDsgYSBzdWJxdWVyeSB0aGF0IGNhbGN1bGF0ZXMgdGhlIGFjY291bnQgYW5kIHVzZWQgd2hlcmUgdGhlIGF2ZXJhZ2UgaXMgbmVlZGVkIHJlc29sdmVzIHRoZSBpc3N1ZS4gT2YgY291cnNlLCBzaW5jZSBhdmVyYWdlIGlzIGEgc2NhbGFyIHZhbHVlLCB0aGUgc3VicXVlcnkgbXVzdCByZXR1cm4gb25lIHZhbHVlLCBidXQgYmVjYXVzZSB0aGUgcmVzdWx0IG11c3QgYmUgYSB0YWJsZSwgdGhlIHN1YnF1ZXJ5IGFjdHVhbGx5IHJldHVybnMgYSB0YWJsZSB3aXRoIG9uZSByb3cgYW5kIG9uZSBjb2x1bW4uCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259ClNFTEVDVCBDT1VOVChwYXltZW50X2lkKSBBUyBudW1HcnRBdmcKRlJPTSBQYXltZW50CldIRVJFIGFtb3VudCA8IChTRUxFQ1QgQVZHKGFtb3VudCkgRlJPTSBQYXltZW50KTsKYGBgCgpUaGlzIHNlY3Rpb24gbGF5cyB0aGUgZm91bmRhdGlvbiBmb3IgdW5kZXJzdGFuZGluZyBzdWJxdWVyaWVzLCBzZXR0aW5nIHRoZSBzdGFnZSBmb3IgbW9yZSBkZXRhaWxlZCBleHBsb3JhdGlvbiBpbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zIG9mIHRoZSBsZXNzb24uCgpFeHBhbmRpbmcgb24gdGhlIHR5cGVzIG9mIHN1YnF1ZXJpZXMgaW4gU1FMOgoKIyMgVHlwZXMgb2YgU3VicXVlcmllcwoKIyMjIFNpbmdsZS1Sb3cgU3VicXVlcmllcwoKKipEZWZpbml0aW9uKio6IFRoZXNlIHJldHVybiBhIHNpbmdsZSByb3cgZnJvbSB0aGUgaW5uZXIgcXVlcnkuIFRoZXkncmUgdHlwaWNhbGx5IHVzZWQgd2l0aCBzaW5nbGUtcm93IGNvbXBhcmlzb24gb3BlcmF0b3JzIGxpa2UgYD1gLCBgPmAsIGA8YCwgYDw9YCwgYD49YCwgYW5kIGA8PmAuCgoqKlVzZSBDYXNlcyoqOiBJZGVhbCBmb3Igc2l0dWF0aW9ucyB3aGVyZSB5b3UgZXhwZWN0IHRoZSBzdWJxdWVyeSB0byByZXR1cm4gZXhhY3RseSBvbmUgcm93LiBGb3IgaW5zdGFuY2UsIGZpbmRpbmcgYW4gZW1wbG95ZWUgd2hvc2Ugc2FsYXJ5IGlzIGVxdWFsIHRvIHRoZSBtYXhpbXVtIHNhbGFyeSBpbiBhIGRlcGFydG1lbnQgb3IgdGhlIHZhbHVlIG9mIGEgY29sdW1uIGlzIGFib3ZlIG9yIGJlbG93IGF2ZXJhZ2UuCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIHBheW1lbnRfaWQsIGFtb3VudCAKICBGUk9NIFBheW1lbnQgCiBXSEVSRSBhbW91bnQgPSAoU0VMRUNUIE1BWChhbW91bnQpIEZST00gUGF5bWVudCkKYGBgCgpUaGUgZXhhbXBsZSBhYm92ZSBmaW5kcyB0aG9zZSBwYXltZW50cyB0aGF0IGFyZSBmb3IgdGhlIGJpZ2dlc3QgKG1heGltdW0pIGFtb3VudC4KCiMjIyMgRXhlcmNpc2Ugey50YWJzZXR9CgpUcnkgdGhlIHF1ZXJ5IGJlbG93IGZvciB5b3Vyc2VsZiBvbiB0aGUgc2FtcGxlIGRhdGFiYXNlLgoKIyMjIyMgUXVlcnkgey5hY3RpdmV9CgoqV2hpY2ggY3VzdG9tZXIgKGxhc3QgbmFtZSwgZmlyc3QgbmFtZSwgYW5kIElEKSBwdXJjaGFzZWQgdGhlIG1vc3QgaW4gdGVybXMgb2YgdG90YWwgYW1vdW50PyoKCiMjIyMjIFNvbHV0aW9uCgpUQkQKCiMjIyBNdWx0aXBsZS1Sb3cgU3VicXVlcmllcwoKKipEZWZpbml0aW9uKio6IFRoZXNlIHJldHVybiBtdWx0aXBsZSByb3dzIGFuZCBhcmUgdXNlZCB3aXRoIGV4aXN0ZW50aWFsIG9yIHNldCBtZW1iZXJzaGlwIG9wZXJhdG9ycyBsaWtlIGBJTmAsIGBBTllgLCBgQUxMYCwgb3IgY29tcGFyaXNvbnMgY29tYmluZWQgd2l0aCBgQU5ZYCBvciBgQUxMYC4KCioqVXNlIENhc2VzKio6IFVzZWZ1bCB3aGVuIHRoZSBzdWJxdWVyeSBpcyBleHBlY3RlZCB0byByZXR1cm4gbW9yZSB0aGFuIG9uZSByb3cuIEZvciBleGFtcGxlLCByZXRyaWV2aW5nIGFsbCBvcmRlcnMgdGhhdCBtYXRjaCBhbnkgb2YgdGhlIG9yZGVyIElEcyBpbiBhIGxpc3QuCgoqKkFsdGVybmF0aXZlKio6IEEgc2ltcGxlIGlubmVyLWpvaW4gb3IgYSB0aGV0YS1qb2luIGNhbiBvZnRlbiBiZSB1c2VkIGluc3RlYWQuCgoqKkV4YW1wbGUqKjoKClRoZSBxdWVyeSBiZWxvdyBmaW5kcyBob3cgbWFueSBjaXRpZXMgYXJlIGxvY2F0ZWQgaW4gYSBjb3VudHJ5IHRoYXQgc3RhcnRzIHdpdGggYSAnQicuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259ClNFTEVDVCBjb3VudCgqKSBBUyBgTnVtQ2l0aWVzYAogIEZST00gQ2l0eSAKIFdIRVJFIGNvdW50cnlfaWQgSU4gCiAgICAoU0VMRUNUIGNvdW50cnlfaWQgCiAgICAgICBGUk9NIENvdW50cnkgCiAgICAgIFdIRVJFIGNvdW50cnkgTElLRSAnQiUnKQpgYGAKCiMjIyBDb3JyZWxhdGVkIFN1YnF1ZXJpZXMKCioqRGVmaW5pdGlvbioqOiBBIGNvcnJlbGF0ZWQgc3VicXVlcnkgaXMgb25lIHRoYXQgdXNlcyB2YWx1ZXMgZnJvbSB0aGUgb3V0ZXIgcXVlcnkuIEl0IGlzIGV4ZWN1dGVkIHJlcGVhdGVkbHksICpvbmNlIGZvciBlYWNoIHJvdyB0aGF0IG1pZ2h0IGJlIHNlbGVjdGVkIGJ5IHRoZSBvdXRlciBxdWVyeSouCgoqKlVzZSBDYXNlcyoqOiBFZmZlY3RpdmUgaW4gc2NlbmFyaW9zIHdoZXJlIGVhY2ggcm93IGluIHRoZSBvdXRlciBxdWVyeSBtYXkgbmVlZCBkaWZmZXJlbnQgcmVzdWx0cyBmcm9tIHRoZSBzdWJxdWVyeS4gRm9yIGluc3RhbmNlLCBmaW5kaW5nIGVtcGxveWVlcyB3aG8gZWFybiBtb3JlIHRoYW4gdGhlIGF2ZXJhZ2Ugc2FsYXJ5IGluIHRoZWlyIHJlc3BlY3RpdmUgZGVwYXJ0bWVudHMuCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIHN0YWZmX2lkLCBhbW91bnQKICBGUk9NIFBheW1lbnQgQVMgUAogV0hFUkUgYW1vdW50ID4gKFNFTEVDVCBBVkcoYW1vdW50KQogICAgICAgICAgICAgICAgICAgRlJPTSBQYXltZW50CiAgICAgICAgICAgICAgICAgIFdIRVJFIHN0YWZmX2lkID0gUC5zdGFmZl9pZCkKYGBgCgoqKlBlcmZvcm1hbmNlIE5vdGUqKjogQ29ycmVsYXRlZCBzdWJxdWVyaWVzIGNhbiBiZSBsZXNzIGVmZmljaWVudCBiZWNhdXNlIHRoZSBzdWJxdWVyeSBtYXkgbmVlZCB0byBiZSBleGVjdXRlZCBtdWx0aXBsZSB0aW1lcy4gQSBqb2luIHdpdGggYSAiZ3JvdXAgYnkiIGlzIG9mdGVuIHNpbXBsZXIgYW5kIHByZWZlcmFibGUgYXMgaXQgZXhlY3V0ZXMgZ2VuZXJhbGx5IG11Y2ggZmFzdGVyIGFuZCB0aGUgZGF0YWJhc2UgY2FuIHVzZSBpbmRleGVzLgoKIyMjIFN1YnF1ZXJ5IGFzIGEgRGVyaXZlZCBUYWJsZQoKKipEZWZpbml0aW9uKio6IFRoaXMgaW52b2x2ZXMgdXNpbmcgYSBzdWJxdWVyeSBpbiB0aGUgYEZST01gIGNsYXVzZSBvZiB0aGUgbWFpbiBxdWVyeS4gVGhlIHJlc3VsdCBvZiB0aGUgc3VicXVlcnkgYWN0cyBhcyBhIHRlbXBvcmFyeSB0YWJsZS4KCioqVXNlIENhc2VzKio6IFVzZWZ1bCBmb3IgY3JlYXRpbmcgY29tcGxleCBqb2lucyBhbmQgYWdncmVnYXRpb25zLiBGb3IgZXhhbXBsZSwgc3VtbWFyaXppbmcgZGF0YSBpbiBhIHN1YnF1ZXJ5IGJlZm9yZSBqb2luaW5nIGl0IHRvIGFub3RoZXIgdGFibGUuCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIEMuY3VzdG9tZXJfaWQsIEMubGFzdF9uYW1lLCBQLnRvdGFsX2Ftb3VudAogIEZST00gQ3VzdG9tZXIgQVMgQyBKT0lOIChTRUxFQ1QgY3VzdG9tZXJfaWQsIHN1bShhbW91bnQpIEFTIGB0b3RhbF9hbW91bnRgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIFBheW1lbnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgY3VzdG9tZXJfaWQpIEFTIFAKICAgICAgICAgICAgICAgICAgICAgT04gKEMuY3VzdG9tZXJfaWQgPSBQLmN1c3RvbWVyX2lkKQpgYGAKCk5vdGUgaG93IHRoZSBzdWJxdWVyeSB3YXMgcHJvdmlkZWQgd2l0aCBhbiBhbGlhcyB1c2luZyAqQVMqLgoKIyMjIEV4aXN0ZW50aWFsIFN1YnF1ZXJpZXMKCioqRGVmaW5pdGlvbioqOiBUaGVzZSBhcmUgdXNlZCB0byB0ZXN0IGZvciB0aGUgZXhpc3RlbmNlIG9mIHJvd3MgaW4gYSBzdWJxdWVyeS4KCioqVXNlIENhc2VzKio6IENvbW1vbmx5IHVzZWQgaW4gY29uZGl0aW9uYWwgbG9naWMsIGxpa2UgY2hlY2tpbmcgaWYgYSBjZXJ0YWluIGNvbmRpdGlvbiBpcyBtZXQgaW4gYW5vdGhlciB0YWJsZSBiZWZvcmUgcGVyZm9ybWluZyBhbiBvcGVyYXRpb24uCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIERJU1RJTkNUIEEubGFzdF9uYW1lLCBBLmZpcnN0X25hbWUKICBGUk9NIEZpbG1fQWN0b3IgQVMgRkEgSk9JTiBBY3RvciBBUyBBIFVTSU5HIChhY3Rvcl9pZCkKIFdIRVJFIEVYSVNUUwogICAgICAgICAoU0VMRUNUICogRlJPTSBGaWxtIAogICAgICAgICAgIFdIRVJFIGZpbG1faWQgPSBGQS5maWxtX2lkIEFORCBsZW5ndGggPiAxODApCiBMSU1JVCAzOwpgYGAKCiMjIyBDb25zaWRlcmF0aW9ucwoKKipVbm5lY2Vzc2FyeSBDb21wbGV4aXR5Kio6IEJlIGNhcmVmdWwgbm90IHRvIG92ZXJ1c2Ugc3VicXVlcmllcywgYXMgdGhleSBjYW4gbWFrZSB0aGUgcXVlcnkgY29tcGxleCBhbmQgZGlmZmljdWx0IHRvIHVuZGVyc3RhbmQuCgoqKlN1YnF1ZXJ5IE9wdGltaXphdGlvbioqOiBJdCdzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIGhvdyBzdWJxdWVyaWVzIGNhbiBpbXBhY3QgcGVyZm9ybWFuY2UgYW5kIHRvIG9wdGltaXplIHRoZW0gYWNjb3JkaW5nbHkuIEZvciBleGFtcGxlLCBtaW5pbWl6aW5nIHRoZSBudW1iZXIgb2Ygcm93cyBwcm9jZXNzZWQgYnkgdXNpbmcgYXBwcm9wcmlhdGUgV0hFUkUgY2xhdXNlcy4KCioqTmVzdGVkIFN1YnF1ZXJpZXMqKjogU3VicXVlcmllcyBjYW4gYmUgbmVzdGVkIGluc2lkZSBvdGhlciBzdWJxdWVyaWVzLCBidXQgdGhpcyBzaG91bGQgYmUgZG9uZSBqdWRpY2lvdXNseSB0byBhdm9pZCBleGNlc3NpdmUgY29tcGxleGl0eSBhbmQgcG90ZW50aWFsIHBlcmZvcm1hbmNlIGlzc3Vlcy4gV2hpbGUgb2Z0ZW4gc2ltcGxlciBmb3Igc29tZSBwcm9ncmFtbWVycyB0byBleHByZXNzIGEgcXVlcnksIGpvaW5zIGFuZCBncm91cCBieSBhcmUgcHJlZmVyYWJsZSB3aGVuIGFuIG9wdGlvbi4KCioqTGluZWFyIFNjYW5zKio6IFN1YnF1ZXJpZXMgb2Z0ZW4gcmVzdWx0IGluIHF1ZXJ5IHBsYW5zIHRoYXQgdXNlIGxpbmVhciB0YWJsZSBzY2FucyByYXRoZXIgdGhhbiBhdmFpbGFibGUgaW5kZXhlcyBhbmQgYXJlIHRodXMgb2Z0ZW4gdmFzdGx5IGxlc3MgZWZmaWNpZW50LgoKKipJbmRlcGVuZGVudCBUZXN0aW5nKio6IFN1YnF1ZXJpZXMgc2hvdWxkIGJlIHRlc3RlZCBpbmRlcGVuZGVudGx5IHRvIGVuc3VyZSB0aGF0IHRoZSBjb3JyZWN0IHJlc3VsdHMgYXJlIHJldHVybmVkIGJlZm9yZSBpbmNvcnBvcmF0aW5nIHRoZW0gaW50byBvdGhlciBxdWVyaWVzLgoKRWFjaCB0eXBlIG9mIHN1YnF1ZXJ5IG9mZmVycyB1bmlxdWUgZnVuY3Rpb25hbGl0aWVzIGFuZCBpcyBzdWl0ZWQgZm9yIGRpZmZlcmVudCBzY2VuYXJpb3MgaW4gU1FMIHF1ZXJ5aW5nLiBVbmRlcnN0YW5kaW5nIHRoZXNlIHR5cGVzIHdpbGwgaGVscCBkYXRhIGFuYWx5c3RzIGVmZmVjdGl2ZWx5IHVzZSBzdWJxdWVyaWVzIHRvIHNvbHZlIGNvbXBsZXggZGF0YSByZXRyaWV2YWwgcHJvYmxlbXMuCgojIyBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMKClRoZSBTUUwgYFdJVEhgIHN0YXRlbWVudCwgYWxzbyBrbm93biBhcyBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMgKENURXMpLCBpcyBhIHVzZWZ1bCBmZWF0dXJlIGZvciBjcmVhdGluZyB0ZW1wb3JhcnkgcmVzdWx0IHNldHMgdGhhdCBjYW4gYmUgcmVmZXJlbmNlZCB3aXRoaW4gYSBgU0VMRUNUYCwgYElOU0VSVGAsIGBVUERBVEVgLCBvciBgREVMRVRFYCBzdGF0ZW1lbnQuIENURXMgYXJlIHNpbWlsYXIgdG8gdGhlIM+BIChyZW5hbWUpIG9wZXJhdG9yIGluIHJlbGF0aW9uYWwgYWxnZWJyYS4KCiMjIyBCYXNpYyBTeW50YXgKClRoZSBiYXNpYyBzeW50YXggb2YgdGhlIGBXSVRIYCBzdGF0ZW1lbnQgaXMgYXMgZm9sbG93czoKCmBgYCBzcWwKV0lUSCBDVEVfTmFtZSBBUyAoCiAgICAtLSBZb3VyIFNRTCBxdWVyeSBoZXJlCikKClNFTEVDVCAqIEZST00gQ1RFX05hbWU7CmBgYAoKSGVyZSwgYENURV9OYW1lYCBpcyBhIHRlbXBvcmFyeSByZXN1bHQgc2V0IHRoYXQgeW91IGRlZmluZSB3aXRoaW4gdGhlIGBXSVRIYCBjbGF1c2UuIFRoaXMgcmVzdWx0IHNldCBjYW4gdGhlbiBiZSB1c2VkIGluIHRoZSBtYWluIFNRTCBxdWVyeSB0aGF0IGZvbGxvd3MgaW4gcGxhY2Ugb2YgYW55IHRhYmxlLgoKIyMjIEFkdmFudGFnZXMgb2YgVXNpbmcgYFdJVEhgCgoxLiAgKipSZWFkYWJpbGl0eSBhbmQgTWFpbnRlbmFuY2UqKjogQ1RFcyBjYW4gbWFrZSBjb21wbGV4IHF1ZXJpZXMgbW9yZSByZWFkYWJsZSBhbmQgbWFpbnRhaW5hYmxlIGJ5IGJyZWFraW5nIHRoZW0gaW50byBzaW1wbGVyLCBtb2R1bGFyIHBhcnRzLgoKMi4gICoqUmVjdXJzaXZlIFF1ZXJpZXMqKjogQ1RFcyBlbmFibGUgcmVjdXJzaXZlIHF1ZXJpZXMsIHdoaWNoIGFyZSB1c2VmdWwgZm9yIGhpZXJhcmNoaWNhbCBkYXRhIHByb2Nlc3NpbmcsIGxpa2UgdHJlZSBzdHJ1Y3R1cmVzLgoKMy4gICoqUGVyZm9ybWFuY2UgT3B0aW1pemF0aW9uKio6IEluIHNvbWUgY2FzZXMsIENURXMgY2FuIGltcHJvdmUgcGVyZm9ybWFuY2UgYnkgc2ltcGxpZnlpbmcgY29tcGxleCBqb2lucyBhbmQgZmlsdGVycy4KCjQuICAqKlJldXNhYmlsaXR5Kio6IEEgc2luZ2xlIENURSBjYW4gYmUgcmVmZXJlbmNlZCBtdWx0aXBsZSB0aW1lcyBpbiB0aGUgbWFpbiBxdWVyeSwgYXZvaWRpbmcgdGhlIHJlcGV0aXRpb24gb2YgdGhlIHNhbWUgc3VicXVlcnkuCgojIyMgRXhhbXBsZXMKClRoZSBTYWtpbGEgc2FtcGxlIGRhdGFiYXNlIGlzIGEgZ29vZCByZXNvdXJjZSBmb3IgZGVtb25zdHJhdGluZyByZWFsLXdvcmxkIGFwcGxpY2F0aW9ucyBvZiBDVEVzLiBIZXJlIGFyZSBhIGZldyB1c2UgY2FzZXM6CgojIyMjIDEuIFNpbXBsaWZ5aW5nIENvbXBsZXggSm9pbnMKClN1cHBvc2UgeW91IHdhbnQgdG8gZmluZCBmaWxtcyB3aXRoIHRoZSBoaWdoZXN0IHJlbnRhbCByYXRlcyBpbiBlYWNoIHJhdGluZyBjYXRlZ29yeS4gVGhpcyBtaWdodCBpbnZvbHZlIGNvbXBsZXggam9pbnMgYW5kIGFnZ3JlZ2F0aW9ucy4gVXNpbmcgYSBDVEUsIHlvdSBjYW4gc2ltcGxpZnkgaXQ6CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CldJVEggSGlnaGVzdFJlbnRhbFJhdGVzIEFTICgKICAgIFNFTEVDVCByYXRpbmcsIE1BWChyZW50YWxfcmF0ZSkgQVMgTWF4UmF0ZQogICAgRlJPTSBGaWxtCiAgICBHUk9VUCBCWSByYXRpbmcKKQoKU0VMRUNUIGYuZmlsbV9pZCwgZi50aXRsZSwgZi5yYXRpbmcsIGYucmVudGFsX3JhdGUKICBGUk9NIEZpbG0gZiBJTk5FUiBKT0lOIEhpZ2hlc3RSZW50YWxSYXRlcyBoIAogICAgICAgICAgICAgIE9OIChmLnJhdGluZyA9IGgucmF0aW5nIEFORCBmLnJlbnRhbF9yYXRlID0gaC5NYXhSYXRlKTsKYGBgCgojIyMjIDIuIFJlY3Vyc2l2ZSBRdWVyaWVzCgpJZiB0aGUgU2FraWxhIGRhdGFiYXNlIGhhZCBoaWVyYXJjaGljYWwgZGF0YSwgbGlrZSBhIHRhYmxlIHJlcHJlc2VudGluZyBmaWxtIGNhdGVnb3JpZXMgYW5kIHN1YmNhdGVnb3JpZXMsIHlvdSBjb3VsZCB1c2UgcmVjdXJzaXZlIENURXMgdG8gdHJhdmVyc2UgdGhpcyBoaWVyYXJjaHkuIEJ1dCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIFNha2lsYSBkYXRhYmFzZSBkb2VzIG5vdCBpbmhlcmVudGx5IGluY2x1ZGUgaGllcmFyY2hpY2FsIGRhdGEgbGlrZSBjYXRlZ29yaWVzIGFuZCBzdWJjYXRlZ29yaWVzLiBGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBleGFtcGxlLCBJJ2xsIGFzc3VtZSBzdWNoIGEgc3RydWN0dXJlIGV4aXN0cyBvciB5b3UncmUgd2lsbGluZyB0byBhZGFwdCB0aGUgZXhhbXBsZSB0byB5b3VyIHNwZWNpZmljIHVzZSBjYXNlLgoKQXNzdW1pbmcgd2UgaGF2ZSBhIHRhYmxlIGBmaWxtX2NhdGVnb3J5YCB3aXRoIGEgaGllcmFyY2hpY2FsIHN0cnVjdHVyZSAod2hpY2ggaXMgbm90IGFjdHVhbGx5IHByZXNlbnQgaW4gdGhlIFNha2lsYSBkYXRhYmFzZSksIHRoZSBxdWVyeSBtaWdodCBsb29rIGxpa2UgdGhpczoKCmBgYCBzcWwKV0lUSCBSRUNVUlNJVkUgU3ViQ2F0ZWdvcnkgQVMgKAogICAgU0VMRUNUIGNhdGVnb3J5X2lkLCBwYXJlbnRfY2F0ZWdvcnlfaWQKICAgICAgRlJPTSBmaWxtX2NhdGVnb3J5CiAgICAgV0hFUkUgcGFyZW50X2NhdGVnb3J5X2lkIElTIE5VTEwKICAgIAogICAgVU5JT04gQUxMCiAgICAKICAgIFNFTEVDVCBmYy5jYXRlZ29yeV9pZCwgc2MucGFyZW50X2NhdGVnb3J5X2lkCiAgICAgIEZST00gZmlsbV9jYXRlZ29yeSBmYyBJTk5FUiBKT0lOIFN1YkNhdGVnb3J5IHNjIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgT04gc2MucGFyZW50X2NhdGVnb3J5X2lkID0gc2MuY2F0ZWdvcnlfaWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAopCgpTRUxFQ1QgKiBGUk9NIFN1YkNhdGVnb3J5OwpgYGAKCiMjIyMgMy4gQW5hbHl6aW5nIFBhdHRlcm5zCgpMZXQncyBzYXkgeW91IHdhbnQgdG8gYW5hbHl6ZSByZW50YWwgcGF0dGVybnMsIGxpa2UgZmluZGluZyBjdXN0b21lcnMgd2hvIHJlbnRlZCBtb3JlIHRoYW4gZml2ZSBtb3ZpZXMgaW4gYSBtb250aC4gQSBDVEUgY2FuIGJlIHVzZWQgdG8gYWdncmVnYXRlIHJlbnRhbCBkYXRhIGJlZm9yZSBqb2luaW5nIGl0IHdpdGggY3VzdG9tZXIgZGF0YToKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KV0lUSCBNb250aGx5UmVudGFscyBBUyAoCiAgICBTRUxFQ1QgY3VzdG9tZXJfaWQsIHN0cmZ0aW1lKCclbScsIHJlbnRhbF9kYXRlKSBBUyByZW50YWxfbW9udGgsIENPVU5UKCopIEFTIHRvdGFsX3JlbnRhbHMKICAgICAgRlJPTSByZW50YWwKICAgICBHUk9VUCBCWSBjdXN0b21lcl9pZCwgc3RyZnRpbWUoJyVtJywgcmVudGFsX2RhdGUpCiAgICBIQVZJTkcgQ09VTlQoKikgPiA1CikKU0VMRUNUIGMuY3VzdG9tZXJfaWQsIGMuZmlyc3RfbmFtZSwgYy5sYXN0X25hbWUsIG1yLnJlbnRhbF9tb250aCwgbXIudG90YWxfcmVudGFscwogIEZST00gY3VzdG9tZXIgYwogICAgICAgSk9JTiBNb250aGx5UmVudGFscyBtciBPTiBjLmN1c3RvbWVyX2lkID0gbXIuY3VzdG9tZXJfaWQ7CgpgYGAKClNRTGl0ZSBoYXMgc2xpZ2h0bHkgZGlmZmVyZW50IHN5bnRheCBhbmQgY2FwYWJpbGl0aWVzIGNvbXBhcmVkIHRvIG90aGVyIFNRTCBkYXRhYmFzZXMgbGlrZSBPcmFjbGUncyB2ZXJzaW9uIHVzZWQgaW4gU2FraWxhLgoKSW4gU1FMaXRlLCB0aGUgYEVYVFJBQ1RgIGZ1bmN0aW9uIGlzbid0IGF2YWlsYWJsZSwgc28gd2UnbGwgdXNlIGBzdHJmdGltZWAgdG8gZXh0cmFjdCB0aGUgbW9udGggZnJvbSB0aGUgKnJlbnRhbF9kYXRlKi4KCiMjIyBDb25zaWRlcmF0aW9ucwoKSW4gc3VtbWFyeSwgdGhlIFNRTCBgV0lUSGAgc3RhdGVtZW50IGlzIGEgdmVyc2F0aWxlIHRvb2wgaW4gdGhlIFNRTCBkZXZlbG9wZXIncyBhcnNlbmFsLCBlbmFibGluZyB0aGUgY3JlYXRpb24gb2YgbW9yZSByZWFkYWJsZSwgbWFpbnRhaW5hYmxlLCBhbmQgb2Z0ZW4gbW9yZSBlZmZpY2llbnQgU1FMIHF1ZXJpZXMuIFRoZSBleGFtcGxlcyB1c2luZyB0aGUgU2FraWxhIGZpbG0gZGF0YWJhc2UgaWxsdXN0cmF0ZSBob3cgQ1RFcyBjYW4gYmUgdXNlZCBmb3Igc2ltcGxpZnlpbmcgY29tcGxleCBxdWVyaWVzLCB3b3JraW5nIHdpdGggcmVjdXJzaXZlIGRhdGEsIGFuZCBwZXJmb3JtaW5nIGRldGFpbGVkIGRhdGEgYW5hbHlzaXMuCgojIyBQYXJ0aXRpb25zCgpUaGUgYFBBUlRJVElPTiBCWWAgY2xhdXNlIGluIFNRTCBpcyBhIHBvd2VyZnVsIGZlYXR1cmUgdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHdpbmRvdyBmdW5jdGlvbnMuIEl0IGFsbG93cyB5b3UgdG8gcGFydGl0aW9uIGEgcmVzdWx0IHNldCBpbnRvIGdyb3VwcyBhbmQgcGVyZm9ybSBjYWxjdWxhdGlvbnMgb3IgYXBwbHkgZnVuY3Rpb25zIHRvIGVhY2ggZ3JvdXAgKHBhcnRpdGlvbikgaW5kZXBlbmRlbnRseS4gSGVyZSdzIGEgZGV0YWlsZWQgZXhwbGFuYXRpb246CgojIyMgMS4gUHVycG9zZQoKLSAgICoqUHVycG9zZSoqOiBgUEFSVElUSU9OIEJZYCBkaXZpZGVzIHRoZSByZXN1bHQgc2V0IGludG8gcGFydGl0aW9ucyB0byB3aGljaCB0aGUgd2luZG93IGZ1bmN0aW9uIGlzIGFwcGxpZWQuIEluc3RlYWQgb2YgdHJlYXRpbmcgdGhlIGVudGlyZSByZXN1bHQgc2V0IGFzIGEgc2luZ2xlIGdyb3VwLCBpdCBlbmFibGVzIGNhbGN1bGF0aW9ucyBhY3Jvc3MgYSBzZXQgb2Ygcm93cyB0aGF0IHNoYXJlIGNvbW1vbiBwcm9wZXJ0aWVzLgoKLSAgICoqVXNlIHdpdGggV2luZG93IEZ1bmN0aW9ucyoqOiBJdCdzIHVzZWQgd2l0aCB3aW5kb3cgZnVuY3Rpb25zIGxpa2UgYFJPV19OVU1CRVIoKWAsIGBSQU5LKClgLCBgU1VNKClgLCBgQVZHKClgLCBldGMuIFRoZXNlIGZ1bmN0aW9ucyBwZXJmb3JtIGEgY2FsY3VsYXRpb24gYWNyb3NzIGEgc2V0IG9mIHRhYmxlIHJvd3MgcmVsYXRlZCB0byB0aGUgY3VycmVudCByb3cuCgojIyMgMi4gVXNlIENhc2VzCgotICAgKipEYXRhIEFuYWx5c2lzKio6IEZvciBhbmFseXppbmcgdHJlbmRzIHdpdGhpbiBzdWJncm91cHMgb2YgZGF0YS4gRm9yIGluc3RhbmNlLCBmaW5kaW5nIHRoZSBhdmVyYWdlIHNhbGVzIHBlciBkZXBhcnRtZW50IGluIGEgc3RvcmUuCi0gICAqKlJhbmtpbmcqKjogQXNzaWduaW5nIHJhbmtzIHdpdGhpbiBjYXRlZ29yaWVzLCBzdWNoIGFzIHJhbmtpbmcgc3R1ZGVudHMgYnkgc2NvcmUgd2l0aGluIGVhY2ggY2xhc3MuCi0gICAqKlJ1bm5pbmcgVG90YWxzKio6IENhbGN1bGF0aW5nIHJ1bm5pbmcgdG90YWxzLCBhdmVyYWdlcywgb3Igb3RoZXIgY3VtdWxhdGl2ZSBtZWFzdXJlcyB3aXRoaW4gZWFjaCBwYXJ0aXRpb24uCi0gICAqKlNlZ21lbnRhdGlvbioqOiBTZWdtZW50aW5nIGRhdGEgaW50byBncm91cHMgZm9yIG1vcmUgZm9jdXNlZCBhbmFseXNpcywgbGlrZSBtb250aGx5IGV4cGVuc2VzIHBlciBkZXBhcnRtZW50LgoKIyMjIDMuIEJlbmVmaXRzCgotICAgKipJbXByb3ZlZCBEYXRhIE9yZ2FuaXphdGlvbioqOiBJdCBlbmFibGVzIG1vcmUgZ3JhbnVsYXIgZGF0YSBhbmFseXNpcyB3aXRob3V0IHRoZSBuZWVkIGZvciBjb21wbGV4IHN1YnF1ZXJpZXMgb3IgdGVtcG9yYXJ5IHRhYmxlcy4KLSAgICoqRWZmaWNpZW5jeSoqOiBNb3JlIGVmZmljaWVudCB0aGFuIHNlbGYtam9pbnMgb3Igc3VicXVlcmllcyBmb3IgY2VydGFpbiB0eXBlcyBvZiBjYWxjdWxhdGlvbnMuCi0gICAqKkZsZXhpYmlsaXR5Kio6IE9mZmVycyBmbGV4aWJpbGl0eSBpbiBkYXRhIGFuYWx5c2lzLCBhbGxvd2luZyBmb3IgY29tcGxleCBjYWxjdWxhdGlvbnMgYWNyb3NzIGRpZmZlcmVudCBzZWdtZW50cyBvZiBkYXRhLgoKIyMjIDQuIExpbWl0YXRpb25zCgotICAgKipQZXJmb3JtYW5jZSoqOiBPbiBsYXJnZSBkYXRhc2V0cywgcGFydGl0aW9uaW5nIGNhbiBsZWFkIHRvIHBlcmZvcm1hbmNlIGlzc3VlcyBpZiBub3QgaW5kZXhlZCBvciBvcHRpbWl6ZWQgcHJvcGVybHkuCi0gICAqKkNvbXBsZXhpdHkqKjogVGhlIHN5bnRheCBjYW4gYmUgY29tcGxleCwgZXNwZWNpYWxseSBmb3IgYmVnaW5uZXJzIG9yIGZvciBjb21wbGV4IGFuYWx5dGljYWwgcXVlcmllcy4KLSAgICoqRGF0YWJhc2UgU3VwcG9ydCoqOiBOb3QgYWxsIGRhdGFiYXNlcyBzdXBwb3J0IGFsbCB3aW5kb3cgZnVuY3Rpb25zLCBhbmQgdGhlcmUgY2FuIGJlIGRpZmZlcmVuY2VzIGluIGltcGxlbWVudGF0aW9uLgoKIyMjIDUuIEV4YW1wbGVzCgpBc3N1bWluZyB0aGUgc2FtZSBTYWtpbGEgZGF0YWJhc2UgYW5kIFNRTGl0ZSwgbGV0J3MgY29uc2lkZXIgc29tZSBleGFtcGxlczoKCiMjIyMgRXhhbXBsZSAxOiBSYW5raW5nIEN1c3RvbWVycyBieSBBbW91bnQgU3BlbnQKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIGN1c3RvbWVyX2lkLCBhbW91bnQsCiAgICAgICBSQU5LKCkgT1ZFUiAoUEFSVElUSU9OIEJZIGN1c3RvbWVyX2lkIE9SREVSIEJZIGFtb3VudCBERVNDKSBhcyByYW5rCkZST00gcGF5bWVudApMSU1JVCA1OwpgYGAKCi0gICAqKkRlc2NyaXB0aW9uKio6IFRoaXMgcmFua3MgY3VzdG9tZXJzIGJhc2VkIG9uIHRoZSBhbW91bnQgc3BlbnQsIHBhcnRpdGlvbmluZyBieSBgY3VzdG9tZXJfaWRgLiBUaGUgTElNSVQgaXMgYXJiaXRyYXJ5IGFuZCBvbmx5IHNlcnZlcyB0byByZWR1Y2UgdGhlIHJlc3VsdCBzZXQgZm9yIGRpc3BsYXkgaW4gdGhpcyBsZXNzb24uCgojIyMjIEV4YW1wbGUgMjogUnVubmluZyBUb3RhbCBvZiBQYXltZW50cyBieSBDdXN0b21lcgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpTRUxFQ1QgY3VzdG9tZXJfaWQsIHBheW1lbnRfZGF0ZSwgYW1vdW50LAogICAgICAgU1VNKGFtb3VudCkgT1ZFUiAoUEFSVElUSU9OIEJZIGN1c3RvbWVyX2lkIE9SREVSIEJZIHBheW1lbnRfZGF0ZSkgYXMgcnVubmluZ190b3RhbApGUk9NIHBheW1lbnQKYGBgCgotICAgKipEZXNjcmlwdGlvbioqOiBDYWxjdWxhdGVzIGEgcnVubmluZyB0b3RhbCBvZiBwYXltZW50cyBmb3IgZWFjaCBjdXN0b21lciBvdmVyIHRpbWUuCgojIyMgQ29uY2x1c2lvbgoKYFBBUlRJVElPTiBCWWAgaXMgYSB2ZXJzYXRpbGUgdG9vbCBpbiBTUUwgdGhhdCBlbmhhbmNlcyBkYXRhIGFuYWx5c2lzIGNhcGFiaWxpdGllcyBieSBlbmFibGluZyBzZWdtZW50LXNwZWNpZmljIGNhbGN1bGF0aW9ucy4gV2hpbGUgaXQgaGFzIGxpbWl0YXRpb25zLCBwYXJ0aWN1bGFybHkgaW4gcGVyZm9ybWFuY2UgYW5kIGNvbXBsZXhpdHksIGl0cyBiZW5lZml0cyBpbiBvcmdhbml6aW5nIGFuZCBwcm9jZXNzaW5nIGRhdGEgZm9yIGFuYWx5dGljYWwgcXVlcmllcyBhcmUgc2lnbmlmaWNhbnQuCgojIyMgUEFSVElPTiBCWSB2cyBHUk9VUCBCWQoKYFBBUlRJVElPTiBCWWAgYW5kIGBHUk9VUCBCWWAgYXJlIGJvdGggU1FMIGNsYXVzZXMgdXNlZCB0byBvcmdhbml6ZSBkYXRhIGludG8gc3Vic2V0cywgYnV0IHRoZXkgc2VydmUgZGlmZmVyZW50IHB1cnBvc2VzIGFuZCBhcmUgdXNlZCBpbiBkaWZmZXJlbnQgY29udGV4dHMuCgojIyMgYFBBUlRJVElPTiBCWWAKCi0gICAqKkNvbnRleHQqKjogVXNlZCB3aXRoIHdpbmRvdyBmdW5jdGlvbnMuCi0gICAqKkZ1bmN0aW9uYWxpdHkqKjogSXQgb3JnYW5pemVzIGRhdGEgaW50byBwYXJ0aXRpb25zLCBhbGxvd2luZyB5b3UgdG8gcGVyZm9ybSBjYWxjdWxhdGlvbnMgYWNyb3NzIGVhY2ggcGFydGl0aW9uLiBFYWNoIHBhcnRpdGlvbiBjYW4gYmUgY29uc2lkZXJlZCBhICd3aW5kb3cnIG92ZXIgd2hpY2ggU1FMIGZ1bmN0aW9ucyBvcGVyYXRlLgotICAgKipSb3cgUmV0ZW50aW9uKio6IEl0IGRvZXMgbm90IGNoYW5nZSB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIHF1ZXJ5J3MgcmVzdWx0LiBFYWNoIHJvdyBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldCByZW1haW5zIGluIHRoZSBvdXRwdXQsIGFsb25nIHdpdGggYW55IGFkZGl0aW9uYWwgY29sdW1ucyBjcmVhdGVkIGJ5IHRoZSB3aW5kb3cgZnVuY3Rpb24uCi0gICAqKlVzZSBDYXNlcyoqOiBDb21tb25seSB1c2VkIGZvciByYW5raW5nLCBjYWxjdWxhdGluZyBydW5uaW5nIHRvdGFscywgbW92aW5nIGF2ZXJhZ2VzLCBhbmQgb3RoZXIgY29tcGxleCBhbmFseXRpY3MgdGhhdCByZXF1aXJlIGFjY2VzcyB0byBtdWx0aXBsZSByb3dzIG9mIHRoZSBkYXRhc2V0IGF0IG9uY2UsIHdpdGhvdXQgZ3JvdXBpbmcgdGhlbSBpbnRvIGEgc2luZ2xlIG91dHB1dCByb3cuCgojIyMjIEV4YW1wbGUgb2YgYFBBUlRJVElPTiBCWWAKCmBgYCBzcWwKU0VMRUNUIGlkLCBkZXBhcnRtZW50LCBzYWxhcnksCiAgICAgICBBVkcoc2FsYXJ5KSBPVkVSIChQQVJUSVRJT04gQlkgZGVwYXJ0bWVudCkgYXMgYXZnX2RlcGFydG1lbnRfc2FsYXJ5CkZST00gZW1wbG95ZWVzOwpgYGAKCkhlcmUsIGBQQVJUSVRJT04gQllgIGNhbGN1bGF0ZXMgdGhlIGF2ZXJhZ2Ugc2FsYXJ5IHBlciBkZXBhcnRtZW50LCBidXQgZXZlcnkgcm93IGZyb20gdGhlIG9yaWdpbmFsIGBlbXBsb3llZXNgIHRhYmxlIGlzIHJldGFpbmVkIGluIHRoZSBvdXRwdXQuCgojIyMgYEdST1VQIEJZYAoKLSAgICoqQ29udGV4dCoqOiBVc2VkIGluIGFnZ3JlZ2F0ZSBxdWVyaWVzLgotICAgKipGdW5jdGlvbmFsaXR5Kio6IEl0IGdyb3VwcyByb3dzIHRoYXQgaGF2ZSB0aGUgc2FtZSB2YWx1ZXMgaW4gc3BlY2lmaWVkIGNvbHVtbnMgaW50byBzdW1tYXJ5IHJvd3MsIGxpa2UgImZpbmQgdGhlIHRvdGFsIHNhbGFyeSBwZXIgZGVwYXJ0bWVudCIuCi0gICAqKlJvdyBSZWR1Y3Rpb24qKjogSXQgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIHJlc3VsdCwgcHJvdmlkaW5nIG9uZSByb3cgcGVyIGdyb3VwLiBZb3UgY2FuIG9ubHkgc2VsZWN0IHRoZSBncm91cGVkIGNvbHVtbnMgYW5kIHRoZSByZXN1bHRzIG9mIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMuCi0gICAqKlVzZSBDYXNlcyoqOiBJZGVhbCBmb3Igc3VtbWFyaWVzLCBzdWNoIGFzIHRvdGFscywgYXZlcmFnZXMsIGNvdW50cywgbWluLCBtYXgsIGV0Yy4sIHdoZXJlIHlvdSBuZWVkIGEgc2luZ2xlIHJlc3VsdCByb3cgcGVyIGdyb3VwLgoKIyMjIyBFeGFtcGxlIG9mIGBHUk9VUCBCWWAKCmBgYCBzcWwKU0VMRUNUIGRlcGFydG1lbnQsIEFWRyhzYWxhcnkpIGFzIGF2Z19kZXBhcnRtZW50X3NhbGFyeQpGUk9NIGVtcGxveWVlcwpHUk9VUCBCWSBkZXBhcnRtZW50OwpgYGAKCkhlcmUsIGBHUk9VUCBCWWAgcHJvdmlkZXMgdGhlIGF2ZXJhZ2Ugc2FsYXJ5IHBlciBkZXBhcnRtZW50LCBidXQgdGhlIG91dHB1dCB3aWxsIG9ubHkgaGF2ZSBvbmUgcm93IHBlciBkZXBhcnRtZW50LgoKIyMjIEtleSBEaWZmZXJlbmNlcwoKMS4gICoqUm93IENvdW50Kio6IGBQQVJUSVRJT04gQllgIHJldGFpbnMgdGhlIG9yaWdpbmFsIHJvdyBjb3VudCwgd2hlcmVhcyBgR1JPVVAgQllgIGNvbnNvbGlkYXRlcyByb3dzIGludG8gZ3JvdXBzLCByZWR1Y2luZyB0aGUgcm93IGNvdW50LgoyLiAgKipVc2Ugd2l0aCBGdW5jdGlvbnMqKjogYFBBUlRJVElPTiBCWWAgaXMgdXNlZCB3aXRoIHdpbmRvdyBmdW5jdGlvbnMsIHdoaWxlIGBHUk9VUCBCWWAgaXMgdXNlZCB3aXRoIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMuCjMuICAqKlJlc3VsdCBTZXQqKjogYFBBUlRJVElPTiBCWWAgYWRkcyBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIHRvIGVhY2ggcm93LCB3aGVyZWFzIGBHUk9VUCBCWWAgcHJvdmlkZXMgYSBzdW1tYXJpemVkIHJlc3VsdCBzZXQuCjQuICAqKlNjb3BlIG9mIENhbGN1bGF0aW9uKio6IEluIGBQQVJUSVRJT04gQllgLCBjYWxjdWxhdGlvbnMgYXJlIGRvbmUgd2l0aGluIGVhY2ggcGFydGl0aW9uIGFuZCBkb24ndCBhZmZlY3Qgb3RoZXIgcGFydGl0aW9ucy4gSW4gYEdST1VQIEJZYCwgY2FsY3VsYXRpb25zIGFyZSBkb25lIG9uIGdyb3VwZWQgZGF0YSwgcHJvZHVjaW5nIGEgc2luZ2xlIHJlc3VsdCBmb3IgZWFjaCBncm91cC4KCiMjIyBFeGFtcGxlcwoKTGV0J3MgdXNlIHRoZSBTYWtpbGEgZGF0YWJhc2Ugd2l0aCBhIGZvY3VzIG9uIGEgc2NlbmFyaW8gd2hlcmUgd2UgbWlnaHQgd2FudCB0byBhbmFseXplIHRoZSBmaWxtIHJlbnRhbCBkYXRhLiBXZSB3aWxsIGNvbXBhcmUgdGhlIHVzYWdlIG9mIGBQQVJUSVRJT04gQllgIGFuZCBgR1JPVVAgQllgIGluIFNRTCBxdWVyaWVzIHVzaW5nIHRoaXMgY29udGV4dC4gTm90ZSB0aGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIFNha2lsYSBkYXRhYmFzZSBtaWdodCBkaWZmZXIgc2xpZ2h0bHkgYmFzZWQgb24gdGhlIGFkYXB0YXRpb24gZm9yIFNRTGl0ZSwgYnV0IHRoZSBnZW5lcmFsIGNvbmNlcHQgcmVtYWlucyB0aGUgc2FtZS4KCiMjIyBTY2VuYXJpbzogQW5hbHl6aW5nIEZpbG0gUmVudGFscwoKV2Ugd2FudCB0byBhbmFseXplIGZpbG0gcmVudGFscywgbG9va2luZyBhdCBib3RoIHRoZSB0b3RhbCBudW1iZXIgb2YgcmVudGFscyBwZXIgY2F0ZWdvcnkgYW5kIHRoZSByYW5rIG9mIGVhY2ggZmlsbSB3aXRoaW4gaXRzIGNhdGVnb3J5IGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gcmVudGVkLgoKIyMjIDEuIFVzaW5nIGBHUk9VUCBCWWAKCioqT2JqZWN0aXZlKio6IFRvIGZpbmQgdGhlIHRvdGFsIG51bWJlciBvZiByZW50YWxzIGZvciBlYWNoIGZpbG0gY2F0ZWdvcnkuCgojIyMjIFNRTCBRdWVyeToKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIGNhdGVnb3J5Lm5hbWUgQVMgY2F0ZWdvcnlfbmFtZSwgQ09VTlQocmVudGFsLnJlbnRhbF9pZCkgQVMgdG90YWxfcmVudGFscwogIEZST00gY2F0ZWdvcnkKICAgICAgIEpPSU4gZmlsbV9jYXRlZ29yeSBPTiBjYXRlZ29yeS5jYXRlZ29yeV9pZCA9IGZpbG1fY2F0ZWdvcnkuY2F0ZWdvcnlfaWQKICAgICAgIEpPSU4gZmlsbSBPTiBmaWxtX2NhdGVnb3J5LmZpbG1faWQgPSBmaWxtLmZpbG1faWQKICAgICAgIEpPSU4gaW52ZW50b3J5IE9OIGZpbG0uZmlsbV9pZCA9IGludmVudG9yeS5maWxtX2lkCiAgICAgICBKT0lOIHJlbnRhbCBPTiBpbnZlbnRvcnkuaW52ZW50b3J5X2lkID0gcmVudGFsLmludmVudG9yeV9pZAogR1JPVVAgQlkgY2F0ZWdvcnkubmFtZTsKYGBgCgoqKkV4cGxhbmF0aW9uKio6IC0gVGhpcyBxdWVyeSBncm91cHMgZmlsbXMgYnkgdGhlaXIgY2F0ZWdvcnkgYW5kIGNvdW50cyB0aGUgdG90YWwgbnVtYmVyIG9mIHJlbnRhbHMgZm9yIGVhY2ggY2F0ZWdvcnkuIC0gVGhlIHJlc3VsdCB3aWxsIGJlIGEgc3VtbWFyeSB0YWJsZSB3aXRoIG9uZSByb3cgcGVyIGNhdGVnb3J5LCBzaG93aW5nIHRoZSB0b3RhbCByZW50YWxzLgoKIyMjIDIuIFVzaW5nIGBQQVJUSVRJT04gQllgCgoqKk9iamVjdGl2ZSoqOiBUbyByYW5rIGVhY2ggZmlsbSB3aXRoaW4gaXRzIGNhdGVnb3J5IGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gcmVudGVkLgoKIyMjIyBTUUwgUXVlcnk6CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259ClNFTEVDVCBmaWxtLnRpdGxlLCBjYXRlZ29yeS5uYW1lIEFTIGNhdGVnb3J5X25hbWUsIAogICAgICAgUkFOSygpIE9WRVIgKFBBUlRJVElPTiBCWSBjYXRlZ29yeS5uYW1lIE9SREVSIEJZIENPVU5UKHJlbnRhbC5yZW50YWxfaWQpIERFU0MpIEFTIHJlbnRhbF9yYW5rCiAgRlJPTSBjYXRlZ29yeQogICAgICAgSk9JTiBmaWxtX2NhdGVnb3J5IE9OIGNhdGVnb3J5LmNhdGVnb3J5X2lkID0gZmlsbV9jYXRlZ29yeS5jYXRlZ29yeV9pZAogICAgICAgSk9JTiBmaWxtIE9OIGZpbG1fY2F0ZWdvcnkuZmlsbV9pZCA9IGZpbG0uZmlsbV9pZAogICAgICAgSk9JTiBpbnZlbnRvcnkgT04gZmlsbS5maWxtX2lkID0gaW52ZW50b3J5LmZpbG1faWQKICAgICAgIEpPSU4gcmVudGFsIE9OIGludmVudG9yeS5pbnZlbnRvcnlfaWQgPSByZW50YWwuaW52ZW50b3J5X2lkCiBHUk9VUCBCWSBmaWxtLnRpdGxlLCBjYXRlZ29yeS5uYW1lOwpgYGAKCioqRXhwbGFuYXRpb24qKjogLSBUaGlzIHF1ZXJ5IHJhbmtzIGZpbG1zIHdpdGhpbiBlYWNoIGNhdGVnb3J5IGJhc2VkIG9uIHRoZWlyIHJlbnRhbCBjb3VudC4gLSBUaGUgYFBBUlRJVElPTiBCWWAgY2xhdXNlIGlzIHVzZWQgdG8gY3JlYXRlIHBhcnRpdGlvbnMgZm9yIGVhY2ggZmlsbSBjYXRlZ29yeSwgYW5kIHdpdGhpbiBlYWNoIHBhcnRpdGlvbiwgZmlsbXMgYXJlIHJhbmtlZCBieSB0aGVpciByZW50YWwgY291bnRzLiAtIFVubGlrZSBgR1JPVVAgQllgLCB0aGlzIHF1ZXJ5IGRvZXMgbm90IHJlZHVjZSB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIG91dHB1dC4gRWFjaCBmaWxtIHJldGFpbnMgaXRzIHJvdywgYnV0IHdpdGggYWRkaXRpb25hbCBpbmZvcm1hdGlvbiAocmFuaykgYWRkZWQuCgojIyMgS2V5IERpZmZlcmVuY2VzIElsbHVzdHJhdGVkOgoKLSAgICoqQWdncmVnYXRpb24gdnMuIFdpbmRvd2luZyoqOiBUaGUgYEdST1VQIEJZYCBxdWVyeSBhZ2dyZWdhdGVzIGRhdGEsIHJlZHVjaW5nIHRoZSByZXN1bHQgdG8gb25lIHJvdyBwZXIgY2F0ZWdvcnksIHdoaWxlIHRoZSBgUEFSVElUSU9OIEJZYCBxdWVyeSBhcHBsaWVzIGEgd2luZG93IGZ1bmN0aW9uIHdpdGhpbiBlYWNoIGNhdGVnb3J5LCByZXRhaW5pbmcgaW5kaXZpZHVhbCBmaWxtIHJvd3MuCi0gICAqKk91dHB1dCoqOiBgR1JPVVAgQllgIHByb2R1Y2VzIGEgc3VtbWFyeSB0YWJsZSwgd2hlcmVhcyBgUEFSVElUSU9OIEJZYCBhZGRzIGFkZGl0aW9uYWwgcmFua2luZyBpbmZvcm1hdGlvbiB0byBlYWNoIGZpbG0gd2l0aGluIGl0cyBjYXRlZ29yeS4KClRoZXNlIGV4YW1wbGVzIGhpZ2hsaWdodCBob3cgYEdST1VQIEJZYCBpcyB1c2VkIGZvciBhZ2dyZWdhdGluZyBkYXRhIGludG8gc3VtbWFyeSBmb3JtLCB3aGVyZWFzIGBQQVJUSVRJT04gQllgIGlzIHVzZWQgZm9yIHBlcmZvcm1pbmcgY2FsY3VsYXRpb25zIHdpdGhpbiBzdWJzZXRzIG9mIGRhdGEgd2hpbGUgcmV0YWluaW5nIHRoZSBvcmlnaW5hbCBkYXRhIHN0cnVjdHVyZS4KCiMjIFN1bW1hcnkKClRoaXMgbGVzc29uIGNvdmVyZWQgdGhlIHVzZSBvZiBzdWJxdWVyaWVzIGFuZCB0aGUgYFdJVEhgIHN0YXRlbWVudCBpbiBTUUwgZm9yIGZvcm11bGF0aW5nIGNvbXBsZXggcXVlcmllcywgYW5kIHRoZSB1c2Ugb2YgYFBBUlRJVElPTiBCWWAgdG8gYXBwbHkgd2luZG93IGZ1bmN0aW9ucyB0byBncm91cHMgb2YgZGF0YSwgdXNpbmcgdGhlIFNha2lsYSBNZWRpYSBhbmQgRmlsbSBkYXRhYmFzZSBmb3IgZXhhbXBsZXMuCgojIyMgU3VicXVlcmllcwoKLSAgICoqRGVmaW5pdGlvbioqOiBTdWJxdWVyaWVzIGFyZSBTUUwgcXVlcmllcyBlbWJlZGRlZCB3aXRoaW4gYW5vdGhlciBTUUwgcXVlcnksIHVzZWQgaW4gYFNFTEVDVGAsIGBGUk9NYCwgYFdIRVJFYCwgYW5kIGBIQVZJTkdgIGNsYXVzZXMuCi0gICAqKkJlbmVmaXRzKio6IFRoZXkgZW5oYW5jZSBkYXRhIHJldHJpZXZhbCBpbiBjb21wbGV4IHNjZW5hcmlvcywgaGFuZGxlIGNvbXBsZXggZGF0YSByZWxhdGlvbnNoaXBzLCBhbmQgaW1wbGVtZW50IG5lc3RlZCBsb2dpYy4KLSAgICoqQ2hhcmFjdGVyaXN0aWNzKio6IFN1YnF1ZXJpZXMgY2FuIHJldHVybiB2YXJpb3VzIHR5cGVzIG9mIHJlc3VsdHMgYW5kIGNhbiBiZSBpbmRlcGVuZGVudCBvciBjb3JyZWxhdGVkIHdpdGggdGhlIG1haW4gcXVlcnkuCi0gICAqKkFkdmFudGFnZXMgYW5kIExpbWl0YXRpb25zKio6IFRoZXkgb2ZmZXIgbW9kdWxhcml0eSBhbmQgaW1wcm92ZWQgcmVhZGFiaWxpdHkgYnV0IGNhbiBjYXVzZSBwZXJmb3JtYW5jZSBpc3N1ZXMgYW5kIGFkZCBjb21wbGV4aXR5LgotICAgKipUeXBlcyBvZiBTdWJxdWVyaWVzKio6IFRoZXNlIGluY2x1ZGUgc2luZ2xlLXJvdywgbXVsdGlwbGUtcm93LCBjb3JyZWxhdGVkLCBzdWJxdWVyaWVzIGFzIGRlcml2ZWQgdGFibGVzLCBhbmQgZXhpc3RlbnRpYWwgc3VicXVlcmllcywgZWFjaCB3aXRoIHNwZWNpZmljIHVzZSBjYXNlcyBhbmQgZXhhbXBsZXMuCgojIyMgQ29tbW9uIFRhYmxlIEV4cHJlc3Npb25zIChDVEVzKQoKLSAgICoqQmFzaWNzKio6IFRoZSBgV0lUSGAgc3RhdGVtZW50IGNyZWF0ZXMgdGVtcG9yYXJ5IHJlc3VsdCBzZXRzIGZvciB1c2UgaW4gU1FMIHF1ZXJpZXMuCi0gICAqKkFkdmFudGFnZXMqKjogQ1RFcyBlbmhhbmNlIHJlYWRhYmlsaXR5LCBhbGxvdyBmb3IgcmVjdXJzaXZlIHF1ZXJpZXMsIGFuZCBjYW4gaW1wcm92ZSBwZXJmb3JtYW5jZS4KLSAgICoqVXNlIENhc2VzIGluIFNha2lsYSBEYXRhYmFzZSoqOiBFeGFtcGxlcyBhcmUgcHJvdmlkZWQsIGxpa2UgZmluZGluZyBmaWxtcyB3aXRoIHRoZSBoaWdoZXN0IHJlbnRhbCByYXRlcyBhbmQgYW5hbHl6aW5nIHJlbnRhbCBwYXR0ZXJucywgYWx0aG91Z2ggaXQncyBub3RlZCB0aGF0IHRoZSBTYWtpbGEgZGF0YWJhc2UgZG9lc24ndCBpbmhlcmVudGx5IGluY2x1ZGUgaGllcmFyY2hpY2FsIGRhdGEgZm9yIHNvbWUgb2YgdGhlIG1vcmUgY29tcGxleCBleGFtcGxlcy4KCiMjIyBQYXJ0aXRpb25zCgpUaGUgcHVycG9zZSBvZiBwYXJ0aXRpb25zIGFuZCB0aGUgYFBBUlRJVElPTiBCWWAgY2xhdXNlIGlzIHRvIGFwcGx5IHdpbmRvdyBmdW5jdGlvbnMgdG8gcGFydGl0aW9ucyBvZiBkYXRhLiBUaGUgaXMgb2Z0ZW4gbmVjZXNzYXJ5IGZvciBkYXRhIGFuYWx5c2lzIGFuZCByYW5raW5nIGFuZCBwcm92aWRlcyBpbXByb3ZlZCBkYXRhIG9yZ2FuaXphdGlvbiwgYnV0IGl0cyB1c2UgY2FuIGxlYWQgdG8gcGVyZm9ybWFuY2UgaXNzdWVzIG9uIGxhcmdlIGRhdGFzZXRzLgoKKipEaWZmZXJlbmNlIGJldHdlZW4gYFBBUlRJVElPTiBCWWAgYW5kIGBHUk9VUCBCWWAqKjogVGhlIGRpc3RpbmN0aW9uIGJldHdlZW4gdGhlc2UgdHdvIGNsYXVzZXMgd2FzIGNsYXJpZmllZC4gYFBBUlRJVElPTiBCWWAsIHVzZWQgd2l0aCB3aW5kb3cgZnVuY3Rpb25zLCByZXRhaW5zIHRoZSBvcmlnaW5hbCByb3cgY291bnQgYW5kIGFsbG93cyBjYWxjdWxhdGlvbnMgd2l0aGluIHBhcnRpdGlvbnMuIENvbnZlcnNlbHksIGBHUk9VUCBCWWAsIHVzZWQgZm9yIGFnZ3JlZ2F0ZSBxdWVyaWVzLCByZWR1Y2VzIHRoZSBudW1iZXIgb2Ygcm93cyB0byBvbmUgcGVyIGdyb3VwIGFuZCBpcyBpZGVhbCBmb3Igc3VtbWFyaWVzLgoKIyMgVHV0b3JpYWwKCkluIHRoaXMgYWR2YW5jZWQgdHV0b3JpYWwgdmlkZW8sIEphbWVzIE9saXZlciBleHBsYWlucyB1bmlvbnMsIHBhcnRpdGlvbnMsIHJlY3Vyc2lvbiwgYW5kIGhpZXJhcmNoaWVzIGZvciBjb25zdHJ1Y3RpbmcgY29tcGxleCBhbmQgZWZmaWNpZW50IFNRTCBxdWVyaWVzLgoKPGlmcmFtZSBzdHlsZSBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUtbm9jb29raWUuY29tL2VtYmVkL0M3Q1BYZUV2S04wIiB3aWR0aD0iNDgwIiBoZWlnaHQ9IjI3MCIgYWxsb3dmdWxsc2NyZWVuPSJhbGxvd2Z1bGxzY3JlZW4iIGRhdGEtZXh0ZXJuYWw9IjEiPgoKPC9pZnJhbWU+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEZpbGVzICYgUmVzb3VyY2VzCgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSZWZlcmVuY2VzCgpbU1FMaXRlIERvY3VtZW50aW9uOiBUaGUgV0lUSCBDbGF1c2VdKGh0dHBzOi8vd3d3LnNxbGl0ZS5vcmcvbGFuZ193aXRoLmh0bWwpCgojIyBFcnJhdGEKCltMZXQgdXMga25vd10oaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLzIxMjE4NzA3Mjc4NDE1Nyl7dGFyZ2V0PSJfYmxhbmsifS4K