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.sqlitedb1.

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);
Table 1: 1 records
numGrtAvg
8303

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
payment_id amount
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?

Solution

TBD

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%')
Table 3: 1 records
NumCities
39

Correlated Subqueries

Definition: A correlated subquery is one that uses values from the outer query. It is executed repeatedly, once for each row that might be selected by the outer query.

Use Cases: Effective in scenarios where each row in the outer query may need different results from the subquery. For instance, finding employees who earn more than the average salary in their respective departments.

Example:

SELECT staff_id, amount
  FROM Payment AS P
 WHERE amount > (SELECT AVG(amount)
                   FROM Payment
                  WHERE staff_id = P.staff_id)
Table 4: Displaying records 1 - 10
staff_id amount
1 5.99
2 9.99
1 4.99
1 4.99
2 5.99
1 5.99
1 4.99
1 4.99
1 7.99
1 4.99

Performance Note: Correlated subqueries can be less efficient because the subquery may need to be executed multiple times. A join with a “group by” is often simpler and preferable as it executes generally much faster and the database can use indexes.

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
customer_id last_name total_amount
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
last_name first_name
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

  1. Readability and Maintenance: CTEs can make complex queries more readable and maintainable by breaking them into simpler, modular parts.

  2. Recursive Queries: CTEs enable recursive queries, which are useful for hierarchical data processing, like tree structures.

  3. Performance Optimization: In some cases, CTEs can improve performance by simplifying complex joins and filters.

  4. 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
film_id title rating rental_rate
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
customer_id first_name last_name rental_month total_rentals
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
customer_id amount rank
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
customer_id payment_date amount running_total
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

  1. Row Count: PARTITION BY retains the original row count, whereas GROUP BY consolidates rows into groups, reducing the row count.
  2. Use with Functions: PARTITION BY is used with window functions, while GROUP BY is used with aggregate functions.
  3. Result Set: PARTITION BY adds additional information to each row, whereas GROUP BY provides a summarized result set.
  4. 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
category_name total_rentals
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
title category_name rental_rank
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.


Files & Resources

All Files for Lesson 70.107

Errata

Let us know.


  1. Sakila Database is licensed under the New BSD License and may be used for any purpose with the provision that notice of © 2020 Oracle Corporation appears.↩︎

LS0tCnRpdGxlOiAiQWR2YW5jZWQgU1FMOiBTdWJxdWVyaWVzLCBQYXJpdGlvbnMsIGFuZCBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMiCnBhcmFtczoKICBjYXRlZ29yeTogNzAKICBudW1iZXI6IDEwNwogIHRpbWU6IDQ1CiAgbGV2ZWw6IGJlZ2lubmVyCiAgdGFnczogInNxbCxTRUxFQ1QsQ1RFLFdJVEgsUEFSVElUSU9OIEJZLCBHUk9VUCBCWSxOVElMRSIKICBkZXNjcmlwdGlvbjogIkV4cGxhaW5zIGhvdyB0byBkZWZpbmUgY29tcGxleCBxdWVyaWVzIGluIFNRTCB1c2luZyBzdWJxdWVyaWVzLAogICAgICAgICAgICAgICAgYXMgd2VsbCBhcyBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMgKENURSkgdXNpbmcgV0lUSCwKICAgICAgICAgICAgICAgIGFuZCBob3cgdG8gYXBwbHkgd2luZG93IGZ1bmN0aW9ucyB0byBkYXRhIHBhcnRpdGlvbnMgdXNpbmcKICAgICAgICAgICAgICAgIFBBUlRJVElPbiBCWS4gSWxsdXN0cmF0ZXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBHUk9VUCBCWQogICAgICAgICAgICAgICAgYW5kIFBBUlRJVElPTiBCWS4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKU3VicXVlcmllcyBhbmQgdGhlIFdJVEggc3RhdGVtZW50IGFyZSB1c2VmdWwgcXVlcnkgZm9ybXVsYXRpb24gbWVjaGFuaXNtcyBmb3Igd3JpdGluZyBjb21wbGV4IHF1ZXJpZXMuIFRoaXMgbGVzc29uIGRlbW9uc3RyYXRlcywgdXNpbmcgZXhhbXBsZXMsIGhvdyB0byB1c2UgYm90aCBpbiBxdWVyaWVzIGFuZCBob3cgdG8gc2ltcGxpZnkgZGF0YWJhc2UgYWNjZXNzIHVzaW5nIHRoZSBjb25zdHJ1Y3QuCgojIyBTYW1wbGUgRGF0YWJhc2UKCkZvciBtYW55IG9mIHRoZSBleGFtcGxlcyB3ZSB3aWxsIHVzZSB0aGUgU2FraWxhIE1lZGlhIGFuZCBGaWxtIGRhdGFiYXNlLiBBbiBFUkQgb2YgdGhlIGRhdGFiYXNlIGlzIHNob3duIGJlbG93LiBUaGUgZXhhbXBsZXMgYXJlIGV4ZWN1dGVkIG9uIFNRTGl0ZSB1c2luZyBbc2FraWxhZGIuc3FsaXRlZGJdKHNha2lsYWRiLnNxbGl0ZWRiKVteMV0uCgpbXjFdOiBTYWtpbGEgRGF0YWJhc2UgaXMgbGljZW5zZWQgdW5kZXIgdGhlIE5ldyBCU0QgTGljZW5zZSBhbmQgbWF5IGJlIHVzZWQgZm9yIGFueSBwdXJwb3NlIHdpdGggdGhlIHByb3Zpc2lvbiB0aGF0IG5vdGljZSBvZiDCqSAyMDIwIE9yYWNsZSBDb3Jwb3JhdGlvbiBhcHBlYXJzLgoKIVtdKFNha2lsYURCLnBuZyl7d2lkdGg9IjcwJSJ9CgpgYGB7ciBvcGVuU2FraWxhREIsIGV2YWw9VCwgZWNobz1GfQpsaWJyYXJ5KFJTUUxpdGUpCmRiY29uIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgInNha2lsYS5zcWxpdGVkYiIpCmBgYAoKIyMgU3VicXVlcmllcwoKQSBzdWJxdWVyeSwgYWxzbyBrbm93biBhcyBhIG5lc3RlZCBxdWVyeSBvciBpbm5lciBxdWVyeSwgaXMgYW4gU1FMIHF1ZXJ5IHRoYXQgaXMgZW1iZWRkZWQgd2l0aGluIGFub3RoZXIgU1FMIHF1ZXJ5LiBJdCBjYW4gYmUgdXNlZCBpbiB2YXJpb3VzIHBhcnRzIG9mIGEgcXVlcnksIGluY2x1ZGluZyBgU0VMRUNUYCwgYEZST01gLCBgV0hFUkVgLCBhbmQgYEhBVklOR2AgY2xhdXNlcy4gU3VicXVlcmllcyBhcmUgcHJpbWFyaWx5IHVzZWQgZm9yIHBlcmZvcm1pbmcgb3BlcmF0aW9ucyB0aGF0IHJlcXVpcmUgbXVsdGlwbGUgc3RlcHMgaW4gZGF0YSBwcm9jZXNzaW5nLiBUaGV5IGFsbG93IGZvciBtb3JlIGZsZXhpYmxlIGFuZCBlZmZpY2llbnQgZGF0YSByZXRyaWV2YWwsIGVuYWJsaW5nIGNvbXBsZXggb3BlcmF0aW9ucyB0byBiZSBleGVjdXRlZCBpbiBhIHNpbmdsZSBxdWVyeS4KCiMjIyBCZW5lZml0cyBvZiBTdWJxdWVyaWVzCgpTdWJxdWVyaWVzIG9mZmVyIHNldmVyYWwgYmVuZWZpdHMsIGluY2x1ZGluZzoKCi0gICAqKkVuaGFuY2luZyBEYXRhIFJldHJpZXZhbCoqOiBTdWJxdWVyaWVzIHByb3ZpZGUgYSBwb3dlcmZ1bCB0b29sIGZvciByZXRyaWV2aW5nIGRhdGEgaW4gY29tcGxleCBzaXR1YXRpb25zIHdoZXJlIHRoZSB1c2Ugb2Ygc3RhbmRhcmQgU1FMIHF1ZXJpZXMgbWlnaHQgYmUgbGltaXRpbmcgb3IgaW5lZmZpY2llbnQuCi0gICAqKkNvbXBsZXggRGF0YSBSZWxhdGlvbnNoaXBzKio6IFRoZXkgYXJlIHBhcnRpY3VsYXJseSB1c2VmdWwgaW4gaGFuZGxpbmcgc2NlbmFyaW9zIHdoZXJlIGRhdGEgcmVsYXRpb25zaGlwcyBhcmUgY29tcGxleCBhbmQgcmVxdWlyZSBtdWx0aXBsZSBsYXllcnMgb2YgZmlsdGVyaW5nIG9yIHNlbGVjdGlvbi4KLSAgICoqTmVzdGVkIExvZ2ljKio6IFN1YnF1ZXJpZXMgb2ZmZXIgYSB3YXkgdG8gaW1wbGVtZW50IG5lc3RlZCBsb2dpYyBpbiBTUUwgcXVlcmllcywgYWxsb3dpbmcgZm9yIHNvcGhpc3RpY2F0ZWQgZGF0YSBhbmFseXNpcyBhbmQgbWFuaXB1bGF0aW9uLgoKIyMjIENoYXJhY3RlcmlzdGljcyBvZiBTdWJxdWVyaWVzCgotICAgKipSZXR1cm4gVHlwZXMqKjogU3VicXVlcmllcyBjYW4gcmV0dXJuIGluZGl2aWR1YWwgdmFsdWVzLCBhIHNpbmdsZSByb3csIG11bHRpcGxlIHJvd3MsIG9yIGV2ZW4gYSB0YWJsZS1saWtlIHN0cnVjdHVyZS4KLSAgICoqSW5kZXBlbmRlbnQgdnMuIENvcnJlbGF0ZWQqKjogVGhleSBjYW4gYmUgaW5kZXBlbmRlbnQgb2YgdGhlIG91dGVyIHF1ZXJ5IChzdGFuZGFsb25lKSBvciBjb3JyZWxhdGVkLCB3aGVyZSB0aGUgc3VicXVlcnkgZGVwZW5kcyBvbiBkYXRhIGZyb20gdGhlIG91dGVyIHF1ZXJ5LgotICAgKipQbGFjZW1lbnQgRmxleGliaWxpdHkqKjogU3VicXVlcmllcyBjYW4gYmUgdXNlZCBpbiB2YXJpb3VzIHBsYWNlcyB3aXRoaW4gYSBxdWVyeSwgc3VjaCBhczoKICAgIC0gICBJbiB0aGUgYFNFTEVDVGAgY2xhdXNlLCB0byBhZGQgY29sdW1ucyB0byB0aGUgbWFpbiBxdWVyeS4KICAgIC0gICBJbiB0aGUgYEZST01gIGNsYXVzZSwgdG8gY3JlYXRlIGEgZGVyaXZlZCB0YWJsZSB0aGF0IHRoZSBtYWluIHF1ZXJ5IGNhbiB1c2UuCiAgICAtICAgSW4gdGhlIGBXSEVSRWAgb3IgYEhBVklOR2AgY2xhdXNlcywgdG8gZmlsdGVyIHJlY29yZHMgYmFzZWQgb24gY29tcGxleCBjb25kaXRpb25zLgoKIyMjIEFkdmFudGFnZXMgYW5kIExpbWl0YXRpb25zCgotICAgKipBZHZhbnRhZ2VzKio6CiAgICAtICAgKipNb2R1bGFyaXR5Kio6IEJyZWFraW5nIGRvd24gY29tcGxleCBxdWVyaWVzIGludG8gc2ltcGxlciwgbW9yZSBtYW5hZ2VhYmxlIHBhcnRzLgogICAgLSAgICoqUmVhZGFiaWxpdHkqKjogSW1wcm92aW5nIHRoZSByZWFkYWJpbGl0eSBvZiBTUUwgc2NyaXB0cyBieSBlbmNhcHN1bGF0aW5nIHNwZWNpZmljIGxvZ2ljLgogICAgLSAgICoqUmV1c2FiaWxpdHkqKjogRmFjaWxpdGF0aW5nIHRoZSByZXVzZSBvZiBjb2RlIGFuZCBsb2dpYyB3aXRoaW4gdGhlIGRhdGFiYXNlLgotICAgKipMaW1pdGF0aW9ucyoqOgogICAgLSAgICoqUGVyZm9ybWFuY2UgQ29uY2VybnMqKjogTmVzdGVkIHF1ZXJpZXMgY2FuIHNvbWV0aW1lcyBsZWFkIHRvIHBlcmZvcm1hbmNlIGlzc3VlcywgcGFydGljdWxhcmx5IGlmIG5vdCBvcHRpbWl6ZWQgY29ycmVjdGx5LgogICAgLSAgICoqQ29tcGxleGl0eSoqOiBPdmVydXNlIG9yIG1pc3VzZSBvZiBzdWJxdWVyaWVzIGNhbiBtYWtlIFNRTCBzdGF0ZW1lbnRzIG1vcmUgY29tcGxleCBhbmQgaGFyZGVyIHRvIG1haW50YWluLgoKIyMjIFNpbXBsZSBFeGFtcGxlCgoqSG93IG1hbnkgcGF5bWVudHMgaGF2ZSBhIGJlbG93IGF2ZXJhZ2UgYW1vdW50PyoKClRoaXMgcXVlcnkgcmVxdWlyZXMgdXMgdG8ga25vdyB0aGUgYXZlcmFnZSBwYXltZW50IGFtb3VudCwgYnV0IHRoYXQgYW1vdW50IHZhcmllcyB3aXRoIHRoZSBkYXRhLCBzbyBpdCBjYW5ub3QgYmUgaGFyZCBjb2RlZDsgYSBzdWJxdWVyeSB0aGF0IGNhbGN1bGF0ZXMgdGhlIGFjY291bnQgYW5kIHVzZWQgd2hlcmUgdGhlIGF2ZXJhZ2UgaXMgbmVlZGVkIHJlc29sdmVzIHRoZSBpc3N1ZS4gT2YgY291cnNlLCBzaW5jZSBhdmVyYWdlIGlzIGEgc2NhbGFyIHZhbHVlLCB0aGUgc3VicXVlcnkgbXVzdCByZXR1cm4gb25lIHZhbHVlLCBidXQgYmVjYXVzZSB0aGUgcmVzdWx0IG11c3QgYmUgYSB0YWJsZSwgdGhlIHN1YnF1ZXJ5IGFjdHVhbGx5IHJldHVybnMgYSB0YWJsZSB3aXRoIG9uZSByb3cgYW5kIG9uZSBjb2x1bW4uCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259ClNFTEVDVCBDT1VOVChwYXltZW50X2lkKSBBUyBudW1HcnRBdmcKRlJPTSBQYXltZW50CldIRVJFIGFtb3VudCA8IChTRUxFQ1QgQVZHKGFtb3VudCkgRlJPTSBQYXltZW50KTsKYGBgCgpUaGlzIHNlY3Rpb24gbGF5cyB0aGUgZm91bmRhdGlvbiBmb3IgdW5kZXJzdGFuZGluZyBzdWJxdWVyaWVzLCBzZXR0aW5nIHRoZSBzdGFnZSBmb3IgbW9yZSBkZXRhaWxlZCBleHBsb3JhdGlvbiBpbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zIG9mIHRoZSBsZXNzb24uCgpFeHBhbmRpbmcgb24gdGhlIHR5cGVzIG9mIHN1YnF1ZXJpZXMgaW4gU1FMOgoKIyMgVHlwZXMgb2YgU3VicXVlcmllcwoKIyMjIFNpbmdsZS1Sb3cgU3VicXVlcmllcwoKKipEZWZpbml0aW9uKio6IFRoZXNlIHJldHVybiBhIHNpbmdsZSByb3cgZnJvbSB0aGUgaW5uZXIgcXVlcnkuIFRoZXkncmUgdHlwaWNhbGx5IHVzZWQgd2l0aCBzaW5nbGUtcm93IGNvbXBhcmlzb24gb3BlcmF0b3JzIGxpa2UgYD1gLCBgPmAsIGA8YCwgYDw9YCwgYD49YCwgYW5kIGA8PmAuCgoqKlVzZSBDYXNlcyoqOiBJZGVhbCBmb3Igc2l0dWF0aW9ucyB3aGVyZSB5b3UgZXhwZWN0IHRoZSBzdWJxdWVyeSB0byByZXR1cm4gZXhhY3RseSBvbmUgcm93LiBGb3IgaW5zdGFuY2UsIGZpbmRpbmcgYW4gZW1wbG95ZWUgd2hvc2Ugc2FsYXJ5IGlzIGVxdWFsIHRvIHRoZSBtYXhpbXVtIHNhbGFyeSBpbiBhIGRlcGFydG1lbnQgb3IgdGhlIHZhbHVlIG9mIGEgY29sdW1uIGlzIGFib3ZlIG9yIGJlbG93IGF2ZXJhZ2UuCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIHBheW1lbnRfaWQsIGFtb3VudCAKICBGUk9NIFBheW1lbnQgCiBXSEVSRSBhbW91bnQgPSAoU0VMRUNUIE1BWChhbW91bnQpIEZST00gUGF5bWVudCkKYGBgCgpUaGUgZXhhbXBsZSBhYm92ZSBmaW5kcyB0aG9zZSBwYXltZW50cyB0aGF0IGFyZSBmb3IgdGhlIGJpZ2dlc3QgKG1heGltdW0pIGFtb3VudC4KCiMjIyMgRXhlcmNpc2Ugey50YWJzZXR9CgpUcnkgdGhlIHF1ZXJ5IGJlbG93IGZvciB5b3Vyc2VsZiBvbiB0aGUgc2FtcGxlIGRhdGFiYXNlLgoKIyMjIyMgUXVlcnkgey5hY3RpdmV9CgoqV2hpY2ggY3VzdG9tZXIgKGxhc3QgbmFtZSwgZmlyc3QgbmFtZSwgYW5kIElEKSBwdXJjaGFzZWQgdGhlIG1vc3QgaW4gdGVybXMgb2YgdG90YWwgYW1vdW50PyoKCiMjIyMjIFNvbHV0aW9uCgpUQkQKCiMjIyBNdWx0aXBsZS1Sb3cgU3VicXVlcmllcwoKKipEZWZpbml0aW9uKio6IFRoZXNlIHJldHVybiBtdWx0aXBsZSByb3dzIGFuZCBhcmUgdXNlZCB3aXRoIGV4aXN0ZW50aWFsIG9yIHNldCBtZW1iZXJzaGlwIG9wZXJhdG9ycyBsaWtlIGBJTmAsIGBBTllgLCBgQUxMYCwgb3IgY29tcGFyaXNvbnMgY29tYmluZWQgd2l0aCBgQU5ZYCBvciBgQUxMYC4KCioqVXNlIENhc2VzKio6IFVzZWZ1bCB3aGVuIHRoZSBzdWJxdWVyeSBpcyBleHBlY3RlZCB0byByZXR1cm4gbW9yZSB0aGFuIG9uZSByb3cuIEZvciBleGFtcGxlLCByZXRyaWV2aW5nIGFsbCBvcmRlcnMgdGhhdCBtYXRjaCBhbnkgb2YgdGhlIG9yZGVyIElEcyBpbiBhIGxpc3QuCgoqKkFsdGVybmF0aXZlKio6IEEgc2ltcGxlIGlubmVyLWpvaW4gb3IgYSB0aGV0YS1qb2luIGNhbiBvZnRlbiBiZSB1c2VkIGluc3RlYWQuCgoqKkV4YW1wbGUqKjoKClRoZSBxdWVyeSBiZWxvdyBmaW5kcyBob3cgbWFueSBjaXRpZXMgYXJlIGxvY2F0ZWQgaW4gYSBjb3VudHJ5IHRoYXQgc3RhcnRzIHdpdGggYSAnQicuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259ClNFTEVDVCBjb3VudCgqKSBBUyBgTnVtQ2l0aWVzYAogIEZST00gQ2l0eSAKIFdIRVJFIGNvdW50cnlfaWQgSU4gCiAgICAoU0VMRUNUIGNvdW50cnlfaWQgCiAgICAgICBGUk9NIENvdW50cnkgCiAgICAgIFdIRVJFIGNvdW50cnkgTElLRSAnQiUnKQpgYGAKCiMjIyBDb3JyZWxhdGVkIFN1YnF1ZXJpZXMKCioqRGVmaW5pdGlvbioqOiBBIGNvcnJlbGF0ZWQgc3VicXVlcnkgaXMgb25lIHRoYXQgdXNlcyB2YWx1ZXMgZnJvbSB0aGUgb3V0ZXIgcXVlcnkuIEl0IGlzIGV4ZWN1dGVkIHJlcGVhdGVkbHksICpvbmNlIGZvciBlYWNoIHJvdyB0aGF0IG1pZ2h0IGJlIHNlbGVjdGVkIGJ5IHRoZSBvdXRlciBxdWVyeSouCgoqKlVzZSBDYXNlcyoqOiBFZmZlY3RpdmUgaW4gc2NlbmFyaW9zIHdoZXJlIGVhY2ggcm93IGluIHRoZSBvdXRlciBxdWVyeSBtYXkgbmVlZCBkaWZmZXJlbnQgcmVzdWx0cyBmcm9tIHRoZSBzdWJxdWVyeS4gRm9yIGluc3RhbmNlLCBmaW5kaW5nIGVtcGxveWVlcyB3aG8gZWFybiBtb3JlIHRoYW4gdGhlIGF2ZXJhZ2Ugc2FsYXJ5IGluIHRoZWlyIHJlc3BlY3RpdmUgZGVwYXJ0bWVudHMuCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIHN0YWZmX2lkLCBhbW91bnQKICBGUk9NIFBheW1lbnQgQVMgUAogV0hFUkUgYW1vdW50ID4gKFNFTEVDVCBBVkcoYW1vdW50KQogICAgICAgICAgICAgICAgICAgRlJPTSBQYXltZW50CiAgICAgICAgICAgICAgICAgIFdIRVJFIHN0YWZmX2lkID0gUC5zdGFmZl9pZCkKYGBgCgoqKlBlcmZvcm1hbmNlIE5vdGUqKjogQ29ycmVsYXRlZCBzdWJxdWVyaWVzIGNhbiBiZSBsZXNzIGVmZmljaWVudCBiZWNhdXNlIHRoZSBzdWJxdWVyeSBtYXkgbmVlZCB0byBiZSBleGVjdXRlZCBtdWx0aXBsZSB0aW1lcy4gQSBqb2luIHdpdGggYSAiZ3JvdXAgYnkiIGlzIG9mdGVuIHNpbXBsZXIgYW5kIHByZWZlcmFibGUgYXMgaXQgZXhlY3V0ZXMgZ2VuZXJhbGx5IG11Y2ggZmFzdGVyIGFuZCB0aGUgZGF0YWJhc2UgY2FuIHVzZSBpbmRleGVzLgoKIyMjIFN1YnF1ZXJ5IGFzIGEgRGVyaXZlZCBUYWJsZQoKKipEZWZpbml0aW9uKio6IFRoaXMgaW52b2x2ZXMgdXNpbmcgYSBzdWJxdWVyeSBpbiB0aGUgYEZST01gIGNsYXVzZSBvZiB0aGUgbWFpbiBxdWVyeS4gVGhlIHJlc3VsdCBvZiB0aGUgc3VicXVlcnkgYWN0cyBhcyBhIHRlbXBvcmFyeSB0YWJsZS4KCioqVXNlIENhc2VzKio6IFVzZWZ1bCBmb3IgY3JlYXRpbmcgY29tcGxleCBqb2lucyBhbmQgYWdncmVnYXRpb25zLiBGb3IgZXhhbXBsZSwgc3VtbWFyaXppbmcgZGF0YSBpbiBhIHN1YnF1ZXJ5IGJlZm9yZSBqb2luaW5nIGl0IHRvIGFub3RoZXIgdGFibGUuCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIEMuY3VzdG9tZXJfaWQsIEMubGFzdF9uYW1lLCBQLnRvdGFsX2Ftb3VudAogIEZST00gQ3VzdG9tZXIgQVMgQyBKT0lOIChTRUxFQ1QgY3VzdG9tZXJfaWQsIHN1bShhbW91bnQpIEFTIGB0b3RhbF9hbW91bnRgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBGUk9NIFBheW1lbnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgR1JPVVAgQlkgY3VzdG9tZXJfaWQpIEFTIFAKICAgICAgICAgICAgICAgICAgICAgT04gKEMuY3VzdG9tZXJfaWQgPSBQLmN1c3RvbWVyX2lkKQpgYGAKCk5vdGUgaG93IHRoZSBzdWJxdWVyeSB3YXMgcHJvdmlkZWQgd2l0aCBhbiBhbGlhcyB1c2luZyAqQVMqLgoKIyMjIEV4aXN0ZW50aWFsIFN1YnF1ZXJpZXMKCioqRGVmaW5pdGlvbioqOiBUaGVzZSBhcmUgdXNlZCB0byB0ZXN0IGZvciB0aGUgZXhpc3RlbmNlIG9mIHJvd3MgaW4gYSBzdWJxdWVyeS4KCioqVXNlIENhc2VzKio6IENvbW1vbmx5IHVzZWQgaW4gY29uZGl0aW9uYWwgbG9naWMsIGxpa2UgY2hlY2tpbmcgaWYgYSBjZXJ0YWluIGNvbmRpdGlvbiBpcyBtZXQgaW4gYW5vdGhlciB0YWJsZSBiZWZvcmUgcGVyZm9ybWluZyBhbiBvcGVyYXRpb24uCgoqKkV4YW1wbGUqKjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIERJU1RJTkNUIEEubGFzdF9uYW1lLCBBLmZpcnN0X25hbWUKICBGUk9NIEZpbG1fQWN0b3IgQVMgRkEgSk9JTiBBY3RvciBBUyBBIFVTSU5HIChhY3Rvcl9pZCkKIFdIRVJFIEVYSVNUUwogICAgICAgICAoU0VMRUNUICogRlJPTSBGaWxtIAogICAgICAgICAgIFdIRVJFIGZpbG1faWQgPSBGQS5maWxtX2lkIEFORCBsZW5ndGggPiAxODApCiBMSU1JVCAzOwpgYGAKCiMjIyBDb25zaWRlcmF0aW9ucwoKKipVbm5lY2Vzc2FyeSBDb21wbGV4aXR5Kio6IEJlIGNhcmVmdWwgbm90IHRvIG92ZXJ1c2Ugc3VicXVlcmllcywgYXMgdGhleSBjYW4gbWFrZSB0aGUgcXVlcnkgY29tcGxleCBhbmQgZGlmZmljdWx0IHRvIHVuZGVyc3RhbmQuCgoqKlN1YnF1ZXJ5IE9wdGltaXphdGlvbioqOiBJdCdzIGltcG9ydGFudCB0byB1bmRlcnN0YW5kIGhvdyBzdWJxdWVyaWVzIGNhbiBpbXBhY3QgcGVyZm9ybWFuY2UgYW5kIHRvIG9wdGltaXplIHRoZW0gYWNjb3JkaW5nbHkuIEZvciBleGFtcGxlLCBtaW5pbWl6aW5nIHRoZSBudW1iZXIgb2Ygcm93cyBwcm9jZXNzZWQgYnkgdXNpbmcgYXBwcm9wcmlhdGUgV0hFUkUgY2xhdXNlcy4KCioqTmVzdGVkIFN1YnF1ZXJpZXMqKjogU3VicXVlcmllcyBjYW4gYmUgbmVzdGVkIGluc2lkZSBvdGhlciBzdWJxdWVyaWVzLCBidXQgdGhpcyBzaG91bGQgYmUgZG9uZSBqdWRpY2lvdXNseSB0byBhdm9pZCBleGNlc3NpdmUgY29tcGxleGl0eSBhbmQgcG90ZW50aWFsIHBlcmZvcm1hbmNlIGlzc3Vlcy4gV2hpbGUgb2Z0ZW4gc2ltcGxlciBmb3Igc29tZSBwcm9ncmFtbWVycyB0byBleHByZXNzIGEgcXVlcnksIGpvaW5zIGFuZCBncm91cCBieSBhcmUgcHJlZmVyYWJsZSB3aGVuIGFuIG9wdGlvbi4KCioqTGluZWFyIFNjYW5zKio6IFN1YnF1ZXJpZXMgb2Z0ZW4gcmVzdWx0IGluIHF1ZXJ5IHBsYW5zIHRoYXQgdXNlIGxpbmVhciB0YWJsZSBzY2FucyByYXRoZXIgdGhhbiBhdmFpbGFibGUgaW5kZXhlcyBhbmQgYXJlIHRodXMgb2Z0ZW4gdmFzdGx5IGxlc3MgZWZmaWNpZW50LgoKKipJbmRlcGVuZGVudCBUZXN0aW5nKio6IFN1YnF1ZXJpZXMgc2hvdWxkIGJlIHRlc3RlZCBpbmRlcGVuZGVudGx5IHRvIGVuc3VyZSB0aGF0IHRoZSBjb3JyZWN0IHJlc3VsdHMgYXJlIHJldHVybmVkIGJlZm9yZSBpbmNvcnBvcmF0aW5nIHRoZW0gaW50byBvdGhlciBxdWVyaWVzLgoKRWFjaCB0eXBlIG9mIHN1YnF1ZXJ5IG9mZmVycyB1bmlxdWUgZnVuY3Rpb25hbGl0aWVzIGFuZCBpcyBzdWl0ZWQgZm9yIGRpZmZlcmVudCBzY2VuYXJpb3MgaW4gU1FMIHF1ZXJ5aW5nLiBVbmRlcnN0YW5kaW5nIHRoZXNlIHR5cGVzIHdpbGwgaGVscCBkYXRhIGFuYWx5c3RzIGVmZmVjdGl2ZWx5IHVzZSBzdWJxdWVyaWVzIHRvIHNvbHZlIGNvbXBsZXggZGF0YSByZXRyaWV2YWwgcHJvYmxlbXMuCgojIyBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMKClRoZSBTUUwgYFdJVEhgIHN0YXRlbWVudCwgYWxzbyBrbm93biBhcyBDb21tb24gVGFibGUgRXhwcmVzc2lvbnMgKENURXMpLCBpcyBhIHVzZWZ1bCBmZWF0dXJlIGZvciBjcmVhdGluZyB0ZW1wb3JhcnkgcmVzdWx0IHNldHMgdGhhdCBjYW4gYmUgcmVmZXJlbmNlZCB3aXRoaW4gYSBgU0VMRUNUYCwgYElOU0VSVGAsIGBVUERBVEVgLCBvciBgREVMRVRFYCBzdGF0ZW1lbnQuIENURXMgYXJlIHNpbWlsYXIgdG8gdGhlIM+BIChyZW5hbWUpIG9wZXJhdG9yIGluIHJlbGF0aW9uYWwgYWxnZWJyYS4KCiMjIyBCYXNpYyBTeW50YXgKClRoZSBiYXNpYyBzeW50YXggb2YgdGhlIGBXSVRIYCBzdGF0ZW1lbnQgaXMgYXMgZm9sbG93czoKCmBgYCBzcWwKV0lUSCBDVEVfTmFtZSBBUyAoCiAgICAtLSBZb3VyIFNRTCBxdWVyeSBoZXJlCikKClNFTEVDVCAqIEZST00gQ1RFX05hbWU7CmBgYAoKSGVyZSwgYENURV9OYW1lYCBpcyBhIHRlbXBvcmFyeSByZXN1bHQgc2V0IHRoYXQgeW91IGRlZmluZSB3aXRoaW4gdGhlIGBXSVRIYCBjbGF1c2UuIFRoaXMgcmVzdWx0IHNldCBjYW4gdGhlbiBiZSB1c2VkIGluIHRoZSBtYWluIFNRTCBxdWVyeSB0aGF0IGZvbGxvd3MgaW4gcGxhY2Ugb2YgYW55IHRhYmxlLgoKIyMjIEFkdmFudGFnZXMgb2YgVXNpbmcgYFdJVEhgCgoxLiAgKipSZWFkYWJpbGl0eSBhbmQgTWFpbnRlbmFuY2UqKjogQ1RFcyBjYW4gbWFrZSBjb21wbGV4IHF1ZXJpZXMgbW9yZSByZWFkYWJsZSBhbmQgbWFpbnRhaW5hYmxlIGJ5IGJyZWFraW5nIHRoZW0gaW50byBzaW1wbGVyLCBtb2R1bGFyIHBhcnRzLgoKMi4gICoqUmVjdXJzaXZlIFF1ZXJpZXMqKjogQ1RFcyBlbmFibGUgcmVjdXJzaXZlIHF1ZXJpZXMsIHdoaWNoIGFyZSB1c2VmdWwgZm9yIGhpZXJhcmNoaWNhbCBkYXRhIHByb2Nlc3NpbmcsIGxpa2UgdHJlZSBzdHJ1Y3R1cmVzLgoKMy4gICoqUGVyZm9ybWFuY2UgT3B0aW1pemF0aW9uKio6IEluIHNvbWUgY2FzZXMsIENURXMgY2FuIGltcHJvdmUgcGVyZm9ybWFuY2UgYnkgc2ltcGxpZnlpbmcgY29tcGxleCBqb2lucyBhbmQgZmlsdGVycy4KCjQuICAqKlJldXNhYmlsaXR5Kio6IEEgc2luZ2xlIENURSBjYW4gYmUgcmVmZXJlbmNlZCBtdWx0aXBsZSB0aW1lcyBpbiB0aGUgbWFpbiBxdWVyeSwgYXZvaWRpbmcgdGhlIHJlcGV0aXRpb24gb2YgdGhlIHNhbWUgc3VicXVlcnkuCgojIyMgRXhhbXBsZXMKClRoZSBTYWtpbGEgc2FtcGxlIGRhdGFiYXNlIGlzIGEgZ29vZCByZXNvdXJjZSBmb3IgZGVtb25zdHJhdGluZyByZWFsLXdvcmxkIGFwcGxpY2F0aW9ucyBvZiBDVEVzLiBIZXJlIGFyZSBhIGZldyB1c2UgY2FzZXM6CgojIyMjIDEuIFNpbXBsaWZ5aW5nIENvbXBsZXggSm9pbnMKClN1cHBvc2UgeW91IHdhbnQgdG8gZmluZCBmaWxtcyB3aXRoIHRoZSBoaWdoZXN0IHJlbnRhbCByYXRlcyBpbiBlYWNoIHJhdGluZyBjYXRlZ29yeS4gVGhpcyBtaWdodCBpbnZvbHZlIGNvbXBsZXggam9pbnMgYW5kIGFnZ3JlZ2F0aW9ucy4gVXNpbmcgYSBDVEUsIHlvdSBjYW4gc2ltcGxpZnkgaXQ6CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CldJVEggSGlnaGVzdFJlbnRhbFJhdGVzIEFTICgKICAgIFNFTEVDVCByYXRpbmcsIE1BWChyZW50YWxfcmF0ZSkgQVMgTWF4UmF0ZQogICAgRlJPTSBGaWxtCiAgICBHUk9VUCBCWSByYXRpbmcKKQoKU0VMRUNUIGYuZmlsbV9pZCwgZi50aXRsZSwgZi5yYXRpbmcsIGYucmVudGFsX3JhdGUKICBGUk9NIEZpbG0gZiBJTk5FUiBKT0lOIEhpZ2hlc3RSZW50YWxSYXRlcyBoIAogICAgICAgICAgICAgIE9OIChmLnJhdGluZyA9IGgucmF0aW5nIEFORCBmLnJlbnRhbF9yYXRlID0gaC5NYXhSYXRlKTsKYGBgCgojIyMjIDIuIFJlY3Vyc2l2ZSBRdWVyaWVzCgpJZiB0aGUgU2FraWxhIGRhdGFiYXNlIGhhZCBoaWVyYXJjaGljYWwgZGF0YSwgbGlrZSBhIHRhYmxlIHJlcHJlc2VudGluZyBmaWxtIGNhdGVnb3JpZXMgYW5kIHN1YmNhdGVnb3JpZXMsIHlvdSBjb3VsZCB1c2UgcmVjdXJzaXZlIENURXMgdG8gdHJhdmVyc2UgdGhpcyBoaWVyYXJjaHkuIEJ1dCBpdCdzIGltcG9ydGFudCB0byBub3RlIHRoYXQgdGhlIFNha2lsYSBkYXRhYmFzZSBkb2VzIG5vdCBpbmhlcmVudGx5IGluY2x1ZGUgaGllcmFyY2hpY2FsIGRhdGEgbGlrZSBjYXRlZ29yaWVzIGFuZCBzdWJjYXRlZ29yaWVzLiBGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyBleGFtcGxlLCBJJ2xsIGFzc3VtZSBzdWNoIGEgc3RydWN0dXJlIGV4aXN0cyBvciB5b3UncmUgd2lsbGluZyB0byBhZGFwdCB0aGUgZXhhbXBsZSB0byB5b3VyIHNwZWNpZmljIHVzZSBjYXNlLgoKQXNzdW1pbmcgd2UgaGF2ZSBhIHRhYmxlIGBmaWxtX2NhdGVnb3J5YCB3aXRoIGEgaGllcmFyY2hpY2FsIHN0cnVjdHVyZSAod2hpY2ggaXMgbm90IGFjdHVhbGx5IHByZXNlbnQgaW4gdGhlIFNha2lsYSBkYXRhYmFzZSksIHRoZSBxdWVyeSBtaWdodCBsb29rIGxpa2UgdGhpczoKCmBgYCBzcWwKV0lUSCBSRUNVUlNJVkUgU3ViQ2F0ZWdvcnkgQVMgKAogICAgU0VMRUNUIGNhdGVnb3J5X2lkLCBwYXJlbnRfY2F0ZWdvcnlfaWQKICAgICAgRlJPTSBmaWxtX2NhdGVnb3J5CiAgICAgV0hFUkUgcGFyZW50X2NhdGVnb3J5X2lkIElTIE5VTEwKICAgIAogICAgVU5JT04gQUxMCiAgICAKICAgIFNFTEVDVCBmYy5jYXRlZ29yeV9pZCwgc2MucGFyZW50X2NhdGVnb3J5X2lkCiAgICAgIEZST00gZmlsbV9jYXRlZ29yeSBmYyBJTk5FUiBKT0lOIFN1YkNhdGVnb3J5IHNjIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgT04gc2MucGFyZW50X2NhdGVnb3J5X2lkID0gc2MuY2F0ZWdvcnlfaWQKICAgICAgICAgICAgICAgICAgICAgICAgICAgIAopCgpTRUxFQ1QgKiBGUk9NIFN1YkNhdGVnb3J5OwpgYGAKCiMjIyMgMy4gQW5hbHl6aW5nIFBhdHRlcm5zCgpMZXQncyBzYXkgeW91IHdhbnQgdG8gYW5hbHl6ZSByZW50YWwgcGF0dGVybnMsIGxpa2UgZmluZGluZyBjdXN0b21lcnMgd2hvIHJlbnRlZCBtb3JlIHRoYW4gZml2ZSBtb3ZpZXMgaW4gYSBtb250aC4gQSBDVEUgY2FuIGJlIHVzZWQgdG8gYWdncmVnYXRlIHJlbnRhbCBkYXRhIGJlZm9yZSBqb2luaW5nIGl0IHdpdGggY3VzdG9tZXIgZGF0YToKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KV0lUSCBNb250aGx5UmVudGFscyBBUyAoCiAgICBTRUxFQ1QgY3VzdG9tZXJfaWQsIHN0cmZ0aW1lKCclbScsIHJlbnRhbF9kYXRlKSBBUyByZW50YWxfbW9udGgsIENPVU5UKCopIEFTIHRvdGFsX3JlbnRhbHMKICAgICAgRlJPTSByZW50YWwKICAgICBHUk9VUCBCWSBjdXN0b21lcl9pZCwgc3RyZnRpbWUoJyVtJywgcmVudGFsX2RhdGUpCiAgICBIQVZJTkcgQ09VTlQoKikgPiA1CikKU0VMRUNUIGMuY3VzdG9tZXJfaWQsIGMuZmlyc3RfbmFtZSwgYy5sYXN0X25hbWUsIG1yLnJlbnRhbF9tb250aCwgbXIudG90YWxfcmVudGFscwogIEZST00gY3VzdG9tZXIgYwogICAgICAgSk9JTiBNb250aGx5UmVudGFscyBtciBPTiBjLmN1c3RvbWVyX2lkID0gbXIuY3VzdG9tZXJfaWQ7CgpgYGAKClNRTGl0ZSBoYXMgc2xpZ2h0bHkgZGlmZmVyZW50IHN5bnRheCBhbmQgY2FwYWJpbGl0aWVzIGNvbXBhcmVkIHRvIG90aGVyIFNRTCBkYXRhYmFzZXMgbGlrZSBPcmFjbGUncyB2ZXJzaW9uIHVzZWQgaW4gU2FraWxhLgoKSW4gU1FMaXRlLCB0aGUgYEVYVFJBQ1RgIGZ1bmN0aW9uIGlzbid0IGF2YWlsYWJsZSwgc28gd2UnbGwgdXNlIGBzdHJmdGltZWAgdG8gZXh0cmFjdCB0aGUgbW9udGggZnJvbSB0aGUgKnJlbnRhbF9kYXRlKi4KCiMjIyBDb25zaWRlcmF0aW9ucwoKSW4gc3VtbWFyeSwgdGhlIFNRTCBgV0lUSGAgc3RhdGVtZW50IGlzIGEgdmVyc2F0aWxlIHRvb2wgaW4gdGhlIFNRTCBkZXZlbG9wZXIncyBhcnNlbmFsLCBlbmFibGluZyB0aGUgY3JlYXRpb24gb2YgbW9yZSByZWFkYWJsZSwgbWFpbnRhaW5hYmxlLCBhbmQgb2Z0ZW4gbW9yZSBlZmZpY2llbnQgU1FMIHF1ZXJpZXMuIFRoZSBleGFtcGxlcyB1c2luZyB0aGUgU2FraWxhIGZpbG0gZGF0YWJhc2UgaWxsdXN0cmF0ZSBob3cgQ1RFcyBjYW4gYmUgdXNlZCBmb3Igc2ltcGxpZnlpbmcgY29tcGxleCBxdWVyaWVzLCB3b3JraW5nIHdpdGggcmVjdXJzaXZlIGRhdGEsIGFuZCBwZXJmb3JtaW5nIGRldGFpbGVkIGRhdGEgYW5hbHlzaXMuCgojIyBQYXJ0aXRpb25zCgpUaGUgYFBBUlRJVElPTiBCWWAgY2xhdXNlIGluIFNRTCBpcyBhIHBvd2VyZnVsIGZlYXR1cmUgdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHdpbmRvdyBmdW5jdGlvbnMuIEl0IGFsbG93cyB5b3UgdG8gcGFydGl0aW9uIGEgcmVzdWx0IHNldCBpbnRvIGdyb3VwcyBhbmQgcGVyZm9ybSBjYWxjdWxhdGlvbnMgb3IgYXBwbHkgZnVuY3Rpb25zIHRvIGVhY2ggZ3JvdXAgKHBhcnRpdGlvbikgaW5kZXBlbmRlbnRseS4gSGVyZSdzIGEgZGV0YWlsZWQgZXhwbGFuYXRpb246CgojIyMgMS4gUHVycG9zZQoKLSAgICoqUHVycG9zZSoqOiBgUEFSVElUSU9OIEJZYCBkaXZpZGVzIHRoZSByZXN1bHQgc2V0IGludG8gcGFydGl0aW9ucyB0byB3aGljaCB0aGUgd2luZG93IGZ1bmN0aW9uIGlzIGFwcGxpZWQuIEluc3RlYWQgb2YgdHJlYXRpbmcgdGhlIGVudGlyZSByZXN1bHQgc2V0IGFzIGEgc2luZ2xlIGdyb3VwLCBpdCBlbmFibGVzIGNhbGN1bGF0aW9ucyBhY3Jvc3MgYSBzZXQgb2Ygcm93cyB0aGF0IHNoYXJlIGNvbW1vbiBwcm9wZXJ0aWVzLgoKLSAgICoqVXNlIHdpdGggV2luZG93IEZ1bmN0aW9ucyoqOiBJdCdzIHVzZWQgd2l0aCB3aW5kb3cgZnVuY3Rpb25zIGxpa2UgYFJPV19OVU1CRVIoKWAsIGBSQU5LKClgLCBgU1VNKClgLCBgQVZHKClgLCBldGMuIFRoZXNlIGZ1bmN0aW9ucyBwZXJmb3JtIGEgY2FsY3VsYXRpb24gYWNyb3NzIGEgc2V0IG9mIHRhYmxlIHJvd3MgcmVsYXRlZCB0byB0aGUgY3VycmVudCByb3cuCgojIyMgMi4gVXNlIENhc2VzCgotICAgKipEYXRhIEFuYWx5c2lzKio6IEZvciBhbmFseXppbmcgdHJlbmRzIHdpdGhpbiBzdWJncm91cHMgb2YgZGF0YS4gRm9yIGluc3RhbmNlLCBmaW5kaW5nIHRoZSBhdmVyYWdlIHNhbGVzIHBlciBkZXBhcnRtZW50IGluIGEgc3RvcmUuCi0gICAqKlJhbmtpbmcqKjogQXNzaWduaW5nIHJhbmtzIHdpdGhpbiBjYXRlZ29yaWVzLCBzdWNoIGFzIHJhbmtpbmcgc3R1ZGVudHMgYnkgc2NvcmUgd2l0aGluIGVhY2ggY2xhc3MuCi0gICAqKlJ1bm5pbmcgVG90YWxzKio6IENhbGN1bGF0aW5nIHJ1bm5pbmcgdG90YWxzLCBhdmVyYWdlcywgb3Igb3RoZXIgY3VtdWxhdGl2ZSBtZWFzdXJlcyB3aXRoaW4gZWFjaCBwYXJ0aXRpb24uCi0gICAqKlNlZ21lbnRhdGlvbioqOiBTZWdtZW50aW5nIGRhdGEgaW50byBncm91cHMgZm9yIG1vcmUgZm9jdXNlZCBhbmFseXNpcywgbGlrZSBtb250aGx5IGV4cGVuc2VzIHBlciBkZXBhcnRtZW50LgoKIyMjIDMuIEJlbmVmaXRzCgotICAgKipJbXByb3ZlZCBEYXRhIE9yZ2FuaXphdGlvbioqOiBJdCBlbmFibGVzIG1vcmUgZ3JhbnVsYXIgZGF0YSBhbmFseXNpcyB3aXRob3V0IHRoZSBuZWVkIGZvciBjb21wbGV4IHN1YnF1ZXJpZXMgb3IgdGVtcG9yYXJ5IHRhYmxlcy4KLSAgICoqRWZmaWNpZW5jeSoqOiBNb3JlIGVmZmljaWVudCB0aGFuIHNlbGYtam9pbnMgb3Igc3VicXVlcmllcyBmb3IgY2VydGFpbiB0eXBlcyBvZiBjYWxjdWxhdGlvbnMuCi0gICAqKkZsZXhpYmlsaXR5Kio6IE9mZmVycyBmbGV4aWJpbGl0eSBpbiBkYXRhIGFuYWx5c2lzLCBhbGxvd2luZyBmb3IgY29tcGxleCBjYWxjdWxhdGlvbnMgYWNyb3NzIGRpZmZlcmVudCBzZWdtZW50cyBvZiBkYXRhLgoKIyMjIDQuIExpbWl0YXRpb25zCgotICAgKipQZXJmb3JtYW5jZSoqOiBPbiBsYXJnZSBkYXRhc2V0cywgcGFydGl0aW9uaW5nIGNhbiBsZWFkIHRvIHBlcmZvcm1hbmNlIGlzc3VlcyBpZiBub3QgaW5kZXhlZCBvciBvcHRpbWl6ZWQgcHJvcGVybHkuCi0gICAqKkNvbXBsZXhpdHkqKjogVGhlIHN5bnRheCBjYW4gYmUgY29tcGxleCwgZXNwZWNpYWxseSBmb3IgYmVnaW5uZXJzIG9yIGZvciBjb21wbGV4IGFuYWx5dGljYWwgcXVlcmllcy4KLSAgICoqRGF0YWJhc2UgU3VwcG9ydCoqOiBOb3QgYWxsIGRhdGFiYXNlcyBzdXBwb3J0IGFsbCB3aW5kb3cgZnVuY3Rpb25zLCBhbmQgdGhlcmUgY2FuIGJlIGRpZmZlcmVuY2VzIGluIGltcGxlbWVudGF0aW9uLgoKIyMjIDUuIEV4YW1wbGVzCgpBc3N1bWluZyB0aGUgc2FtZSBTYWtpbGEgZGF0YWJhc2UgYW5kIFNRTGl0ZSwgbGV0J3MgY29uc2lkZXIgc29tZSBleGFtcGxlczoKCiMjIyMgRXhhbXBsZSAxOiBSYW5raW5nIEN1c3RvbWVycyBieSBBbW91bnQgU3BlbnQKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIGN1c3RvbWVyX2lkLCBhbW91bnQsCiAgICAgICBSQU5LKCkgT1ZFUiAoUEFSVElUSU9OIEJZIGN1c3RvbWVyX2lkIE9SREVSIEJZIGFtb3VudCBERVNDKSBhcyByYW5rCkZST00gcGF5bWVudApMSU1JVCA1OwpgYGAKCi0gICAqKkRlc2NyaXB0aW9uKio6IFRoaXMgcmFua3MgY3VzdG9tZXJzIGJhc2VkIG9uIHRoZSBhbW91bnQgc3BlbnQsIHBhcnRpdGlvbmluZyBieSBgY3VzdG9tZXJfaWRgLiBUaGUgTElNSVQgaXMgYXJiaXRyYXJ5IGFuZCBvbmx5IHNlcnZlcyB0byByZWR1Y2UgdGhlIHJlc3VsdCBzZXQgZm9yIGRpc3BsYXkgaW4gdGhpcyBsZXNzb24uCgojIyMjIEV4YW1wbGUgMjogUnVubmluZyBUb3RhbCBvZiBQYXltZW50cyBieSBDdXN0b21lcgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpTRUxFQ1QgY3VzdG9tZXJfaWQsIHBheW1lbnRfZGF0ZSwgYW1vdW50LAogICAgICAgU1VNKGFtb3VudCkgT1ZFUiAoUEFSVElUSU9OIEJZIGN1c3RvbWVyX2lkIE9SREVSIEJZIHBheW1lbnRfZGF0ZSkgYXMgcnVubmluZ190b3RhbApGUk9NIHBheW1lbnQKYGBgCgotICAgKipEZXNjcmlwdGlvbioqOiBDYWxjdWxhdGVzIGEgcnVubmluZyB0b3RhbCBvZiBwYXltZW50cyBmb3IgZWFjaCBjdXN0b21lciBvdmVyIHRpbWUuCgojIyMgQ29uY2x1c2lvbgoKYFBBUlRJVElPTiBCWWAgaXMgYSB2ZXJzYXRpbGUgdG9vbCBpbiBTUUwgdGhhdCBlbmhhbmNlcyBkYXRhIGFuYWx5c2lzIGNhcGFiaWxpdGllcyBieSBlbmFibGluZyBzZWdtZW50LXNwZWNpZmljIGNhbGN1bGF0aW9ucy4gV2hpbGUgaXQgaGFzIGxpbWl0YXRpb25zLCBwYXJ0aWN1bGFybHkgaW4gcGVyZm9ybWFuY2UgYW5kIGNvbXBsZXhpdHksIGl0cyBiZW5lZml0cyBpbiBvcmdhbml6aW5nIGFuZCBwcm9jZXNzaW5nIGRhdGEgZm9yIGFuYWx5dGljYWwgcXVlcmllcyBhcmUgc2lnbmlmaWNhbnQuCgojIyMgUEFSVElPTiBCWSB2cyBHUk9VUCBCWQoKYFBBUlRJVElPTiBCWWAgYW5kIGBHUk9VUCBCWWAgYXJlIGJvdGggU1FMIGNsYXVzZXMgdXNlZCB0byBvcmdhbml6ZSBkYXRhIGludG8gc3Vic2V0cywgYnV0IHRoZXkgc2VydmUgZGlmZmVyZW50IHB1cnBvc2VzIGFuZCBhcmUgdXNlZCBpbiBkaWZmZXJlbnQgY29udGV4dHMuCgojIyMgYFBBUlRJVElPTiBCWWAKCi0gICAqKkNvbnRleHQqKjogVXNlZCB3aXRoIHdpbmRvdyBmdW5jdGlvbnMuCi0gICAqKkZ1bmN0aW9uYWxpdHkqKjogSXQgb3JnYW5pemVzIGRhdGEgaW50byBwYXJ0aXRpb25zLCBhbGxvd2luZyB5b3UgdG8gcGVyZm9ybSBjYWxjdWxhdGlvbnMgYWNyb3NzIGVhY2ggcGFydGl0aW9uLiBFYWNoIHBhcnRpdGlvbiBjYW4gYmUgY29uc2lkZXJlZCBhICd3aW5kb3cnIG92ZXIgd2hpY2ggU1FMIGZ1bmN0aW9ucyBvcGVyYXRlLgotICAgKipSb3cgUmV0ZW50aW9uKio6IEl0IGRvZXMgbm90IGNoYW5nZSB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIHF1ZXJ5J3MgcmVzdWx0LiBFYWNoIHJvdyBpbiB0aGUgb3JpZ2luYWwgZGF0YXNldCByZW1haW5zIGluIHRoZSBvdXRwdXQsIGFsb25nIHdpdGggYW55IGFkZGl0aW9uYWwgY29sdW1ucyBjcmVhdGVkIGJ5IHRoZSB3aW5kb3cgZnVuY3Rpb24uCi0gICAqKlVzZSBDYXNlcyoqOiBDb21tb25seSB1c2VkIGZvciByYW5raW5nLCBjYWxjdWxhdGluZyBydW5uaW5nIHRvdGFscywgbW92aW5nIGF2ZXJhZ2VzLCBhbmQgb3RoZXIgY29tcGxleCBhbmFseXRpY3MgdGhhdCByZXF1aXJlIGFjY2VzcyB0byBtdWx0aXBsZSByb3dzIG9mIHRoZSBkYXRhc2V0IGF0IG9uY2UsIHdpdGhvdXQgZ3JvdXBpbmcgdGhlbSBpbnRvIGEgc2luZ2xlIG91dHB1dCByb3cuCgojIyMjIEV4YW1wbGUgb2YgYFBBUlRJVElPTiBCWWAKCmBgYCBzcWwKU0VMRUNUIGlkLCBkZXBhcnRtZW50LCBzYWxhcnksCiAgICAgICBBVkcoc2FsYXJ5KSBPVkVSIChQQVJUSVRJT04gQlkgZGVwYXJ0bWVudCkgYXMgYXZnX2RlcGFydG1lbnRfc2FsYXJ5CkZST00gZW1wbG95ZWVzOwpgYGAKCkhlcmUsIGBQQVJUSVRJT04gQllgIGNhbGN1bGF0ZXMgdGhlIGF2ZXJhZ2Ugc2FsYXJ5IHBlciBkZXBhcnRtZW50LCBidXQgZXZlcnkgcm93IGZyb20gdGhlIG9yaWdpbmFsIGBlbXBsb3llZXNgIHRhYmxlIGlzIHJldGFpbmVkIGluIHRoZSBvdXRwdXQuCgojIyMgYEdST1VQIEJZYAoKLSAgICoqQ29udGV4dCoqOiBVc2VkIGluIGFnZ3JlZ2F0ZSBxdWVyaWVzLgotICAgKipGdW5jdGlvbmFsaXR5Kio6IEl0IGdyb3VwcyByb3dzIHRoYXQgaGF2ZSB0aGUgc2FtZSB2YWx1ZXMgaW4gc3BlY2lmaWVkIGNvbHVtbnMgaW50byBzdW1tYXJ5IHJvd3MsIGxpa2UgImZpbmQgdGhlIHRvdGFsIHNhbGFyeSBwZXIgZGVwYXJ0bWVudCIuCi0gICAqKlJvdyBSZWR1Y3Rpb24qKjogSXQgcmVkdWNlcyB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIHJlc3VsdCwgcHJvdmlkaW5nIG9uZSByb3cgcGVyIGdyb3VwLiBZb3UgY2FuIG9ubHkgc2VsZWN0IHRoZSBncm91cGVkIGNvbHVtbnMgYW5kIHRoZSByZXN1bHRzIG9mIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMuCi0gICAqKlVzZSBDYXNlcyoqOiBJZGVhbCBmb3Igc3VtbWFyaWVzLCBzdWNoIGFzIHRvdGFscywgYXZlcmFnZXMsIGNvdW50cywgbWluLCBtYXgsIGV0Yy4sIHdoZXJlIHlvdSBuZWVkIGEgc2luZ2xlIHJlc3VsdCByb3cgcGVyIGdyb3VwLgoKIyMjIyBFeGFtcGxlIG9mIGBHUk9VUCBCWWAKCmBgYCBzcWwKU0VMRUNUIGRlcGFydG1lbnQsIEFWRyhzYWxhcnkpIGFzIGF2Z19kZXBhcnRtZW50X3NhbGFyeQpGUk9NIGVtcGxveWVlcwpHUk9VUCBCWSBkZXBhcnRtZW50OwpgYGAKCkhlcmUsIGBHUk9VUCBCWWAgcHJvdmlkZXMgdGhlIGF2ZXJhZ2Ugc2FsYXJ5IHBlciBkZXBhcnRtZW50LCBidXQgdGhlIG91dHB1dCB3aWxsIG9ubHkgaGF2ZSBvbmUgcm93IHBlciBkZXBhcnRtZW50LgoKIyMjIEtleSBEaWZmZXJlbmNlcwoKMS4gICoqUm93IENvdW50Kio6IGBQQVJUSVRJT04gQllgIHJldGFpbnMgdGhlIG9yaWdpbmFsIHJvdyBjb3VudCwgd2hlcmVhcyBgR1JPVVAgQllgIGNvbnNvbGlkYXRlcyByb3dzIGludG8gZ3JvdXBzLCByZWR1Y2luZyB0aGUgcm93IGNvdW50LgoyLiAgKipVc2Ugd2l0aCBGdW5jdGlvbnMqKjogYFBBUlRJVElPTiBCWWAgaXMgdXNlZCB3aXRoIHdpbmRvdyBmdW5jdGlvbnMsIHdoaWxlIGBHUk9VUCBCWWAgaXMgdXNlZCB3aXRoIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMuCjMuICAqKlJlc3VsdCBTZXQqKjogYFBBUlRJVElPTiBCWWAgYWRkcyBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIHRvIGVhY2ggcm93LCB3aGVyZWFzIGBHUk9VUCBCWWAgcHJvdmlkZXMgYSBzdW1tYXJpemVkIHJlc3VsdCBzZXQuCjQuICAqKlNjb3BlIG9mIENhbGN1bGF0aW9uKio6IEluIGBQQVJUSVRJT04gQllgLCBjYWxjdWxhdGlvbnMgYXJlIGRvbmUgd2l0aGluIGVhY2ggcGFydGl0aW9uIGFuZCBkb24ndCBhZmZlY3Qgb3RoZXIgcGFydGl0aW9ucy4gSW4gYEdST1VQIEJZYCwgY2FsY3VsYXRpb25zIGFyZSBkb25lIG9uIGdyb3VwZWQgZGF0YSwgcHJvZHVjaW5nIGEgc2luZ2xlIHJlc3VsdCBmb3IgZWFjaCBncm91cC4KCiMjIyBFeGFtcGxlcwoKTGV0J3MgdXNlIHRoZSBTYWtpbGEgZGF0YWJhc2Ugd2l0aCBhIGZvY3VzIG9uIGEgc2NlbmFyaW8gd2hlcmUgd2UgbWlnaHQgd2FudCB0byBhbmFseXplIHRoZSBmaWxtIHJlbnRhbCBkYXRhLiBXZSB3aWxsIGNvbXBhcmUgdGhlIHVzYWdlIG9mIGBQQVJUSVRJT04gQllgIGFuZCBgR1JPVVAgQllgIGluIFNRTCBxdWVyaWVzIHVzaW5nIHRoaXMgY29udGV4dC4gTm90ZSB0aGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIFNha2lsYSBkYXRhYmFzZSBtaWdodCBkaWZmZXIgc2xpZ2h0bHkgYmFzZWQgb24gdGhlIGFkYXB0YXRpb24gZm9yIFNRTGl0ZSwgYnV0IHRoZSBnZW5lcmFsIGNvbmNlcHQgcmVtYWlucyB0aGUgc2FtZS4KCiMjIyBTY2VuYXJpbzogQW5hbHl6aW5nIEZpbG0gUmVudGFscwoKV2Ugd2FudCB0byBhbmFseXplIGZpbG0gcmVudGFscywgbG9va2luZyBhdCBib3RoIHRoZSB0b3RhbCBudW1iZXIgb2YgcmVudGFscyBwZXIgY2F0ZWdvcnkgYW5kIHRoZSByYW5rIG9mIGVhY2ggZmlsbSB3aXRoaW4gaXRzIGNhdGVnb3J5IGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gcmVudGVkLgoKIyMjIDEuIFVzaW5nIGBHUk9VUCBCWWAKCioqT2JqZWN0aXZlKio6IFRvIGZpbmQgdGhlIHRvdGFsIG51bWJlciBvZiByZW50YWxzIGZvciBlYWNoIGZpbG0gY2F0ZWdvcnkuCgojIyMjIFNRTCBRdWVyeToKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIGNhdGVnb3J5Lm5hbWUgQVMgY2F0ZWdvcnlfbmFtZSwgQ09VTlQocmVudGFsLnJlbnRhbF9pZCkgQVMgdG90YWxfcmVudGFscwogIEZST00gY2F0ZWdvcnkKICAgICAgIEpPSU4gZmlsbV9jYXRlZ29yeSBPTiBjYXRlZ29yeS5jYXRlZ29yeV9pZCA9IGZpbG1fY2F0ZWdvcnkuY2F0ZWdvcnlfaWQKICAgICAgIEpPSU4gZmlsbSBPTiBmaWxtX2NhdGVnb3J5LmZpbG1faWQgPSBmaWxtLmZpbG1faWQKICAgICAgIEpPSU4gaW52ZW50b3J5IE9OIGZpbG0uZmlsbV9pZCA9IGludmVudG9yeS5maWxtX2lkCiAgICAgICBKT0lOIHJlbnRhbCBPTiBpbnZlbnRvcnkuaW52ZW50b3J5X2lkID0gcmVudGFsLmludmVudG9yeV9pZAogR1JPVVAgQlkgY2F0ZWdvcnkubmFtZTsKYGBgCgoqKkV4cGxhbmF0aW9uKio6IC0gVGhpcyBxdWVyeSBncm91cHMgZmlsbXMgYnkgdGhlaXIgY2F0ZWdvcnkgYW5kIGNvdW50cyB0aGUgdG90YWwgbnVtYmVyIG9mIHJlbnRhbHMgZm9yIGVhY2ggY2F0ZWdvcnkuIC0gVGhlIHJlc3VsdCB3aWxsIGJlIGEgc3VtbWFyeSB0YWJsZSB3aXRoIG9uZSByb3cgcGVyIGNhdGVnb3J5LCBzaG93aW5nIHRoZSB0b3RhbCByZW50YWxzLgoKIyMjIDIuIFVzaW5nIGBQQVJUSVRJT04gQllgCgoqKk9iamVjdGl2ZSoqOiBUbyByYW5rIGVhY2ggZmlsbSB3aXRoaW4gaXRzIGNhdGVnb3J5IGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgdGltZXMgaXQgaGFzIGJlZW4gcmVudGVkLgoKIyMjIyBTUUwgUXVlcnk6CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259ClNFTEVDVCBmaWxtLnRpdGxlLCBjYXRlZ29yeS5uYW1lIEFTIGNhdGVnb3J5X25hbWUsIAogICAgICAgUkFOSygpIE9WRVIgKFBBUlRJVElPTiBCWSBjYXRlZ29yeS5uYW1lIE9SREVSIEJZIENPVU5UKHJlbnRhbC5yZW50YWxfaWQpIERFU0MpIEFTIHJlbnRhbF9yYW5rCiAgRlJPTSBjYXRlZ29yeQogICAgICAgSk9JTiBmaWxtX2NhdGVnb3J5IE9OIGNhdGVnb3J5LmNhdGVnb3J5X2lkID0gZmlsbV9jYXRlZ29yeS5jYXRlZ29yeV9pZAogICAgICAgSk9JTiBmaWxtIE9OIGZpbG1fY2F0ZWdvcnkuZmlsbV9pZCA9IGZpbG0uZmlsbV9pZAogICAgICAgSk9JTiBpbnZlbnRvcnkgT04gZmlsbS5maWxtX2lkID0gaW52ZW50b3J5LmZpbG1faWQKICAgICAgIEpPSU4gcmVudGFsIE9OIGludmVudG9yeS5pbnZlbnRvcnlfaWQgPSByZW50YWwuaW52ZW50b3J5X2lkCiBHUk9VUCBCWSBmaWxtLnRpdGxlLCBjYXRlZ29yeS5uYW1lOwpgYGAKCioqRXhwbGFuYXRpb24qKjogLSBUaGlzIHF1ZXJ5IHJhbmtzIGZpbG1zIHdpdGhpbiBlYWNoIGNhdGVnb3J5IGJhc2VkIG9uIHRoZWlyIHJlbnRhbCBjb3VudC4gLSBUaGUgYFBBUlRJVElPTiBCWWAgY2xhdXNlIGlzIHVzZWQgdG8gY3JlYXRlIHBhcnRpdGlvbnMgZm9yIGVhY2ggZmlsbSBjYXRlZ29yeSwgYW5kIHdpdGhpbiBlYWNoIHBhcnRpdGlvbiwgZmlsbXMgYXJlIHJhbmtlZCBieSB0aGVpciByZW50YWwgY291bnRzLiAtIFVubGlrZSBgR1JPVVAgQllgLCB0aGlzIHF1ZXJ5IGRvZXMgbm90IHJlZHVjZSB0aGUgbnVtYmVyIG9mIHJvd3MgaW4gdGhlIG91dHB1dC4gRWFjaCBmaWxtIHJldGFpbnMgaXRzIHJvdywgYnV0IHdpdGggYWRkaXRpb25hbCBpbmZvcm1hdGlvbiAocmFuaykgYWRkZWQuCgojIyMgS2V5IERpZmZlcmVuY2VzIElsbHVzdHJhdGVkOgoKLSAgICoqQWdncmVnYXRpb24gdnMuIFdpbmRvd2luZyoqOiBUaGUgYEdST1VQIEJZYCBxdWVyeSBhZ2dyZWdhdGVzIGRhdGEsIHJlZHVjaW5nIHRoZSByZXN1bHQgdG8gb25lIHJvdyBwZXIgY2F0ZWdvcnksIHdoaWxlIHRoZSBgUEFSVElUSU9OIEJZYCBxdWVyeSBhcHBsaWVzIGEgd2luZG93IGZ1bmN0aW9uIHdpdGhpbiBlYWNoIGNhdGVnb3J5LCByZXRhaW5pbmcgaW5kaXZpZHVhbCBmaWxtIHJvd3MuCi0gICAqKk91dHB1dCoqOiBgR1JPVVAgQllgIHByb2R1Y2VzIGEgc3VtbWFyeSB0YWJsZSwgd2hlcmVhcyBgUEFSVElUSU9OIEJZYCBhZGRzIGFkZGl0aW9uYWwgcmFua2luZyBpbmZvcm1hdGlvbiB0byBlYWNoIGZpbG0gd2l0aGluIGl0cyBjYXRlZ29yeS4KClRoZXNlIGV4YW1wbGVzIGhpZ2hsaWdodCBob3cgYEdST1VQIEJZYCBpcyB1c2VkIGZvciBhZ2dyZWdhdGluZyBkYXRhIGludG8gc3VtbWFyeSBmb3JtLCB3aGVyZWFzIGBQQVJUSVRJT04gQllgIGlzIHVzZWQgZm9yIHBlcmZvcm1pbmcgY2FsY3VsYXRpb25zIHdpdGhpbiBzdWJzZXRzIG9mIGRhdGEgd2hpbGUgcmV0YWluaW5nIHRoZSBvcmlnaW5hbCBkYXRhIHN0cnVjdHVyZS4KCiMjIFN1bW1hcnkKClRoaXMgbGVzc29uIGNvdmVyZWQgdGhlIHVzZSBvZiBzdWJxdWVyaWVzIGFuZCB0aGUgYFdJVEhgIHN0YXRlbWVudCBpbiBTUUwgZm9yIGZvcm11bGF0aW5nIGNvbXBsZXggcXVlcmllcywgYW5kIHRoZSB1c2Ugb2YgYFBBUlRJVElPTiBCWWAgdG8gYXBwbHkgd2luZG93IGZ1bmN0aW9ucyB0byBncm91cHMgb2YgZGF0YSwgdXNpbmcgdGhlIFNha2lsYSBNZWRpYSBhbmQgRmlsbSBkYXRhYmFzZSBmb3IgZXhhbXBsZXMuCgojIyMgU3VicXVlcmllcwoKLSAgICoqRGVmaW5pdGlvbioqOiBTdWJxdWVyaWVzIGFyZSBTUUwgcXVlcmllcyBlbWJlZGRlZCB3aXRoaW4gYW5vdGhlciBTUUwgcXVlcnksIHVzZWQgaW4gYFNFTEVDVGAsIGBGUk9NYCwgYFdIRVJFYCwgYW5kIGBIQVZJTkdgIGNsYXVzZXMuCi0gICAqKkJlbmVmaXRzKio6IFRoZXkgZW5oYW5jZSBkYXRhIHJldHJpZXZhbCBpbiBjb21wbGV4IHNjZW5hcmlvcywgaGFuZGxlIGNvbXBsZXggZGF0YSByZWxhdGlvbnNoaXBzLCBhbmQgaW1wbGVtZW50IG5lc3RlZCBsb2dpYy4KLSAgICoqQ2hhcmFjdGVyaXN0aWNzKio6IFN1YnF1ZXJpZXMgY2FuIHJldHVybiB2YXJpb3VzIHR5cGVzIG9mIHJlc3VsdHMgYW5kIGNhbiBiZSBpbmRlcGVuZGVudCBvciBjb3JyZWxhdGVkIHdpdGggdGhlIG1haW4gcXVlcnkuCi0gICAqKkFkdmFudGFnZXMgYW5kIExpbWl0YXRpb25zKio6IFRoZXkgb2ZmZXIgbW9kdWxhcml0eSBhbmQgaW1wcm92ZWQgcmVhZGFiaWxpdHkgYnV0IGNhbiBjYXVzZSBwZXJmb3JtYW5jZSBpc3N1ZXMgYW5kIGFkZCBjb21wbGV4aXR5LgotICAgKipUeXBlcyBvZiBTdWJxdWVyaWVzKio6IFRoZXNlIGluY2x1ZGUgc2luZ2xlLXJvdywgbXVsdGlwbGUtcm93LCBjb3JyZWxhdGVkLCBzdWJxdWVyaWVzIGFzIGRlcml2ZWQgdGFibGVzLCBhbmQgZXhpc3RlbnRpYWwgc3VicXVlcmllcywgZWFjaCB3aXRoIHNwZWNpZmljIHVzZSBjYXNlcyBhbmQgZXhhbXBsZXMuCgojIyMgQ29tbW9uIFRhYmxlIEV4cHJlc3Npb25zIChDVEVzKQoKLSAgICoqQmFzaWNzKio6IFRoZSBgV0lUSGAgc3RhdGVtZW50IGNyZWF0ZXMgdGVtcG9yYXJ5IHJlc3VsdCBzZXRzIGZvciB1c2UgaW4gU1FMIHF1ZXJpZXMuCi0gICAqKkFkdmFudGFnZXMqKjogQ1RFcyBlbmhhbmNlIHJlYWRhYmlsaXR5LCBhbGxvdyBmb3IgcmVjdXJzaXZlIHF1ZXJpZXMsIGFuZCBjYW4gaW1wcm92ZSBwZXJmb3JtYW5jZS4KLSAgICoqVXNlIENhc2VzIGluIFNha2lsYSBEYXRhYmFzZSoqOiBFeGFtcGxlcyBhcmUgcHJvdmlkZWQsIGxpa2UgZmluZGluZyBmaWxtcyB3aXRoIHRoZSBoaWdoZXN0IHJlbnRhbCByYXRlcyBhbmQgYW5hbHl6aW5nIHJlbnRhbCBwYXR0ZXJucywgYWx0aG91Z2ggaXQncyBub3RlZCB0aGF0IHRoZSBTYWtpbGEgZGF0YWJhc2UgZG9lc24ndCBpbmhlcmVudGx5IGluY2x1ZGUgaGllcmFyY2hpY2FsIGRhdGEgZm9yIHNvbWUgb2YgdGhlIG1vcmUgY29tcGxleCBleGFtcGxlcy4KCiMjIyBQYXJ0aXRpb25zCgpUaGUgcHVycG9zZSBvZiBwYXJ0aXRpb25zIGFuZCB0aGUgYFBBUlRJVElPTiBCWWAgY2xhdXNlIGlzIHRvIGFwcGx5IHdpbmRvdyBmdW5jdGlvbnMgdG8gcGFydGl0aW9ucyBvZiBkYXRhLiBUaGUgaXMgb2Z0ZW4gbmVjZXNzYXJ5IGZvciBkYXRhIGFuYWx5c2lzIGFuZCByYW5raW5nIGFuZCBwcm92aWRlcyBpbXByb3ZlZCBkYXRhIG9yZ2FuaXphdGlvbiwgYnV0IGl0cyB1c2UgY2FuIGxlYWQgdG8gcGVyZm9ybWFuY2UgaXNzdWVzIG9uIGxhcmdlIGRhdGFzZXRzLgoKKipEaWZmZXJlbmNlIGJldHdlZW4gYFBBUlRJVElPTiBCWWAgYW5kIGBHUk9VUCBCWWAqKjogVGhlIGRpc3RpbmN0aW9uIGJldHdlZW4gdGhlc2UgdHdvIGNsYXVzZXMgd2FzIGNsYXJpZmllZC4gYFBBUlRJVElPTiBCWWAsIHVzZWQgd2l0aCB3aW5kb3cgZnVuY3Rpb25zLCByZXRhaW5zIHRoZSBvcmlnaW5hbCByb3cgY291bnQgYW5kIGFsbG93cyBjYWxjdWxhdGlvbnMgd2l0aGluIHBhcnRpdGlvbnMuIENvbnZlcnNlbHksIGBHUk9VUCBCWWAsIHVzZWQgZm9yIGFnZ3JlZ2F0ZSBxdWVyaWVzLCByZWR1Y2VzIHRoZSBudW1iZXIgb2Ygcm93cyB0byBvbmUgcGVyIGdyb3VwIGFuZCBpcyBpZGVhbCBmb3Igc3VtbWFyaWVzLgoKIyMgVHV0b3JpYWwKCkluIHRoaXMgYWR2YW5jZWQgdHV0b3JpYWwgdmlkZW8sIEphbWVzIE9saXZlciBleHBsYWlucyB1bmlvbnMsIHBhcnRpdGlvbnMsIHJlY3Vyc2lvbiwgYW5kIGhpZXJhcmNoaWVzIGZvciBjb25zdHJ1Y3RpbmcgY29tcGxleCBhbmQgZWZmaWNpZW50IFNRTCBxdWVyaWVzLgoKPGlmcmFtZSBzdHlsZSBzcmM9Imh0dHBzOi8vd3d3LnlvdXR1YmUtbm9jb29raWUuY29tL2VtYmVkL0M3Q1BYZUV2S04wIiB3aWR0aD0iNDgwIiBoZWlnaHQ9IjI3MCIgYWxsb3dmdWxsc2NyZWVuPSJhbGxvd2Z1bGxzY3JlZW4iIGRhdGEtZXh0ZXJuYWw9IjEiPgoKPC9pZnJhbWU+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEZpbGVzICYgUmVzb3VyY2VzCgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSZWZlcmVuY2VzCgpbU1FMaXRlIERvY3VtZW50aW9uOiBUaGUgV0lUSCBDbGF1c2VdKGh0dHBzOi8vd3d3LnNxbGl0ZS5vcmcvbGFuZ193aXRoLmh0bWwpCgojIyBFcnJhdGEKCltMZXQgdXMga25vd10oaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLzIxMjE4NzA3Mjc4NDE1Nyl7dGFyZ2V0PSJfYmxhbmsifS4K