Prerequisites
Prior to this lesson, consider working through:
Introduction
This lesson on Python and SQLite is structured to guide you through the process of integrating Python applications with the SQLite database. You will acquire expertise in executing SQLite database operations via Python as well as through embedded SQL code chunks in Posit and Quarto Notebooks.
SQLite, a comprehensive C-language library, is renowned for its SQL database engine that offers impressive speed, serverless architecture, self-sufficiency, and exceptional reliability. It holds the distinction of being the most prevalent database engine in testing environments, as well as on mobile apps. Note that SQLite is not a multi-user database and is file-based rather than server-based like MySQL or SQL Server.
The ubiquity of SQLite extends to its pre-installation on a majority of computers, mobile devices, and web browsers. Python’s built-in sqlite3 module is a critical tool in interacting with SQLite databases.
Significantly, the Python sqlite3 module is in compliance with the Python Database API Specification v2.0, as defined in PEP 249. This specification outlines a standardized SQL interface, aiming to promote uniformity and consistency across Python modules that facilitate database access.
At-a-Glance
Connecting to and accessing data in a SQLite database in Python generally involves these steps:
- Import sqlite3 module:
The import sqlite3
statement imports the sqlite3 module which is required to access SQLite databases. It contains the classes and methods necessary for communicating with the SQLite database. Note that the sqlite3 module actually contains the code for SQLite and therefore no further installation of SQLite is necessary. This emphasizes the lighweight nature of SQLite and the reason why it is often built into apps and simple single-user applications.
- Connect to database with
connect()
method:
Use the connect()
method of the connector
class with the database file name to connect to the database. If a database with that file name does not exist, it will be created. This method returns the SQLite Connection
Object if the connection is successful and that object is needed to access the database.
- Define access with
cursor()
method:
Use the cursor()
method of a Connection
object to create a cursor object to execute SQLite statements including queries.
- Run SQL with the
execute()
method:
The execute()
methods runs the SQL statement and returns a result (result set; a table).
- Get result set with
fetchall()
:
Use cursor.fetchall()
or fetchone()
or fetchmany()
to read the query result.
- Close cursor and connection objects:
Use cursor.close()
and connection.close()
methods to close the cursor and disconnect from the SQLite database. This is necessary to release all allocated resources and free memory. The database cannot be accessed after this.
Of course, be mindful to catch any database exceptions that may occur during this process.
The code below demonstrates accessing an existing database and retrieving all rows from a single table using a SQL SELECT statement.
import sqlite3
try:
dbcon = sqlite3.connect('committeeDB.sqlite')
cursor = dbcon.cursor()
print("connection successful")
sql = "SELECT * FROM faculty;"
cursor.execute(sql)
rs = cursor.fetchall()
print(rs)
cursor.close()
except sqlite3.Error as error:
print("can't connect", error)
finally:
if sqliteConnection:
dbcon.close()
print("connection closed")
Caution: The connection object is not thread-safe; the sqlite3 module does not allow sharing connections between threads. Any synchronization must be done by the application programmer.
Create Database
A new SQLite database is created by connecting to a database that does not exist. This forces creation of a new database file. Note that SQLite is not a server-based database management system like MySQL or Oracle, but is a file-based relational database akin to Microsoft Access.
In the example below, the database will be created in the current folder – if the code is in an R Project then the database file will be created in the project folder. If the database is to be created in a different folder, then a path name must be provided.
By convention, SQLite database files generally have an extension of .db, .sqlite, or .sqlitedb.
Start your Python script by importing the sqlite3
module. This module provides an interface for connecting to a SQLite database. The sqlite3
module comes pre-installed with Python, so there’s no need for additional packages.
To create a new SQLite database, you use the connect
method from the sqlite3
module. If the database file specified does not exist, it will be created automatically.
# Connect to a database (or create if it doesn't exist)
conn = sqlite3.connect('newDB.db')
# Create a cursor object using the cursor method
cursor = conn.cursor()
# Close the connection when done
conn.close()
In the above code:
sqlite3.connect('newDB.db')
creates a new SQLite database file named my_database.db
in the current working directory. You can specify a different path if you wish to create the database elsewhere.
- The
cursor
object is essential for executing SQL statements in Python. Think of the cursor as a pointer to the database, allowing you to interact with it.
- Finally,
conn.close()
closes the connection to the database. It’s important to close the connection when you’re done to free up resources.
Creating an In-Memory Database
SQLite also allows you to create a database in RAM (main memory), which can be faster but is temporary (it’s lost when the program ends). This is useful for testing and scenarios where you need a temporary database such as when you want to run SQL queries agains tabular data loaded from a CSV.
Here’s how you can create an in-memory SQLite database:
# Create a database in RAM
conn = sqlite3.connect(':memory:')
# ... perform operations ...
conn.close()
When ':memory:'
is used in place of a database filename, SQLite creates a temporary database in memory.
This section explained how to set up a Python script to work with SQLite databases. We covered how to create a SQLite database file, connect to it, create a cursor object for executing SQL commands, and close the connection. In the following sections, we will explore how to create tables and manipulate data within the SQLite database using this setup.
Creating Tables
After establishing a connection to a SQLite database, the next step – for a new database – is creating tables to store data. Of course, if the database already exists, then there is no need to create new tables. In this section, we’ll learn how to define and create tables within a SQLite database using Python’s sqlite3
module.
Creating a table in SQLite involves using the CREATE TABLE
SQL statement. This statement allows you to define the table’s structure, including its column names, data types, and any constraints like primary keys.
CREATE TABLE
The basic syntax for creating a table in SQL is as follows:
CREATE TABLE table_name (
column1 datatype,
column2 datatype,
column3 datatype,
[PRIMARY KEY (column)],
[FOREIGN KEY REFERENCES]
....
);
Primary Keys
A primary key is a unique identifier for each record in a database table. It ensures that no two rows have the same key, enabling efficient data retrieval and management.
Defining a Primary Key
When creating a table, you designate one or more columns as the primary key. In SQLite, a primary key can be a single column or a combination of multiple columns (composite key). Designating a primary key can be done in two ways:
- adding the PRIMARY KEY constraint to a column’s definition
- using the PRIMARY KEY constraint to define which column or combination of columns are the primary key
Example: Single Column Primary Key
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
email TEXT
);
Here, id is an integer that serves as the primary key for the users table.
Example: Composite Primary Key
CREATE TABLE user_roles (
user_id INTEGER,
role_id INTEGER,
PRIMARY KEY (user_id, role_id)
);
This creates a composite primary key consisting of user_id and role_id.
Foreign Keys
A foreign key is a column or a group of columns in one table that references the primary key column(s) in another table. It is used to link the data between two tables. SQLite enforces referential integrity which ensures that every foreign key is an actual primary key value in the linked table.
Defining a Foreign Key
The FOREIGN KEY constraint is used to define a foreign key. It establishes a relationship between the foreign key in one table and the primary key in another.
Example: Foreign Key
Imagine we have another table named roles:
CREATE TABLE roles (
id INTEGER PRIMARY KEY,
role_name TEXT
);
We can create a foreign key in the user_roles table from above that references the id column in the roles table:
CREATE TABLE user_roles (
user_id INTEGER,
role_id INTEGER,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (role_id) REFERENCES roles(id)
);
Here, role_id in the user_roles table is a foreign key that references the id in the roles table.
Enforcing Foreign Key Constraints
SQLite has foreign key constraints disabled by default. To enforce them, you need to enable it in your SQLite session setting the PRAGMA below:
conn = sqlite3.connect('my_database.db')
conn.execute("PRAGMA foreign_keys = ON")
This command ensures that all foreign key constraints are enforced in your database session. Note that the enforcement only applies to subsequent insertions of data and is not checked retroactively, so it is still possible to have referential integrity issues and “dangling references” if insertions were done prior to turning on enforcement.
To summarize, a primary key uniquely identifies each record in a table and can be a single column or a combination of columns, while a foreign key is used to link two tables together, ensuring that the data in one table relates correctly to data in another. Defining these keys correctly is essential for data integrity and efficient, and is a key element of the relational model and relational databases. These links are necessary when executing SQL “joins”.
Data Types in SQLite
SQLite uses a dynamic typing system. Some of the common data types include:
INTEGER
: for integers
REAL
: for floating-point numbers
TEXT
: for text strings
BLOB
: for binary data
NUMERIC
: for any numeric data
DATE
: for dates
DATETIME
: for dates and times
Example: Creating a Table
Let’s create a simple table in Python named users
with three columns: id
, name
, and email
with id as the primary key.
# connect to the SQLite database
conn = sqlite3.connect('userDB.sqlitedb')
cursor = conn.cursor()
# create table with 3 columns
cursor.execute('''
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
)
''')
# commit the changes
conn.commit()
# close database connection
conn.close()
Ensuring Table Creation
Sometimes, you might run the script multiple times, and attempting to create an already existing table will result in an error. To prevent this, you can use IF NOT EXISTS
in your CREATE TABLE
statement:
cursor.execute('''
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE
)
''')
This will only create the table if the table does not exist. If you wish to alter the table definition it is necessary to first delete the table from the database (which will also delete any data in the table, so caution is advised). Deleting a table is done with the DROP statement as shown below.
cursor.execute('''
DROP TABLE IF EXISTS users
''')
In this section we learned how to create tables in a SQLite database using Python. We covered the SQL syntax for creating tables, demonstrated how to create primary and foreign keys, discussed SQLite data types, and walked through an example of creating a simple users
table. The ability to create tables is foundational for storing and organizing data effectively in a database. In the next sections, we will explore how to insert, retrieve, update, and delete data in these tables.
Inserting Data into Tables
Having created our tables, we now turn to populating them with data. Inserting data into SQLite tables using Python involves the INSERT INTO SQL statement. Let’s explore how to insert data into tables, handle exceptions, and ensure data integrity through Python’s sqlite3 module.
Basic Insert Statement
The INSERT INTO statement is used to add new rows of data to a table.
Syntax
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);
Example: Inserting Data into Users Table
Let’s insert a new user into the users table we created earlier.
# Assuming connection (conn) and cursor (cursor) are already set up
# Insert a new user
cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("John Doe", "john@example.com"))
# Commit the changes
conn.commit()
In this example, we use ? as placeholders for our values in the INSERT INTO statement. This is a parameterized query that helps prevent SQL injection attacks, a common web security vulnerability. The values for the placeholders are provided as a tuple (“John Doe”, “john@example.com”).
Handling Exceptions
When working with databases, it’s important to handle exceptions that may occur during data insertion, such as inserting data that violates primary key or foreign key constraints.
try:
cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("Jane Doe", "jane@example.com"))
conn.commit()
except sqlite3.IntegrityError:
print("Couldn't insert data due to an integrity error.")
In this code snippet, we catch sqlite3.IntegrityError
exceptions, which may occur due to constraint violations.
Bulk Insertion
If you have multiple records to insert, you can use executemany
to perform bulk insertions efficiently.
users_to_insert = [("Alice", "alice@example.com"), ("Bob", "bob@example.com")]
cursor.executemany("INSERT INTO users (name, email) VALUES (?, ?)", users_to_insert)
conn.commit()
This method is more efficient than inserting each row in a separate transaction, especially when dealing with a large volume of data.
Auto-Incrementing Primary Keys
If a column in a table is defined as INTEGER PRIMARY KEY, SQLite treats it as an “auto-increment” field. This means you don’t need to specify a value for this column; SQLite will automatically assign a unique value by incrementing some counter. Do not assume that the primary key values are sequential.
Retrieving Data with SELECT
Once data is stored in a SQLite database, the next critical operation is retrieving it. The SELECT
SQL statement is used for this purpose. In this section, we will explore how to use SELECT
to retrieve data from a table in SQLite, filter it with conditions, and handle the results in Python. This is only an overview to demonstrate how to execute queries from Python. Consult additional lessons on SQL for more details on SQL SELECT.
Basic SELECT Statement
The SELECT
statement is used to select data from a database table.
Syntax
SELECT column1, column2, ... FROM table_name;
To select all columns from a table, use *
.
Example: Selecting Data
Here’s how to select all data from the users
table:
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)
cursor.fetchall()
retrieves all rows of the query result, returning them as a list of tuples
- the loop then prints each row
Selecting Specific Columns
To select only specific columns:
cursor.execute("SELECT name, email FROM users")
rows = cursor.fetchall()
for row in rows:
print(row)
Filtering Results with WHERE
You can filter query results using the WHERE
clause.
Example: Filtering Data
To find users with a specific name:
cursor.execute("SELECT * FROM users WHERE name = ?", ("John Doe",))
rows = cursor.fetchall()
for row in rows:
print(row)
- this query retrieves rows from
users
where the name
column matches “John Doe”.
Sorting Results
Use the ORDER BY
clause to sort the results.
Example: Sorting Data
To sort users by name in ascending order:
cursor.execute("SELECT * FROM users ORDER BY name ASC")
rows = cursor.fetchall()
for row in rows:
print(row)
Limiting Results
The LIMIT
clause can be used to limit the number of results returned.
Example: Limiting Data
To get the first 5 users:
cursor.execute("SELECT * FROM users LIMIT 5")
rows = cursor.fetchall()
for row in rows:
print(row)
In this section, we discussed how to retrieve data from a SQLite database using the SELECT
statement. We covered how to select all or specific columns, filter results using WHERE
, sort results with ORDER BY
, and limit the number of results returned using LIMIT
. These tools are fundamental for efficiently querying and handling data in a SQLite database. The next section will focus on processing the results of these queries in Python.
After retrieving data from a SQLite database using the SELECT statement, the next step is to process these results in Python. SQLite returns query results as a list of tuples, where each tuple corresponds to a row in the database. Let’s explore how to handle and utilize these query results effectively.
Processing Query Results
When you execute a SELECT statement using the sqlite3 module in Python, the results are typically returned as a list of tuples. Each tuple represents a database record, and each item within the tuple corresponds to a column in the table.
Fetch Methods
There are several methods to retrieve query results:
fetchone()
: Retrieves the next row of a query result set, returning a single tuple, or None when no more data is available.
fetchall()
: Fetches all (remaining) rows of a query result, returning a list of tuples.
fetchmany(size)
: Fetches the next set of rows of a query result, returning a list. You can specify the number of rows to retrieve.
Example: Processing Results with fetchall()
Here’s a basic example of executing a SELECT statement and using fetchall()
:
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
print(f"ID: {row[0]}, Name: {row[1]}, Email: {row[2]}")
In this example, we iterate over each row in a loop and access individual column values using indexing (row[0], row[1], row[2], etc.).
Working with Named Tuples
For better code readability, you can use named tuples. This makes your code more readable by accessing column values by name.
import sqlite3
from collections import namedtuple
# Connect to SQLite database
conn = sqlite3.connect('my_database.db')
conn.row_factory = sqlite3.Row # use sqlite3.Row for named tuples
cursor = conn.cursor()
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
for row in rows:
user = namedtuple('User', row.keys())(*row)
print(f"ID: {user.id}, Name: {user.name}, Email: {user.email}")
sqlite3.Row
provides both index-based and case-insensitive name-based access to columns with almost no memory overhead.
- We then create a named tuple User for each row, making the code more expressive.
Mapping Results to a Dictionary
Alternatively, you can map results to a dictionary for ease of use.
cursor.execute("SELECT * FROM users")
rows = cursor.fetchall()
users = [dict(row) for row in rows]
for user in users:
print(user)
This approach converts each row into a dictionary where column names are keys, allowing you to access data by column names.
In this section, we discussed how to process and handle the results obtained from SQLite queries in Python. We looked at different fetch methods (fetchone
, fetchall
, fetchmany
), and explored how to make query results more readable and manageable using named tuples and dictionaries. Understanding these methods is crucial for effective data manipulation and retrieval in Python-based database applications. Next, we will move on to updating and deleting data in tables.
Updating and Deleting Data
After inserting and retrieving data in SQLite using Python, you may need to update or delete records. This section covers how to modify existing data with the UPDATE statement and remove records with the DELETE statement.
Updating Data
The UPDATE Statement
The UPDATE
statement is used to modify existing records in a table.
Syntax
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;
Example: Updating User Data
Suppose we want to update the email of a user with a specific id.
# Assuming connection (conn) and cursor (cursor) are already set up
# Update user's email
cursor.execute("UPDATE users SET email = ? WHERE id = ?", ("newemail@example.com", 1))
# Commit changes
conn.commit()
In this example:
- we use UPDATE to change the email of the user where id equals 1
- it’s important to use the WHERE clause to specify which records to update. Without it, all records in the table would be updated
Deleting Data
The DELETE Statement
The DELETE
statement is used to delete records from a table.
Syntax
DELETE FROM table_name WHERE condition;
Example: Deleting a User
To delete a user with a specific id:
# Delete user with id 1
cursor.execute("DELETE FROM users WHERE id = ?", (1,))
# Commit changes
conn.commit()
- This statement deletes the record from users where id equals 1.
- As with UPDATE, it’s crucial to use the WHERE clause to specify which records to delete to avoid removing all records in the table.
Handling Exceptions
When updating or deleting data, exceptions can occur, particularly relating to constraints like foreign keys. It’s a good practice to handle these exceptions.
try:
cursor.execute("DELETE FROM users WHERE id = ?", (1,))
conn.commit()
except sqlite3.IntegrityError as e:
print(f"An error occurred: {e}")
In this section, we covered how to update and delete data in a SQLite database using Python. We discussed the importance of using the WHERE clause in both UPDATE and DELETE statements to target specific records and avoid unintended data modifications. We also touched on the importance of exception handling to manage constraint violations. These operations are fundamental for maintaining the integrity and relevance of data within your database. Next, we will look into finalizing transactions and properly closing the database connection.
Finalizing Transactions and Closing the Connection
After performing database operations such as creating tables, inserting, updating, or deleting data, it’s crucial to finalize transactions and properly close the connection to the SQLite database. This section covers the significance of committing transactions and safely closing the database connection using Python’s sqlite3 module.
Committing Transactions
In database systems, a transaction is a sequence of database operations that are treated as a single unit. In SQLite, these transactions are used to ensure data integrity. When you modify data (insert, update, delete), these changes are not immediately saved to the database. They are instead held in a temporary state until you commit the transaction.
Using commit()
The commit()
method of the connection object finalizes the transaction and writes all changes to the database.
# Perform some data modification
cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("John Doe", "john@example.com"))
# Commit the transaction
conn.commit()
Failing to call commit()
will result in changes not being saved when the connection is closed.
Auto-Commit Mode
SQLite also supports an auto-commit mode, which can be activated by setting isolation_level to None. In auto-commit mode, each statement is treated as a transaction and is automatically committed.
conn.isolation_level = None
However, manual control of transactions using commit()
is recommended for better error handling and performance, especially in applications with multiple operations.
Rolling Back Transactions
If an error occurs during a transaction, you may need to undo all the operations in that transaction. This is known as rolling back a transaction.
try:
# Perform database operations
cursor.execute("INSERT INTO users (name, email) VALUES (?, ?)", ("Jane Doe", "jane@example.com"))
# Commit if successful
conn.commit()
except sqlite3.Error as error:
# Rollback in case of an error
print(f"Error: {error}")
conn.rollback()
rollback()
reverts the database to the state before the transaction began.
Closing the Connection
Properly closing the database connection is vital to free up resources. Use the close()
method on the connection object to close the connection.
# Close the connection
conn.close()
Always ensure that the connection is closed after all database operations have completed.
Summary
In this lesson, we have covered the fundamentals of using SQLite with Python. Starting from setting up a SQLite database to performing various database operations such as creating tables, inserting, updating, deleting, and retrieving data, we have navigated through the essential elements of Python’s sqlite3
module.
Let’s briefly recap the key points:
SQLite and Python Setup: We began by exploring how to set up a SQLite database using Python’s sqlite3
module. We discussed creating a new database and establishing a connection to it.
Creating Tables: We learned how to create tables in SQLite, defining primary keys and structuring data with various data types.
Inserting Data: The tutorial covered the insertion of data into tables, including handling exceptions and performing bulk insertions for efficiency.
Retrieving Data: We demonstrated how to retrieve data using the SELECT
statement, applying filters with the WHERE
clause, and sorting and limiting results.
Processing Query Results: The handling and processing of query results returned as tuples, and the use of named tuples and dictionaries for better data management, were explored.
Updating and Deleting Data: We discussed updating existing records and deleting data from tables, emphasizing the importance of the WHERE
clause to target specific records.
Transactions and Connection Management: The tutorial highlighted the significance of committing transactions, rolling back in case of errors, and the importance of properly closing the database connection to ensure data integrity and resource management.
To solidify your understanding, it’s beneficial to engage in hands-on practice:
- Experiment with different data types and table structures.
- Try more complex SQL queries, joins, and aggregate functions.
- Implement error handling and transaction management in more complex scenarios.
SQLite, with its simplicity and lightweight nature, combined with Python’s versatility, offers a powerful set of tools for managing and manipulating database-driven applications. The skills you’ve acquired here should serve as a robust foundation for your journey in database programming with Python and SQLite.
LS0tCnRpdGxlOiAiU1FMaXRlIHdpdGggUHl0aG9uOiBBIFByaW1lciIKcGFyYW1zOgogIGNhdGVnb3J5OiA3CiAgbnVtYmVyOiAzMDAKICB0aW1lOiAzMAogIGxldmVsOiBiZWdpbm5lcgogIHRhZ3M6ICJQeXRob24sU1FMLHNxbGRmLGRhdGFiYXNlLHNxbGl0ZSIKICBkZXNjcmlwdGlvbjogIkRlbW9uc3RyYXRlcyBob3cgdG8gY3JlYXRlIGEgc2ltcGxlIFNRTGl0ZSAob3IKICAgICAgICAgICAgICAgIGFueSBvdGhlciByZWxhdGlvbmFsKSBkYXRhYmFzZSBmcm9tIFB5dGhvbiB1c2luZyB7c3FsfQogICAgICAgICAgICAgICAgY29kZSBjaHVua3MgYW5kIGZ1bmN0aW9uIGNhbGxzLiBNb3N0IG9mIHRoZSBjb25jZXB0cwogICAgICAgICAgICAgICAgYXBwbHkgdG8gYWxsIHJlbGF0aW9uYWwgZGF0YWJhc2VzLCBpbmNsdWRpbmcgTXlTUUwuIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgpgYGB7cn0KbGlicmFyeShyZXRpY3VsYXRlKQpgYGAKCiMjIFByZXJlcXVpc2l0ZXMKClByaW9yIHRvIHRoaXMgbGVzc29uLCBjb25zaWRlciB3b3JraW5nIHRocm91Z2g6CgotICAgWzcwLjgwMSDilIYgVGhlIFNRTGl0ZSBEYXRhYmFzZV0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy83MC5zcWwvbC03MC04MDEtaW50cm8tc3FsaXRlL2wtNzAtODAxLmh0bWwpCgojIyBJbnRyb2R1Y3Rpb24KClRoaXMgbGVzc29uIG9uIFB5dGhvbiBhbmQgU1FMaXRlIGlzIHN0cnVjdHVyZWQgdG8gZ3VpZGUgeW91IHRocm91Z2ggdGhlIHByb2Nlc3Mgb2YgaW50ZWdyYXRpbmcgUHl0aG9uIGFwcGxpY2F0aW9ucyB3aXRoIHRoZSBTUUxpdGUgZGF0YWJhc2UuIFlvdSB3aWxsIGFjcXVpcmUgZXhwZXJ0aXNlIGluIGV4ZWN1dGluZyBTUUxpdGUgZGF0YWJhc2Ugb3BlcmF0aW9ucyB2aWEgUHl0aG9uIGFzIHdlbGwgYXMgdGhyb3VnaCBlbWJlZGRlZCBTUUwgY29kZSBjaHVua3MgaW4gUG9zaXQgYW5kIFF1YXJ0byBOb3RlYm9va3MuCgpTUUxpdGUsIGEgY29tcHJlaGVuc2l2ZSBDLWxhbmd1YWdlIGxpYnJhcnksIGlzIHJlbm93bmVkIGZvciBpdHMgU1FMIGRhdGFiYXNlIGVuZ2luZSB0aGF0IG9mZmVycyBpbXByZXNzaXZlIHNwZWVkLCBzZXJ2ZXJsZXNzIGFyY2hpdGVjdHVyZSwgc2VsZi1zdWZmaWNpZW5jeSwgYW5kIGV4Y2VwdGlvbmFsIHJlbGlhYmlsaXR5LiBJdCBob2xkcyB0aGUgZGlzdGluY3Rpb24gb2YgYmVpbmcgdGhlIG1vc3QgcHJldmFsZW50IGRhdGFiYXNlIGVuZ2luZSBpbiB0ZXN0aW5nIGVudmlyb25tZW50cywgYXMgd2VsbCBhcyBvbiBtb2JpbGUgYXBwcy4gTm90ZSB0aGF0IFNRTGl0ZSBpcyBub3QgYSBtdWx0aS11c2VyIGRhdGFiYXNlIGFuZCBpcyBmaWxlLWJhc2VkIHJhdGhlciB0aGFuIHNlcnZlci1iYXNlZCBsaWtlIE15U1FMIG9yIFNRTCBTZXJ2ZXIuCgpUaGUgdWJpcXVpdHkgb2YgU1FMaXRlIGV4dGVuZHMgdG8gaXRzIHByZS1pbnN0YWxsYXRpb24gb24gYSBtYWpvcml0eSBvZiBjb21wdXRlcnMsIG1vYmlsZSBkZXZpY2VzLCBhbmQgd2ViIGJyb3dzZXJzLiBQeXRob24ncyBidWlsdC1pbiAqKnNxbGl0ZTMqKiBtb2R1bGUgaXMgYSBjcml0aWNhbCB0b29sIGluIGludGVyYWN0aW5nIHdpdGggU1FMaXRlIGRhdGFiYXNlcy4KClNpZ25pZmljYW50bHksIHRoZSBQeXRob24gKipzcWxpdGUzKiogbW9kdWxlIGlzIGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgUHl0aG9uIERhdGFiYXNlIEFQSSBTcGVjaWZpY2F0aW9uIHYyLjAsIGFzIGRlZmluZWQgaW4gUEVQIDI0OS4gVGhpcyBzcGVjaWZpY2F0aW9uIG91dGxpbmVzIGEgc3RhbmRhcmRpemVkIFNRTCBpbnRlcmZhY2UsIGFpbWluZyB0byBwcm9tb3RlIHVuaWZvcm1pdHkgYW5kIGNvbnNpc3RlbmN5IGFjcm9zcyBQeXRob24gbW9kdWxlcyB0aGF0IGZhY2lsaXRhdGUgZGF0YWJhc2UgYWNjZXNzLgoKIyMgQXQtYS1HbGFuY2UKCkNvbm5lY3RpbmcgdG8gYW5kIGFjY2Vzc2luZyBkYXRhIGluIGEgU1FMaXRlIGRhdGFiYXNlIGluIFB5dGhvbiBnZW5lcmFsbHkgaW52b2x2ZXMgdGhlc2Ugc3RlcHM6CgoxLiAgKipJbXBvcnQgc3FsaXRlMyoqIG1vZHVsZToKClRoZSBgaW1wb3J0IHNxbGl0ZTNgIHN0YXRlbWVudCBpbXBvcnRzIHRoZSAqKnNxbGl0ZTMqKiBtb2R1bGUgd2hpY2ggaXMgcmVxdWlyZWQgdG8gYWNjZXNzIFNRTGl0ZSBkYXRhYmFzZXMuIEl0IGNvbnRhaW5zIHRoZSBjbGFzc2VzIGFuZCBtZXRob2RzIG5lY2Vzc2FyeSBmb3IgY29tbXVuaWNhdGluZyB3aXRoIHRoZSBTUUxpdGUgZGF0YWJhc2UuIE5vdGUgdGhhdCB0aGUgKipzcWxpdGUzKiogbW9kdWxlIGFjdHVhbGx5IGNvbnRhaW5zIHRoZSBjb2RlIGZvciBTUUxpdGUgYW5kIHRoZXJlZm9yZSBubyBmdXJ0aGVyIGluc3RhbGxhdGlvbiBvZiBTUUxpdGUgaXMgbmVjZXNzYXJ5LiBUaGlzIGVtcGhhc2l6ZXMgdGhlIGxpZ2h3ZWlnaHQgbmF0dXJlIG9mIFNRTGl0ZSBhbmQgdGhlIHJlYXNvbiB3aHkgaXQgaXMgb2Z0ZW4gYnVpbHQgaW50byBhcHBzIGFuZCBzaW1wbGUgc2luZ2xlLXVzZXIgYXBwbGljYXRpb25zLgoKMi4gICoqQ29ubmVjdCB0byBkYXRhYmFzZSB3aXRoIGBjb25uZWN0KClgIG1ldGhvZCoqOgoKVXNlIHRoZSBgY29ubmVjdCgpYCBtZXRob2Qgb2YgdGhlIGBjb25uZWN0b3JgIGNsYXNzIHdpdGggdGhlIGRhdGFiYXNlIGZpbGUgbmFtZSB0byBjb25uZWN0IHRvIHRoZSBkYXRhYmFzZS4gSWYgYSBkYXRhYmFzZSB3aXRoIHRoYXQgZmlsZSBuYW1lIGRvZXMgbm90IGV4aXN0LCBpdCB3aWxsIGJlIGNyZWF0ZWQuIFRoaXMgbWV0aG9kIHJldHVybnMgdGhlIFNRTGl0ZSBgQ29ubmVjdGlvbmAgT2JqZWN0IGlmIHRoZSBjb25uZWN0aW9uIGlzIHN1Y2Nlc3NmdWwgYW5kIHRoYXQgb2JqZWN0IGlzIG5lZWRlZCB0byBhY2Nlc3MgdGhlIGRhdGFiYXNlLgoKMy4gICoqRGVmaW5lIGFjY2VzcyB3aXRoIGBjdXJzb3IoKWAgbWV0aG9kKio6CgpVc2UgdGhlIGBjdXJzb3IoKWAgbWV0aG9kIG9mIGEgYENvbm5lY3Rpb25gIG9iamVjdCB0byBjcmVhdGUgYSBjdXJzb3Igb2JqZWN0IHRvIGV4ZWN1dGUgU1FMaXRlIHN0YXRlbWVudHMgaW5jbHVkaW5nIHF1ZXJpZXMuCgo0LiAgKipSdW4gU1FMIHdpdGggdGhlIGBleGVjdXRlKClgIG1ldGhvZCoqOgoKVGhlIGBleGVjdXRlKClgIG1ldGhvZHMgcnVucyB0aGUgU1FMIHN0YXRlbWVudCBhbmQgcmV0dXJucyBhIHJlc3VsdCAocmVzdWx0IHNldDsgYSB0YWJsZSkuCgo1LiAgKipHZXQgcmVzdWx0IHNldCB3aXRoIGBmZXRjaGFsbCgpYCoqOgoKVXNlIGBjdXJzb3IuZmV0Y2hhbGwoKWAgb3IgYGZldGNob25lKClgIG9yIGBmZXRjaG1hbnkoKWAgdG8gcmVhZCB0aGUgcXVlcnkgcmVzdWx0LgoKNi4gICoqQ2xvc2UgY3Vyc29yIGFuZCBjb25uZWN0aW9uIG9iamVjdHMqKjoKClVzZSBgY3Vyc29yLmNsb3NlKClgIGFuZCBgY29ubmVjdGlvbi5jbG9zZSgpYCBtZXRob2RzIHRvIGNsb3NlIHRoZSBjdXJzb3IgYW5kIGRpc2Nvbm5lY3QgZnJvbSB0aGUgU1FMaXRlIGRhdGFiYXNlLiBUaGlzIGlzIG5lY2Vzc2FyeSB0byByZWxlYXNlIGFsbCBhbGxvY2F0ZWQgcmVzb3VyY2VzIGFuZCBmcmVlIG1lbW9yeS4gVGhlIGRhdGFiYXNlIGNhbm5vdCBiZSBhY2Nlc3NlZCBhZnRlciB0aGlzLgoKT2YgY291cnNlLCBiZSBtaW5kZnVsIHRvIGNhdGNoIGFueSBkYXRhYmFzZSBleGNlcHRpb25zIHRoYXQgbWF5IG9jY3VyIGR1cmluZyB0aGlzIHByb2Nlc3MuCgpUaGUgY29kZSBiZWxvdyBkZW1vbnN0cmF0ZXMgYWNjZXNzaW5nIGFuIGV4aXN0aW5nIGRhdGFiYXNlIGFuZCByZXRyaWV2aW5nIGFsbCByb3dzIGZyb20gYSBzaW5nbGUgdGFibGUgdXNpbmcgYSBTUUwgKlNFTEVDVCogc3RhdGVtZW50LgoKYGBge3B5dGhvbiBldmFsPUZ9CmltcG9ydCBzcWxpdGUzCgp0cnk6CiAgICBkYmNvbiA9IHNxbGl0ZTMuY29ubmVjdCgnY29tbWl0dGVlREIuc3FsaXRlJykKICAgIGN1cnNvciA9IGRiY29uLmN1cnNvcigpCiAgICBwcmludCgiY29ubmVjdGlvbiBzdWNjZXNzZnVsIikKCiAgICBzcWwgPSAiU0VMRUNUICogRlJPTSBmYWN1bHR5OyIKICAgIGN1cnNvci5leGVjdXRlKHNxbCkKICAgIAogICAgcnMgPSBjdXJzb3IuZmV0Y2hhbGwoKQogICAgCiAgICBwcmludChycykKICAgIAogICAgY3Vyc29yLmNsb3NlKCkKCmV4Y2VwdCBzcWxpdGUzLkVycm9yIGFzIGVycm9yOgogICAgcHJpbnQoImNhbid0IGNvbm5lY3QiLCBlcnJvcikKICAgIApmaW5hbGx5OgogICAgaWYgc3FsaXRlQ29ubmVjdGlvbjoKICAgICAgICBkYmNvbi5jbG9zZSgpCiAgICAgICAgcHJpbnQoImNvbm5lY3Rpb24gY2xvc2VkIikKYGBgCgo+ICoqQ2F1dGlvbioqOiBUaGUgY29ubmVjdGlvbiBvYmplY3QgaXMgbm90IHRocmVhZC1zYWZlOyB0aGUgKipzcWxpdGUzKiogbW9kdWxlIGRvZXMgbm90IGFsbG93IHNoYXJpbmcgY29ubmVjdGlvbnMgYmV0d2VlbiB0aHJlYWRzLiBBbnkgc3luY2hyb25pemF0aW9uIG11c3QgYmUgZG9uZSBieSB0aGUgYXBwbGljYXRpb24gcHJvZ3JhbW1lci4KCiMjIFR1dG9yaWFsCgpCZWZvcmUgcHJvY2VlZGluZyB3aXRoIHRoZSByZW1haW5kZXIgb2YgdGhlIGxlc3Nvbiwgd2F0Y2ggdGhlIHZpZGVvIGJ5IERyLiBNYXJ0aW4gU2NoZWRsYmF1ZXIgYW5kIGZvbGxvdyBhbG9uZy4KCjxpZnJhbWUgc3JjPSJodHRwczovL3BsYXllci52aW1lby5jb20vdmlkZW8vODg0NDkwOTU3P3RpdGxlPTAmYW1wO2J5bGluZT0wJmFtcDtwb3J0cmFpdD0wJmFtcDtzcGVlZD0wJmFtcDtiYWRnZT0wJmFtcDthdXRvcGF1c2U9MCZhbXA7YWlycGxheT0wJmFtcDthdWRpb190cmFja3M9MCZhbXA7Y2hhcHRlcnM9MCZhbXA7Y2hyb21lY2FzdD0wJmFtcDtjbG9zZWRfY2FwdGlvbnM9MCZhbXA7dHJhbnNjcmlwdD0wJmFtcDtwbGF5ZXJfaWQ9MCZhbXA7YXBwX2lkPTU4NDc5IiB3aWR0aD0iNDgwIiBoZWlnaHQ9IjI3MCIgZnJhbWVib3JkZXI9IjEiIGFsbG93PSJhdXRvcGxheTsgZnVsbHNjcmVlbjsgcGljdHVyZS1pbi1waWN0dXJlIiB0aXRsZT0iV29ya2luZyB3aXRoIFNRTGl0ZSBmcm9tIFB5dGhvbiIgZGF0YS1leHRlcm5hbD0iMSI+Cgo8L2lmcmFtZT4KCioqRklsZSB1c2VkIGluIFR1dG9yaWFsKio6IFtjcmVhdGUtdGFibGVzLnB5XShjcmVhdGUtdGFibGVzLnB5KSBcfCBbc3FsaXRlLWZyb20tcHl0aG9uLnB5XShzcWxpdGUtZnJvbS1weXRob24ucHkpCgojIyBDcmVhdGUgRGF0YWJhc2UKCkEgbmV3IFNRTGl0ZSBkYXRhYmFzZSBpcyBjcmVhdGVkIGJ5IGNvbm5lY3RpbmcgdG8gYSBkYXRhYmFzZSB0aGF0IGRvZXMgbm90IGV4aXN0LiBUaGlzIGZvcmNlcyBjcmVhdGlvbiBvZiBhIG5ldyBkYXRhYmFzZSBmaWxlLiBOb3RlIHRoYXQgU1FMaXRlIGlzIG5vdCBhIHNlcnZlci1iYXNlZCBkYXRhYmFzZSBtYW5hZ2VtZW50IHN5c3RlbSBsaWtlIE15U1FMIG9yIE9yYWNsZSwgYnV0IGlzIGEgZmlsZS1iYXNlZCByZWxhdGlvbmFsIGRhdGFiYXNlIGFraW4gdG8gTWljcm9zb2Z0IEFjY2Vzcy4KCkluIHRoZSBleGFtcGxlIGJlbG93LCB0aGUgZGF0YWJhc2Ugd2lsbCBiZSBjcmVhdGVkIGluIHRoZSBjdXJyZW50IGZvbGRlciAtLSBpZiB0aGUgY29kZSBpcyBpbiBhbiBSIFByb2plY3QgdGhlbiB0aGUgZGF0YWJhc2UgZmlsZSB3aWxsIGJlIGNyZWF0ZWQgaW4gdGhlIHByb2plY3QgZm9sZGVyLiBJZiB0aGUgZGF0YWJhc2UgaXMgdG8gYmUgY3JlYXRlZCBpbiBhIGRpZmZlcmVudCBmb2xkZXIsIHRoZW4gYSBwYXRoIG5hbWUgbXVzdCBiZSBwcm92aWRlZC4KCkJ5IGNvbnZlbnRpb24sIFNRTGl0ZSBkYXRhYmFzZSBmaWxlcyBnZW5lcmFsbHkgaGF2ZSBhbiBleHRlbnNpb24gb2YgKi5kYiosICouc3FsaXRlKiwgb3IgKi5zcWxpdGVkYiouCgpTdGFydCB5b3VyIFB5dGhvbiBzY3JpcHQgYnkgaW1wb3J0aW5nIHRoZSBgc3FsaXRlM2AgbW9kdWxlLiBUaGlzIG1vZHVsZSBwcm92aWRlcyBhbiBpbnRlcmZhY2UgZm9yIGNvbm5lY3RpbmcgdG8gYSBTUUxpdGUgZGF0YWJhc2UuIFRoZSBgc3FsaXRlM2AgbW9kdWxlIGNvbWVzIHByZS1pbnN0YWxsZWQgd2l0aCBQeXRob24sIHNvIHRoZXJlJ3Mgbm8gbmVlZCBmb3IgYWRkaXRpb25hbCBwYWNrYWdlcy4KCmBgYCBweXRob24KaW1wb3J0IHNxbGl0ZTMKYGBgCgpUbyBjcmVhdGUgYSBuZXcgU1FMaXRlIGRhdGFiYXNlLCB5b3UgdXNlIHRoZSBgY29ubmVjdGAgbWV0aG9kIGZyb20gdGhlIGBzcWxpdGUzYCBtb2R1bGUuIElmIHRoZSBkYXRhYmFzZSBmaWxlIHNwZWNpZmllZCBkb2VzIG5vdCBleGlzdCwgaXQgd2lsbCBiZSBjcmVhdGVkIGF1dG9tYXRpY2FsbHkuCgpgYGAgcHl0aG9uCiMgQ29ubmVjdCB0byBhIGRhdGFiYXNlIChvciBjcmVhdGUgaWYgaXQgZG9lc24ndCBleGlzdCkKY29ubiA9IHNxbGl0ZTMuY29ubmVjdCgnbmV3REIuZGInKQoKIyBDcmVhdGUgYSBjdXJzb3Igb2JqZWN0IHVzaW5nIHRoZSBjdXJzb3IgbWV0aG9kCmN1cnNvciA9IGNvbm4uY3Vyc29yKCkKCiMgQ2xvc2UgdGhlIGNvbm5lY3Rpb24gd2hlbiBkb25lCmNvbm4uY2xvc2UoKQpgYGAKCkluIHRoZSBhYm92ZSBjb2RlOgoKLSAgIGBzcWxpdGUzLmNvbm5lY3QoJ25ld0RCLmRiJylgIGNyZWF0ZXMgYSBuZXcgU1FMaXRlIGRhdGFiYXNlIGZpbGUgbmFtZWQgYG15X2RhdGFiYXNlLmRiYCBpbiB0aGUgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeS4gWW91IGNhbiBzcGVjaWZ5IGEgZGlmZmVyZW50IHBhdGggaWYgeW91IHdpc2ggdG8gY3JlYXRlIHRoZSBkYXRhYmFzZSBlbHNld2hlcmUuCi0gICBUaGUgYGN1cnNvcmAgb2JqZWN0IGlzIGVzc2VudGlhbCBmb3IgZXhlY3V0aW5nIFNRTCBzdGF0ZW1lbnRzIGluIFB5dGhvbi4gVGhpbmsgb2YgdGhlIGN1cnNvciBhcyBhIHBvaW50ZXIgdG8gdGhlIGRhdGFiYXNlLCBhbGxvd2luZyB5b3UgdG8gaW50ZXJhY3Qgd2l0aCBpdC4KLSAgIEZpbmFsbHksIGBjb25uLmNsb3NlKClgIGNsb3NlcyB0aGUgY29ubmVjdGlvbiB0byB0aGUgZGF0YWJhc2UuIEl0J3MgaW1wb3J0YW50IHRvIGNsb3NlIHRoZSBjb25uZWN0aW9uIHdoZW4geW91J3JlIGRvbmUgdG8gZnJlZSB1cCByZXNvdXJjZXMuCgojIyMgQ3JlYXRpbmcgYW4gSW4tTWVtb3J5IERhdGFiYXNlCgpTUUxpdGUgYWxzbyBhbGxvd3MgeW91IHRvIGNyZWF0ZSBhIGRhdGFiYXNlIGluIFJBTSAobWFpbiBtZW1vcnkpLCB3aGljaCBjYW4gYmUgZmFzdGVyIGJ1dCBpcyB0ZW1wb3JhcnkgKGl0J3MgbG9zdCB3aGVuIHRoZSBwcm9ncmFtIGVuZHMpLiBUaGlzIGlzIHVzZWZ1bCBmb3IgdGVzdGluZyBhbmQgc2NlbmFyaW9zIHdoZXJlIHlvdSBuZWVkIGEgdGVtcG9yYXJ5IGRhdGFiYXNlIHN1Y2ggYXMgd2hlbiB5b3Ugd2FudCB0byBydW4gU1FMIHF1ZXJpZXMgYWdhaW5zIHRhYnVsYXIgZGF0YSBsb2FkZWQgZnJvbSBhIENTVi4KCkhlcmUncyBob3cgeW91IGNhbiBjcmVhdGUgYW4gaW4tbWVtb3J5IFNRTGl0ZSBkYXRhYmFzZToKCmBgYCBweXRob24KIyBDcmVhdGUgYSBkYXRhYmFzZSBpbiBSQU0KY29ubiA9IHNxbGl0ZTMuY29ubmVjdCgnOm1lbW9yeTonKQojIC4uLiBwZXJmb3JtIG9wZXJhdGlvbnMgLi4uCmNvbm4uY2xvc2UoKQpgYGAKCldoZW4gYCc6bWVtb3J5OidgIGlzIHVzZWQgaW4gcGxhY2Ugb2YgYSBkYXRhYmFzZSBmaWxlbmFtZSwgU1FMaXRlIGNyZWF0ZXMgYSB0ZW1wb3JhcnkgZGF0YWJhc2UgaW4gbWVtb3J5LgoKVGhpcyBzZWN0aW9uIGV4cGxhaW5lZCBob3cgdG8gc2V0IHVwIGEgUHl0aG9uIHNjcmlwdCB0byB3b3JrIHdpdGggU1FMaXRlIGRhdGFiYXNlcy4gV2UgY292ZXJlZCBob3cgdG8gY3JlYXRlIGEgU1FMaXRlIGRhdGFiYXNlIGZpbGUsIGNvbm5lY3QgdG8gaXQsIGNyZWF0ZSBhIGN1cnNvciBvYmplY3QgZm9yIGV4ZWN1dGluZyBTUUwgY29tbWFuZHMsIGFuZCBjbG9zZSB0aGUgY29ubmVjdGlvbi4gSW4gdGhlIGZvbGxvd2luZyBzZWN0aW9ucywgd2Ugd2lsbCBleHBsb3JlIGhvdyB0byBjcmVhdGUgdGFibGVzIGFuZCBtYW5pcHVsYXRlIGRhdGEgd2l0aGluIHRoZSBTUUxpdGUgZGF0YWJhc2UgdXNpbmcgdGhpcyBzZXR1cC4KCiMjIENyZWF0aW5nIFRhYmxlcwoKQWZ0ZXIgZXN0YWJsaXNoaW5nIGEgY29ubmVjdGlvbiB0byBhIFNRTGl0ZSBkYXRhYmFzZSwgdGhlIG5leHQgc3RlcCAtLSBmb3IgYSBuZXcgZGF0YWJhc2UgLS0gaXMgY3JlYXRpbmcgdGFibGVzIHRvIHN0b3JlIGRhdGEuIE9mIGNvdXJzZSwgaWYgdGhlIGRhdGFiYXNlIGFscmVhZHkgZXhpc3RzLCB0aGVuIHRoZXJlIGlzIG5vIG5lZWQgdG8gY3JlYXRlIG5ldyB0YWJsZXMuIEluIHRoaXMgc2VjdGlvbiwgd2UnbGwgbGVhcm4gaG93IHRvIGRlZmluZSBhbmQgY3JlYXRlIHRhYmxlcyB3aXRoaW4gYSBTUUxpdGUgZGF0YWJhc2UgdXNpbmcgUHl0aG9uJ3MgYHNxbGl0ZTNgIG1vZHVsZS4KCkNyZWF0aW5nIGEgdGFibGUgaW4gU1FMaXRlIGludm9sdmVzIHVzaW5nIHRoZSBgQ1JFQVRFIFRBQkxFYCBTUUwgc3RhdGVtZW50LiBUaGlzIHN0YXRlbWVudCBhbGxvd3MgeW91IHRvIGRlZmluZSB0aGUgdGFibGUncyBzdHJ1Y3R1cmUsIGluY2x1ZGluZyBpdHMgY29sdW1uIG5hbWVzLCBkYXRhIHR5cGVzLCBhbmQgYW55IGNvbnN0cmFpbnRzIGxpa2UgcHJpbWFyeSBrZXlzLgoKIyMjIENSRUFURSBUQUJMRQoKVGhlIGJhc2ljIHN5bnRheCBmb3IgY3JlYXRpbmcgYSB0YWJsZSBpbiBTUUwgaXMgYXMgZm9sbG93czoKCmBgYCBzcWwKQ1JFQVRFIFRBQkxFIHRhYmxlX25hbWUgKAogICAgY29sdW1uMSBkYXRhdHlwZSwKICAgIGNvbHVtbjIgZGF0YXR5cGUsCiAgICBjb2x1bW4zIGRhdGF0eXBlLAogICAgW1BSSU1BUlkgS0VZIChjb2x1bW4pXSwKICAgIFtGT1JFSUdOIEtFWSBSRUZFUkVOQ0VTXQogICAuLi4uCik7CmBgYAoKIyMjIFByaW1hcnkgS2V5cwoKQSBwcmltYXJ5IGtleSBpcyBhIHVuaXF1ZSBpZGVudGlmaWVyIGZvciBlYWNoIHJlY29yZCBpbiBhIGRhdGFiYXNlIHRhYmxlLiBJdCBlbnN1cmVzIHRoYXQgbm8gdHdvIHJvd3MgaGF2ZSB0aGUgc2FtZSBrZXksIGVuYWJsaW5nIGVmZmljaWVudCBkYXRhIHJldHJpZXZhbCBhbmQgbWFuYWdlbWVudC4KCiMjIyMgRGVmaW5pbmcgYSBQcmltYXJ5IEtleQoKV2hlbiBjcmVhdGluZyBhIHRhYmxlLCB5b3UgZGVzaWduYXRlIG9uZSBvciBtb3JlIGNvbHVtbnMgYXMgdGhlIHByaW1hcnkga2V5LiBJbiBTUUxpdGUsIGEgcHJpbWFyeSBrZXkgY2FuIGJlIGEgc2luZ2xlIGNvbHVtbiBvciBhIGNvbWJpbmF0aW9uIG9mIG11bHRpcGxlIGNvbHVtbnMgKGNvbXBvc2l0ZSBrZXkpLiBEZXNpZ25hdGluZyBhIHByaW1hcnkga2V5IGNhbiBiZSBkb25lIGluIHR3byB3YXlzOgoKLSAgIGFkZGluZyB0aGUgKlBSSU1BUlkgS0VZKiBjb25zdHJhaW50IHRvIGEgY29sdW1uJ3MgZGVmaW5pdGlvbgotICAgdXNpbmcgdGhlICpQUklNQVJZIEtFWSogY29uc3RyYWludCB0byBkZWZpbmUgd2hpY2ggY29sdW1uIG9yIGNvbWJpbmF0aW9uIG9mIGNvbHVtbnMgYXJlIHRoZSBwcmltYXJ5IGtleQoKKipFeGFtcGxlOiBTaW5nbGUgQ29sdW1uIFByaW1hcnkgS2V5KioKCmBgYCBzcWwKQ1JFQVRFIFRBQkxFIHVzZXJzICgKICAgIGlkIElOVEVHRVIgUFJJTUFSWSBLRVksCiAgICBuYW1lIFRFWFQsCiAgICBlbWFpbCBURVhUCik7CmBgYAoKSGVyZSwgKmlkKiBpcyBhbiBpbnRlZ2VyIHRoYXQgc2VydmVzIGFzIHRoZSBwcmltYXJ5IGtleSBmb3IgdGhlICp1c2VycyogdGFibGUuCgoqKkV4YW1wbGU6IENvbXBvc2l0ZSBQcmltYXJ5IEtleSoqCgpgYGAgc3FsCkNSRUFURSBUQUJMRSB1c2VyX3JvbGVzICgKICAgIHVzZXJfaWQgSU5URUdFUiwKICAgIHJvbGVfaWQgSU5URUdFUiwKICAgIFBSSU1BUlkgS0VZICh1c2VyX2lkLCByb2xlX2lkKQopOwpgYGAKClRoaXMgY3JlYXRlcyBhIGNvbXBvc2l0ZSBwcmltYXJ5IGtleSBjb25zaXN0aW5nIG9mICp1c2VyX2lkKiBhbmQgKnJvbGVfaWQqLgoKIyMjIEZvcmVpZ24gS2V5cwoKQSBmb3JlaWduIGtleSBpcyBhIGNvbHVtbiBvciBhIGdyb3VwIG9mIGNvbHVtbnMgaW4gb25lIHRhYmxlIHRoYXQgcmVmZXJlbmNlcyB0aGUgcHJpbWFyeSBrZXkgY29sdW1uKHMpIGluIGFub3RoZXIgdGFibGUuIEl0IGlzIHVzZWQgdG8gbGluayB0aGUgZGF0YSBiZXR3ZWVuIHR3byB0YWJsZXMuIFNRTGl0ZSBlbmZvcmNlcyByZWZlcmVudGlhbCBpbnRlZ3JpdHkgd2hpY2ggZW5zdXJlcyB0aGF0IGV2ZXJ5IGZvcmVpZ24ga2V5IGlzIGFuIGFjdHVhbCBwcmltYXJ5IGtleSB2YWx1ZSBpbiB0aGUgbGlua2VkIHRhYmxlLgoKIyMjIyBEZWZpbmluZyBhIEZvcmVpZ24gS2V5CgpUaGUgKkZPUkVJR04gS0VZKiBjb25zdHJhaW50IGlzIHVzZWQgdG8gZGVmaW5lIGEgZm9yZWlnbiBrZXkuIEl0IGVzdGFibGlzaGVzIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIGZvcmVpZ24ga2V5IGluIG9uZSB0YWJsZSBhbmQgdGhlIHByaW1hcnkga2V5IGluIGFub3RoZXIuCgoqKkV4YW1wbGU6IEZvcmVpZ24gS2V5KioKCkltYWdpbmUgd2UgaGF2ZSBhbm90aGVyIHRhYmxlIG5hbWVkICpyb2xlcyo6CgpgYGAgc3FsCkNSRUFURSBUQUJMRSByb2xlcyAoCiAgICBpZCBJTlRFR0VSIFBSSU1BUlkgS0VZLAogICAgcm9sZV9uYW1lIFRFWFQKKTsKYGBgCgpXZSBjYW4gY3JlYXRlIGEgZm9yZWlnbiBrZXkgaW4gdGhlICp1c2VyX3JvbGVzKiB0YWJsZSBmcm9tIGFib3ZlIHRoYXQgcmVmZXJlbmNlcyB0aGUgKmlkKiBjb2x1bW4gaW4gdGhlICpyb2xlcyogdGFibGU6CgpgYGAgc3FsCkNSRUFURSBUQUJMRSB1c2VyX3JvbGVzICgKICAgIHVzZXJfaWQgSU5URUdFUiwKICAgIHJvbGVfaWQgSU5URUdFUiwKICAgIFBSSU1BUlkgS0VZICh1c2VyX2lkLCByb2xlX2lkKSwKICAgIEZPUkVJR04gS0VZIChyb2xlX2lkKSBSRUZFUkVOQ0VTIHJvbGVzKGlkKQopOwpgYGAKCkhlcmUsICpyb2xlX2lkKiBpbiB0aGUgKnVzZXJfcm9sZXMqIHRhYmxlIGlzIGEgZm9yZWlnbiBrZXkgdGhhdCByZWZlcmVuY2VzIHRoZSAqaWQqIGluIHRoZSByb2xlcyB0YWJsZS4KCiMjIyMgRW5mb3JjaW5nIEZvcmVpZ24gS2V5IENvbnN0cmFpbnRzCgpTUUxpdGUgaGFzIGZvcmVpZ24ga2V5IGNvbnN0cmFpbnRzIGRpc2FibGVkIGJ5IGRlZmF1bHQuIFRvIGVuZm9yY2UgdGhlbSwgeW91IG5lZWQgdG8gZW5hYmxlIGl0IGluIHlvdXIgU1FMaXRlIHNlc3Npb24gc2V0dGluZyB0aGUgKlBSQUdNQSogYmVsb3c6CgpgYGAgcHl0aG9uCmNvbm4gPSBzcWxpdGUzLmNvbm5lY3QoJ215X2RhdGFiYXNlLmRiJykKY29ubi5leGVjdXRlKCJQUkFHTUEgZm9yZWlnbl9rZXlzID0gT04iKQpgYGAKClRoaXMgY29tbWFuZCBlbnN1cmVzIHRoYXQgYWxsIGZvcmVpZ24ga2V5IGNvbnN0cmFpbnRzIGFyZSBlbmZvcmNlZCBpbiB5b3VyIGRhdGFiYXNlIHNlc3Npb24uIE5vdGUgdGhhdCB0aGUgZW5mb3JjZW1lbnQgb25seSBhcHBsaWVzIHRvIHN1YnNlcXVlbnQgaW5zZXJ0aW9ucyBvZiBkYXRhIGFuZCBpcyBub3QgY2hlY2tlZCByZXRyb2FjdGl2ZWx5LCBzbyBpdCBpcyBzdGlsbCBwb3NzaWJsZSB0byBoYXZlIHJlZmVyZW50aWFsIGludGVncml0eSBpc3N1ZXMgYW5kICJkYW5nbGluZyByZWZlcmVuY2VzIiBpZiBpbnNlcnRpb25zIHdlcmUgZG9uZSBwcmlvciB0byB0dXJuaW5nIG9uIGVuZm9yY2VtZW50LgoKVG8gc3VtbWFyaXplLCBhIHByaW1hcnkga2V5IHVuaXF1ZWx5IGlkZW50aWZpZXMgZWFjaCByZWNvcmQgaW4gYSB0YWJsZSBhbmQgY2FuIGJlIGEgc2luZ2xlIGNvbHVtbiBvciBhIGNvbWJpbmF0aW9uIG9mIGNvbHVtbnMsIHdoaWxlIGEgZm9yZWlnbiBrZXkgaXMgdXNlZCB0byBsaW5rIHR3byB0YWJsZXMgdG9nZXRoZXIsIGVuc3VyaW5nIHRoYXQgdGhlIGRhdGEgaW4gb25lIHRhYmxlIHJlbGF0ZXMgY29ycmVjdGx5IHRvIGRhdGEgaW4gYW5vdGhlci4gRGVmaW5pbmcgdGhlc2Uga2V5cyBjb3JyZWN0bHkgaXMgZXNzZW50aWFsIGZvciBkYXRhIGludGVncml0eSBhbmQgZWZmaWNpZW50LCBhbmQgaXMgYSBrZXkgZWxlbWVudCBvZiB0aGUgcmVsYXRpb25hbCBtb2RlbCBhbmQgcmVsYXRpb25hbCBkYXRhYmFzZXMuIFRoZXNlIGxpbmtzIGFyZSBuZWNlc3Nhcnkgd2hlbiBleGVjdXRpbmcgU1FMICJqb2lucyIuCgojIyMgRGF0YSBUeXBlcyBpbiBTUUxpdGUKClNRTGl0ZSB1c2VzIGEgZHluYW1pYyB0eXBpbmcgc3lzdGVtLiBTb21lIG9mIHRoZSBjb21tb24gZGF0YSB0eXBlcyBpbmNsdWRlOgoKLSAgIGBJTlRFR0VSYDogZm9yIGludGVnZXJzCi0gICBgUkVBTGA6IGZvciBmbG9hdGluZy1wb2ludCBudW1iZXJzCi0gICBgVEVYVGA6IGZvciB0ZXh0IHN0cmluZ3MKLSAgIGBCTE9CYDogZm9yIGJpbmFyeSBkYXRhCi0gICBgTlVNRVJJQ2A6IGZvciBhbnkgbnVtZXJpYyBkYXRhCi0gICBgREFURWA6IGZvciBkYXRlcwotICAgYERBVEVUSU1FYDogZm9yIGRhdGVzIGFuZCB0aW1lcwoKIyMjIEV4YW1wbGU6IENyZWF0aW5nIGEgVGFibGUKCkxldCdzIGNyZWF0ZSBhIHNpbXBsZSB0YWJsZSBpbiBQeXRob24gbmFtZWQgYHVzZXJzYCB3aXRoIHRocmVlIGNvbHVtbnM6IGBpZGAsIGBuYW1lYCwgYW5kIGBlbWFpbGAgd2l0aCAqaWQqIGFzIHRoZSBwcmltYXJ5IGtleS4KCmBgYCBweXRob24KIyBjb25uZWN0IHRvIHRoZSBTUUxpdGUgZGF0YWJhc2UKY29ubiA9IHNxbGl0ZTMuY29ubmVjdCgndXNlckRCLnNxbGl0ZWRiJykKY3Vyc29yID0gY29ubi5jdXJzb3IoKQoKIyBjcmVhdGUgdGFibGUgd2l0aCAzIGNvbHVtbnMKY3Vyc29yLmV4ZWN1dGUoJycnCiAgICBDUkVBVEUgVEFCTEUgdXNlcnMgKAogICAgICAgIGlkIElOVEVHRVIgUFJJTUFSWSBLRVksCiAgICAgICAgbmFtZSBURVhUIE5PVCBOVUxMLAogICAgICAgIGVtYWlsIFRFWFQgTk9UIE5VTEwgVU5JUVVFCiAgICApCicnJykKCiMgY29tbWl0IHRoZSBjaGFuZ2VzCmNvbm4uY29tbWl0KCkKCiMgY2xvc2UgZGF0YWJhc2UgY29ubmVjdGlvbgpjb25uLmNsb3NlKCkKYGBgCgojIyMjIEVuc3VyaW5nIFRhYmxlIENyZWF0aW9uCgpTb21ldGltZXMsIHlvdSBtaWdodCBydW4gdGhlIHNjcmlwdCBtdWx0aXBsZSB0aW1lcywgYW5kIGF0dGVtcHRpbmcgdG8gY3JlYXRlIGFuIGFscmVhZHkgZXhpc3RpbmcgdGFibGUgd2lsbCByZXN1bHQgaW4gYW4gZXJyb3IuIFRvIHByZXZlbnQgdGhpcywgeW91IGNhbiB1c2UgYElGIE5PVCBFWElTVFNgIGluIHlvdXIgYENSRUFURSBUQUJMRWAgc3RhdGVtZW50OgoKYGBgIHB5dGhvbgpjdXJzb3IuZXhlY3V0ZSgnJycKICAgIENSRUFURSBUQUJMRSBJRiBOT1QgRVhJU1RTIHVzZXJzICgKICAgICAgICBpZCBJTlRFR0VSIFBSSU1BUlkgS0VZLAogICAgICAgIG5hbWUgVEVYVCBOT1QgTlVMTCwKICAgICAgICBlbWFpbCBURVhUIE5PVCBOVUxMIFVOSVFVRQogICAgKQonJycpCmBgYAoKVGhpcyB3aWxsIG9ubHkgY3JlYXRlIHRoZSB0YWJsZSBpZiB0aGUgdGFibGUgZG9lcyBub3QgZXhpc3QuIElmIHlvdSB3aXNoIHRvIGFsdGVyIHRoZSB0YWJsZSBkZWZpbml0aW9uIGl0IGlzIG5lY2Vzc2FyeSB0byBmaXJzdCBkZWxldGUgdGhlIHRhYmxlIGZyb20gdGhlIGRhdGFiYXNlICh3aGljaCB3aWxsIGFsc28gZGVsZXRlIGFueSBkYXRhIGluIHRoZSB0YWJsZSwgc28gY2F1dGlvbiBpcyBhZHZpc2VkKS4gRGVsZXRpbmcgYSB0YWJsZSBpcyBkb25lIHdpdGggdGhlICpEUk9QKiBzdGF0ZW1lbnQgYXMgc2hvd24gYmVsb3cuCgpgYGAgcHl0aG9uCmN1cnNvci5leGVjdXRlKCcnJwogICAgRFJPUCBUQUJMRSBJRiBFWElTVFMgdXNlcnMKJycnKQpgYGAKCkluIHRoaXMgc2VjdGlvbiB3ZSBsZWFybmVkIGhvdyB0byBjcmVhdGUgdGFibGVzIGluIGEgU1FMaXRlIGRhdGFiYXNlIHVzaW5nIFB5dGhvbi4gV2UgY292ZXJlZCB0aGUgU1FMIHN5bnRheCBmb3IgY3JlYXRpbmcgdGFibGVzLCBkZW1vbnN0cmF0ZWQgaG93IHRvIGNyZWF0ZSBwcmltYXJ5IGFuZCBmb3JlaWduIGtleXMsIGRpc2N1c3NlZCBTUUxpdGUgZGF0YSB0eXBlcywgYW5kIHdhbGtlZCB0aHJvdWdoIGFuIGV4YW1wbGUgb2YgY3JlYXRpbmcgYSBzaW1wbGUgYHVzZXJzYCB0YWJsZS4gVGhlIGFiaWxpdHkgdG8gY3JlYXRlIHRhYmxlcyBpcyBmb3VuZGF0aW9uYWwgZm9yIHN0b3JpbmcgYW5kIG9yZ2FuaXppbmcgZGF0YSBlZmZlY3RpdmVseSBpbiBhIGRhdGFiYXNlLiBJbiB0aGUgbmV4dCBzZWN0aW9ucywgd2Ugd2lsbCBleHBsb3JlIGhvdyB0byBpbnNlcnQsIHJldHJpZXZlLCB1cGRhdGUsIGFuZCBkZWxldGUgZGF0YSBpbiB0aGVzZSB0YWJsZXMuCgojIyBJbnNlcnRpbmcgRGF0YSBpbnRvIFRhYmxlcwoKSGF2aW5nIGNyZWF0ZWQgb3VyIHRhYmxlcywgd2Ugbm93IHR1cm4gdG8gcG9wdWxhdGluZyB0aGVtIHdpdGggZGF0YS4gSW5zZXJ0aW5nIGRhdGEgaW50byBTUUxpdGUgdGFibGVzIHVzaW5nIFB5dGhvbiBpbnZvbHZlcyB0aGUgKklOU0VSVCBJTlRPKiBTUUwgc3RhdGVtZW50LiBMZXQncyBleHBsb3JlIGhvdyB0byBpbnNlcnQgZGF0YSBpbnRvIHRhYmxlcywgaGFuZGxlIGV4Y2VwdGlvbnMsIGFuZCBlbnN1cmUgZGF0YSBpbnRlZ3JpdHkgdGhyb3VnaCBQeXRob24ncyAqKnNxbGl0ZTMqKiBtb2R1bGUuCgojIyMgQmFzaWMgSW5zZXJ0IFN0YXRlbWVudAoKVGhlICpJTlNFUlQgSU5UTyogc3RhdGVtZW50IGlzIHVzZWQgdG8gYWRkIG5ldyByb3dzIG9mIGRhdGEgdG8gYSB0YWJsZS4KCiMjIyMgU3ludGF4CgpgYGAgc3FsCklOU0VSVCBJTlRPIHRhYmxlX25hbWUgKGNvbHVtbjEsIGNvbHVtbjIsIGNvbHVtbjMsIC4uLikKICAgICBWQUxVRVMgKHZhbHVlMSwgdmFsdWUyLCB2YWx1ZTMsIC4uLik7CmBgYAoKKipFeGFtcGxlOiBJbnNlcnRpbmcgRGF0YSBpbnRvIFVzZXJzIFRhYmxlKioKCkxldCdzIGluc2VydCBhIG5ldyB1c2VyIGludG8gdGhlIHVzZXJzIHRhYmxlIHdlIGNyZWF0ZWQgZWFybGllci4KCmBgYCBweXRob24KIyBBc3N1bWluZyBjb25uZWN0aW9uIChjb25uKSBhbmQgY3Vyc29yIChjdXJzb3IpIGFyZSBhbHJlYWR5IHNldCB1cAoKIyBJbnNlcnQgYSBuZXcgdXNlcgpjdXJzb3IuZXhlY3V0ZSgiSU5TRVJUIElOVE8gdXNlcnMgKG5hbWUsIGVtYWlsKSBWQUxVRVMgKD8sID8pIiwgKCJKb2huIERvZSIsICJqb2huQGV4YW1wbGUuY29tIikpCgojIENvbW1pdCB0aGUgY2hhbmdlcwpjb25uLmNvbW1pdCgpCmBgYAoKSW4gdGhpcyBleGFtcGxlLCB3ZSB1c2UgPyBhcyBwbGFjZWhvbGRlcnMgZm9yIG91ciB2YWx1ZXMgaW4gdGhlICpJTlNFUlQgSU5UTyogc3RhdGVtZW50LiBUaGlzIGlzIGEgcGFyYW1ldGVyaXplZCBxdWVyeSB0aGF0IGhlbHBzIHByZXZlbnQgU1FMIGluamVjdGlvbiBhdHRhY2tzLCBhIGNvbW1vbiB3ZWIgc2VjdXJpdHkgdnVsbmVyYWJpbGl0eS4gVGhlIHZhbHVlcyBmb3IgdGhlIHBsYWNlaG9sZGVycyBhcmUgcHJvdmlkZWQgYXMgYSB0dXBsZSAoIkpvaG4gRG9lIiwgIltqb2huXEBleGFtcGxlLmNvbV0obWFpbHRvOmpvaG5AZXhhbXBsZS5jb20pey5lbWFpbH0iKS4KCiMjIyBIYW5kbGluZyBFeGNlcHRpb25zCgpXaGVuIHdvcmtpbmcgd2l0aCBkYXRhYmFzZXMsIGl0J3MgaW1wb3J0YW50IHRvIGhhbmRsZSBleGNlcHRpb25zIHRoYXQgbWF5IG9jY3VyIGR1cmluZyBkYXRhIGluc2VydGlvbiwgc3VjaCBhcyBpbnNlcnRpbmcgZGF0YSB0aGF0IHZpb2xhdGVzIHByaW1hcnkga2V5IG9yIGZvcmVpZ24ga2V5IGNvbnN0cmFpbnRzLgoKYGBgIHB5dGhvbgp0cnk6CiAgICBjdXJzb3IuZXhlY3V0ZSgiSU5TRVJUIElOVE8gdXNlcnMgKG5hbWUsIGVtYWlsKSBWQUxVRVMgKD8sID8pIiwgKCJKYW5lIERvZSIsICJqYW5lQGV4YW1wbGUuY29tIikpCiAgICBjb25uLmNvbW1pdCgpCmV4Y2VwdCBzcWxpdGUzLkludGVncml0eUVycm9yOgogICAgcHJpbnQoIkNvdWxkbid0IGluc2VydCBkYXRhIGR1ZSB0byBhbiBpbnRlZ3JpdHkgZXJyb3IuIikKYGBgCgpJbiB0aGlzIGNvZGUgc25pcHBldCwgd2UgY2F0Y2ggYHNxbGl0ZTMuSW50ZWdyaXR5RXJyb3JgIGV4Y2VwdGlvbnMsIHdoaWNoIG1heSBvY2N1ciBkdWUgdG8gY29uc3RyYWludCB2aW9sYXRpb25zLgoKIyMjIEJ1bGsgSW5zZXJ0aW9uCgpJZiB5b3UgaGF2ZSBtdWx0aXBsZSByZWNvcmRzIHRvIGluc2VydCwgeW91IGNhbiB1c2UgYGV4ZWN1dGVtYW55YCB0byBwZXJmb3JtIGJ1bGsgaW5zZXJ0aW9ucyBlZmZpY2llbnRseS4KCmBgYCBweXRob24KdXNlcnNfdG9faW5zZXJ0ID0gWygiQWxpY2UiLCAiYWxpY2VAZXhhbXBsZS5jb20iKSwgKCJCb2IiLCAiYm9iQGV4YW1wbGUuY29tIildCmN1cnNvci5leGVjdXRlbWFueSgiSU5TRVJUIElOVE8gdXNlcnMgKG5hbWUsIGVtYWlsKSBWQUxVRVMgKD8sID8pIiwgdXNlcnNfdG9faW5zZXJ0KQpjb25uLmNvbW1pdCgpCmBgYAoKVGhpcyBtZXRob2QgaXMgbW9yZSBlZmZpY2llbnQgdGhhbiBpbnNlcnRpbmcgZWFjaCByb3cgaW4gYSBzZXBhcmF0ZSB0cmFuc2FjdGlvbiwgZXNwZWNpYWxseSB3aGVuIGRlYWxpbmcgd2l0aCBhIGxhcmdlIHZvbHVtZSBvZiBkYXRhLgoKIyMjIEF1dG8tSW5jcmVtZW50aW5nIFByaW1hcnkgS2V5cwoKSWYgYSBjb2x1bW4gaW4gYSB0YWJsZSBpcyBkZWZpbmVkIGFzICpJTlRFR0VSIFBSSU1BUlkgS0VZKiwgU1FMaXRlIHRyZWF0cyBpdCBhcyBhbiAiYXV0by1pbmNyZW1lbnQiIGZpZWxkLiBUaGlzIG1lYW5zIHlvdSBkb24ndCBuZWVkIHRvIHNwZWNpZnkgYSB2YWx1ZSBmb3IgdGhpcyBjb2x1bW47IFNRTGl0ZSB3aWxsIGF1dG9tYXRpY2FsbHkgYXNzaWduIGEgdW5pcXVlIHZhbHVlIGJ5IGluY3JlbWVudGluZyBzb21lIGNvdW50ZXIuIERvIG5vdCBhc3N1bWUgdGhhdCB0aGUgcHJpbWFyeSBrZXkgdmFsdWVzIGFyZSBzZXF1ZW50aWFsLgoKIyMgUmV0cmlldmluZyBEYXRhIHdpdGggU0VMRUNUCgpPbmNlIGRhdGEgaXMgc3RvcmVkIGluIGEgU1FMaXRlIGRhdGFiYXNlLCB0aGUgbmV4dCBjcml0aWNhbCBvcGVyYXRpb24gaXMgcmV0cmlldmluZyBpdC4gVGhlIGBTRUxFQ1RgIFNRTCBzdGF0ZW1lbnQgaXMgdXNlZCBmb3IgdGhpcyBwdXJwb3NlLiBJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgZXhwbG9yZSBob3cgdG8gdXNlIGBTRUxFQ1RgIHRvIHJldHJpZXZlIGRhdGEgZnJvbSBhIHRhYmxlIGluIFNRTGl0ZSwgZmlsdGVyIGl0IHdpdGggY29uZGl0aW9ucywgYW5kIGhhbmRsZSB0aGUgcmVzdWx0cyBpbiBQeXRob24uIFRoaXMgaXMgb25seSBhbiBvdmVydmlldyB0byBkZW1vbnN0cmF0ZSBob3cgdG8gZXhlY3V0ZSBxdWVyaWVzIGZyb20gUHl0aG9uLiBDb25zdWx0IGFkZGl0aW9uYWwgbGVzc29ucyBvbiBTUUwgZm9yIG1vcmUgZGV0YWlscyBvbiBTUUwgKlNFTEVDVC4qCgojIyMgQmFzaWMgU0VMRUNUIFN0YXRlbWVudAoKVGhlIGBTRUxFQ1RgIHN0YXRlbWVudCBpcyB1c2VkIHRvIHNlbGVjdCBkYXRhIGZyb20gYSBkYXRhYmFzZSB0YWJsZS4KCiMjIyMgU3ludGF4CgpgYGAgc3FsClNFTEVDVCBjb2x1bW4xLCBjb2x1bW4yLCAuLi4gRlJPTSB0YWJsZV9uYW1lOwpgYGAKClRvIHNlbGVjdCBhbGwgY29sdW1ucyBmcm9tIGEgdGFibGUsIHVzZSBgKmAuCgojIyMgRXhhbXBsZTogU2VsZWN0aW5nIERhdGEKCkhlcmUncyBob3cgdG8gc2VsZWN0IGFsbCBkYXRhIGZyb20gdGhlIGB1c2Vyc2AgdGFibGU6CgpgYGAgcHl0aG9uCmN1cnNvci5leGVjdXRlKCJTRUxFQ1QgKiBGUk9NIHVzZXJzIikKcm93cyA9IGN1cnNvci5mZXRjaGFsbCgpCmZvciByb3cgaW4gcm93czoKICAgIHByaW50KHJvdykKYGBgCgotICAgYGN1cnNvci5mZXRjaGFsbCgpYCByZXRyaWV2ZXMgYWxsIHJvd3Mgb2YgdGhlIHF1ZXJ5IHJlc3VsdCwgcmV0dXJuaW5nIHRoZW0gYXMgYSBsaXN0IG9mIHR1cGxlcwotICAgdGhlIGxvb3AgdGhlbiBwcmludHMgZWFjaCByb3cKCiMjIyBTZWxlY3RpbmcgU3BlY2lmaWMgQ29sdW1ucwoKVG8gc2VsZWN0IG9ubHkgc3BlY2lmaWMgY29sdW1uczoKCmBgYCBweXRob24KY3Vyc29yLmV4ZWN1dGUoIlNFTEVDVCBuYW1lLCBlbWFpbCBGUk9NIHVzZXJzIikKcm93cyA9IGN1cnNvci5mZXRjaGFsbCgpCmZvciByb3cgaW4gcm93czoKICAgIHByaW50KHJvdykKYGBgCgojIyMgRmlsdGVyaW5nIFJlc3VsdHMgd2l0aCBXSEVSRQoKWW91IGNhbiBmaWx0ZXIgcXVlcnkgcmVzdWx0cyB1c2luZyB0aGUgYFdIRVJFYCBjbGF1c2UuCgojIyMjIEV4YW1wbGU6IEZpbHRlcmluZyBEYXRhCgpUbyBmaW5kIHVzZXJzIHdpdGggYSBzcGVjaWZpYyBuYW1lOgoKYGBgIHB5dGhvbgpjdXJzb3IuZXhlY3V0ZSgiU0VMRUNUICogRlJPTSB1c2VycyBXSEVSRSBuYW1lID0gPyIsICgiSm9obiBEb2UiLCkpCnJvd3MgPSBjdXJzb3IuZmV0Y2hhbGwoKQpmb3Igcm93IGluIHJvd3M6CiAgICBwcmludChyb3cpCmBgYAoKLSAgIHRoaXMgcXVlcnkgcmV0cmlldmVzIHJvd3MgZnJvbSBgdXNlcnNgIHdoZXJlIHRoZSBgbmFtZWAgY29sdW1uIG1hdGNoZXMgIkpvaG4gRG9lIi4KCiMjIyBTb3J0aW5nIFJlc3VsdHMKClVzZSB0aGUgYE9SREVSIEJZYCBjbGF1c2UgdG8gc29ydCB0aGUgcmVzdWx0cy4KCiMjIyMgRXhhbXBsZTogU29ydGluZyBEYXRhCgpUbyBzb3J0IHVzZXJzIGJ5IG5hbWUgaW4gYXNjZW5kaW5nIG9yZGVyOgoKYGBgIHB5dGhvbgpjdXJzb3IuZXhlY3V0ZSgiU0VMRUNUICogRlJPTSB1c2VycyBPUkRFUiBCWSBuYW1lIEFTQyIpCnJvd3MgPSBjdXJzb3IuZmV0Y2hhbGwoKQpmb3Igcm93IGluIHJvd3M6CiAgICBwcmludChyb3cpCmBgYAoKIyMjIExpbWl0aW5nIFJlc3VsdHMKClRoZSBgTElNSVRgIGNsYXVzZSBjYW4gYmUgdXNlZCB0byBsaW1pdCB0aGUgbnVtYmVyIG9mIHJlc3VsdHMgcmV0dXJuZWQuCgojIyMjIEV4YW1wbGU6IExpbWl0aW5nIERhdGEKClRvIGdldCB0aGUgZmlyc3QgNSB1c2VyczoKCmBgYCBweXRob24KY3Vyc29yLmV4ZWN1dGUoIlNFTEVDVCAqIEZST00gdXNlcnMgTElNSVQgNSIpCnJvd3MgPSBjdXJzb3IuZmV0Y2hhbGwoKQpmb3Igcm93IGluIHJvd3M6CiAgICBwcmludChyb3cpCmBgYAoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBkaXNjdXNzZWQgaG93IHRvIHJldHJpZXZlIGRhdGEgZnJvbSBhIFNRTGl0ZSBkYXRhYmFzZSB1c2luZyB0aGUgYFNFTEVDVGAgc3RhdGVtZW50LiBXZSBjb3ZlcmVkIGhvdyB0byBzZWxlY3QgYWxsIG9yIHNwZWNpZmljIGNvbHVtbnMsIGZpbHRlciByZXN1bHRzIHVzaW5nIGBXSEVSRWAsIHNvcnQgcmVzdWx0cyB3aXRoIGBPUkRFUiBCWWAsIGFuZCBsaW1pdCB0aGUgbnVtYmVyIG9mIHJlc3VsdHMgcmV0dXJuZWQgdXNpbmcgYExJTUlUYC4gVGhlc2UgdG9vbHMgYXJlIGZ1bmRhbWVudGFsIGZvciBlZmZpY2llbnRseSBxdWVyeWluZyBhbmQgaGFuZGxpbmcgZGF0YSBpbiBhIFNRTGl0ZSBkYXRhYmFzZS4gVGhlIG5leHQgc2VjdGlvbiB3aWxsIGZvY3VzIG9uIHByb2Nlc3NpbmcgdGhlIHJlc3VsdHMgb2YgdGhlc2UgcXVlcmllcyBpbiBQeXRob24uCgpBZnRlciByZXRyaWV2aW5nIGRhdGEgZnJvbSBhIFNRTGl0ZSBkYXRhYmFzZSB1c2luZyB0aGUgU0VMRUNUIHN0YXRlbWVudCwgdGhlIG5leHQgc3RlcCBpcyB0byBwcm9jZXNzIHRoZXNlIHJlc3VsdHMgaW4gUHl0aG9uLiBTUUxpdGUgcmV0dXJucyBxdWVyeSByZXN1bHRzIGFzIGEgbGlzdCBvZiB0dXBsZXMsIHdoZXJlIGVhY2ggdHVwbGUgY29ycmVzcG9uZHMgdG8gYSByb3cgaW4gdGhlIGRhdGFiYXNlLiBMZXQncyBleHBsb3JlIGhvdyB0byBoYW5kbGUgYW5kIHV0aWxpemUgdGhlc2UgcXVlcnkgcmVzdWx0cyBlZmZlY3RpdmVseS4KCiMjIFByb2Nlc3NpbmcgUXVlcnkgUmVzdWx0cwoKV2hlbiB5b3UgZXhlY3V0ZSBhIFNFTEVDVCBzdGF0ZW1lbnQgdXNpbmcgdGhlICoqc3FsaXRlMyoqIG1vZHVsZSBpbiBQeXRob24sIHRoZSByZXN1bHRzIGFyZSB0eXBpY2FsbHkgcmV0dXJuZWQgYXMgYSBsaXN0IG9mIHR1cGxlcy4gRWFjaCB0dXBsZSByZXByZXNlbnRzIGEgZGF0YWJhc2UgcmVjb3JkLCBhbmQgZWFjaCBpdGVtIHdpdGhpbiB0aGUgdHVwbGUgY29ycmVzcG9uZHMgdG8gYSBjb2x1bW4gaW4gdGhlIHRhYmxlLgoKIyMjIEZldGNoIE1ldGhvZHMKClRoZXJlIGFyZSBzZXZlcmFsIG1ldGhvZHMgdG8gcmV0cmlldmUgcXVlcnkgcmVzdWx0czoKCi0gICBgZmV0Y2hvbmUoKWA6IFJldHJpZXZlcyB0aGUgbmV4dCByb3cgb2YgYSBxdWVyeSByZXN1bHQgc2V0LCByZXR1cm5pbmcgYSBzaW5nbGUgdHVwbGUsIG9yIE5vbmUgd2hlbiBubyBtb3JlIGRhdGEgaXMgYXZhaWxhYmxlLgotICAgYGZldGNoYWxsKClgOiBGZXRjaGVzIGFsbCAocmVtYWluaW5nKSByb3dzIG9mIGEgcXVlcnkgcmVzdWx0LCByZXR1cm5pbmcgYSBsaXN0IG9mIHR1cGxlcy4KLSAgIGBmZXRjaG1hbnkoc2l6ZSlgOiBGZXRjaGVzIHRoZSBuZXh0IHNldCBvZiByb3dzIG9mIGEgcXVlcnkgcmVzdWx0LCByZXR1cm5pbmcgYSBsaXN0LiBZb3UgY2FuIHNwZWNpZnkgdGhlIG51bWJlciBvZiByb3dzIHRvIHJldHJpZXZlLgoKKipFeGFtcGxlOiBQcm9jZXNzaW5nIFJlc3VsdHMgd2l0aCBmZXRjaGFsbCgpKioKCkhlcmUncyBhIGJhc2ljIGV4YW1wbGUgb2YgZXhlY3V0aW5nIGEgU0VMRUNUIHN0YXRlbWVudCBhbmQgdXNpbmcgYGZldGNoYWxsKClgOgoKYGBgIHB5dGhvbgpjdXJzb3IuZXhlY3V0ZSgiU0VMRUNUICogRlJPTSB1c2VycyIpCnJvd3MgPSBjdXJzb3IuZmV0Y2hhbGwoKQpmb3Igcm93IGluIHJvd3M6CiAgICBwcmludChmIklEOiB7cm93WzBdfSwgTmFtZToge3Jvd1sxXX0sIEVtYWlsOiB7cm93WzJdfSIpCmBgYAoKSW4gdGhpcyBleGFtcGxlLCB3ZSBpdGVyYXRlIG92ZXIgZWFjaCByb3cgaW4gYSBsb29wIGFuZCBhY2Nlc3MgaW5kaXZpZHVhbCBjb2x1bW4gdmFsdWVzIHVzaW5nIGluZGV4aW5nIChyb3dbMF0sIHJvd1sxXSwgcm93WzJdLCAqZXRjLiopLgoKIyMjIyBXb3JraW5nIHdpdGggTmFtZWQgVHVwbGVzCgpGb3IgYmV0dGVyIGNvZGUgcmVhZGFiaWxpdHksIHlvdSBjYW4gdXNlIG5hbWVkIHR1cGxlcy4gVGhpcyBtYWtlcyB5b3VyIGNvZGUgbW9yZSByZWFkYWJsZSBieSBhY2Nlc3NpbmcgY29sdW1uIHZhbHVlcyBieSBuYW1lLgoKYGBgIHB5dGhvbgppbXBvcnQgc3FsaXRlMwpmcm9tIGNvbGxlY3Rpb25zIGltcG9ydCBuYW1lZHR1cGxlCgojIENvbm5lY3QgdG8gU1FMaXRlIGRhdGFiYXNlCmNvbm4gPSBzcWxpdGUzLmNvbm5lY3QoJ215X2RhdGFiYXNlLmRiJykKY29ubi5yb3dfZmFjdG9yeSA9IHNxbGl0ZTMuUm93ICAjIHVzZSBzcWxpdGUzLlJvdyBmb3IgbmFtZWQgdHVwbGVzCmN1cnNvciA9IGNvbm4uY3Vyc29yKCkKCmN1cnNvci5leGVjdXRlKCJTRUxFQ1QgKiBGUk9NIHVzZXJzIikKcm93cyA9IGN1cnNvci5mZXRjaGFsbCgpCmZvciByb3cgaW4gcm93czoKICAgIHVzZXIgPSBuYW1lZHR1cGxlKCdVc2VyJywgcm93LmtleXMoKSkoKnJvdykKICAgIHByaW50KGYiSUQ6IHt1c2VyLmlkfSwgTmFtZToge3VzZXIubmFtZX0sIEVtYWlsOiB7dXNlci5lbWFpbH0iKQpgYGAKCi0gICBgc3FsaXRlMy5Sb3dgIHByb3ZpZGVzIGJvdGggaW5kZXgtYmFzZWQgYW5kIGNhc2UtaW5zZW5zaXRpdmUgbmFtZS1iYXNlZCBhY2Nlc3MgdG8gY29sdW1ucyB3aXRoIGFsbW9zdCBubyBtZW1vcnkgb3ZlcmhlYWQuCi0gICBXZSB0aGVuIGNyZWF0ZSBhIG5hbWVkIHR1cGxlIFVzZXIgZm9yIGVhY2ggcm93LCBtYWtpbmcgdGhlIGNvZGUgbW9yZSBleHByZXNzaXZlLgoKIyMjIyBNYXBwaW5nIFJlc3VsdHMgdG8gYSBEaWN0aW9uYXJ5CgpBbHRlcm5hdGl2ZWx5LCB5b3UgY2FuIG1hcCByZXN1bHRzIHRvIGEgZGljdGlvbmFyeSBmb3IgZWFzZSBvZiB1c2UuCgpgYGAgcHl0aG9uCmN1cnNvci5leGVjdXRlKCJTRUxFQ1QgKiBGUk9NIHVzZXJzIikKcm93cyA9IGN1cnNvci5mZXRjaGFsbCgpCnVzZXJzID0gW2RpY3Qocm93KSBmb3Igcm93IGluIHJvd3NdCmZvciB1c2VyIGluIHVzZXJzOgogICAgcHJpbnQodXNlcikKYGBgCgpUaGlzIGFwcHJvYWNoIGNvbnZlcnRzIGVhY2ggcm93IGludG8gYSBkaWN0aW9uYXJ5IHdoZXJlIGNvbHVtbiBuYW1lcyBhcmUga2V5cywgYWxsb3dpbmcgeW91IHRvIGFjY2VzcyBkYXRhIGJ5IGNvbHVtbiBuYW1lcy4KCkluIHRoaXMgc2VjdGlvbiwgd2UgZGlzY3Vzc2VkIGhvdyB0byBwcm9jZXNzIGFuZCBoYW5kbGUgdGhlIHJlc3VsdHMgb2J0YWluZWQgZnJvbSBTUUxpdGUgcXVlcmllcyBpbiBQeXRob24uIFdlIGxvb2tlZCBhdCBkaWZmZXJlbnQgZmV0Y2ggbWV0aG9kcyAoYGZldGNob25lYCwgYGZldGNoYWxsYCwgYGZldGNobWFueWApLCBhbmQgZXhwbG9yZWQgaG93IHRvIG1ha2UgcXVlcnkgcmVzdWx0cyBtb3JlIHJlYWRhYmxlIGFuZCBtYW5hZ2VhYmxlIHVzaW5nIG5hbWVkIHR1cGxlcyBhbmQgZGljdGlvbmFyaWVzLiBVbmRlcnN0YW5kaW5nIHRoZXNlIG1ldGhvZHMgaXMgY3J1Y2lhbCBmb3IgZWZmZWN0aXZlIGRhdGEgbWFuaXB1bGF0aW9uIGFuZCByZXRyaWV2YWwgaW4gUHl0aG9uLWJhc2VkIGRhdGFiYXNlIGFwcGxpY2F0aW9ucy4gTmV4dCwgd2Ugd2lsbCBtb3ZlIG9uIHRvIHVwZGF0aW5nIGFuZCBkZWxldGluZyBkYXRhIGluIHRhYmxlcy4KCiMjIFVwZGF0aW5nIGFuZCBEZWxldGluZyBEYXRhCgpBZnRlciBpbnNlcnRpbmcgYW5kIHJldHJpZXZpbmcgZGF0YSBpbiBTUUxpdGUgdXNpbmcgUHl0aG9uLCB5b3UgbWF5IG5lZWQgdG8gdXBkYXRlIG9yIGRlbGV0ZSByZWNvcmRzLiBUaGlzIHNlY3Rpb24gY292ZXJzIGhvdyB0byBtb2RpZnkgZXhpc3RpbmcgZGF0YSB3aXRoIHRoZSAqVVBEQVRFKiBzdGF0ZW1lbnQgYW5kIHJlbW92ZSByZWNvcmRzIHdpdGggdGhlICpERUxFVEUqIHN0YXRlbWVudC4KCiMjIyBVcGRhdGluZyBEYXRhCgojIyMjIFRoZSBVUERBVEUgU3RhdGVtZW50CgpUaGUgYFVQREFURWAgc3RhdGVtZW50IGlzIHVzZWQgdG8gbW9kaWZ5IGV4aXN0aW5nIHJlY29yZHMgaW4gYSB0YWJsZS4KCiMjIyMgU3ludGF4CgpgYGAgc3FsClVQREFURSB0YWJsZV9uYW1lClNFVCBjb2x1bW4xID0gdmFsdWUxLCBjb2x1bW4yID0gdmFsdWUyLCAuLi4KV0hFUkUgY29uZGl0aW9uOwpgYGAKCiMjIyMgRXhhbXBsZTogVXBkYXRpbmcgVXNlciBEYXRhCgpTdXBwb3NlIHdlIHdhbnQgdG8gdXBkYXRlIHRoZSBlbWFpbCBvZiBhIHVzZXIgd2l0aCBhIHNwZWNpZmljICppZC4qCgpgYGAgcHl0aG9uCiMgQXNzdW1pbmcgY29ubmVjdGlvbiAoY29ubikgYW5kIGN1cnNvciAoY3Vyc29yKSBhcmUgYWxyZWFkeSBzZXQgdXAKCiMgVXBkYXRlIHVzZXIncyBlbWFpbApjdXJzb3IuZXhlY3V0ZSgiVVBEQVRFIHVzZXJzIFNFVCBlbWFpbCA9ID8gV0hFUkUgaWQgPSA/IiwgKCJuZXdlbWFpbEBleGFtcGxlLmNvbSIsIDEpKQoKIyBDb21taXQgY2hhbmdlcwpjb25uLmNvbW1pdCgpCmBgYAoKSW4gdGhpcyBleGFtcGxlOgoKLSAgIHdlIHVzZSAqVVBEQVRFKiB0byBjaGFuZ2UgdGhlICplbWFpbCogb2YgdGhlIHVzZXIgd2hlcmUgKmlkKiBlcXVhbHMgMQotICAgaXQncyBpbXBvcnRhbnQgdG8gdXNlIHRoZSAqV0hFUkUqIGNsYXVzZSB0byBzcGVjaWZ5IHdoaWNoIHJlY29yZHMgdG8gdXBkYXRlLiBXaXRob3V0IGl0LCBhbGwgcmVjb3JkcyBpbiB0aGUgdGFibGUgd291bGQgYmUgdXBkYXRlZAoKIyMjIERlbGV0aW5nIERhdGEKCiMjIyMgVGhlIERFTEVURSBTdGF0ZW1lbnQKClRoZSBgREVMRVRFYCBzdGF0ZW1lbnQgaXMgdXNlZCB0byBkZWxldGUgcmVjb3JkcyBmcm9tIGEgdGFibGUuCgojIyMjIFN5bnRheAoKYGBgIHNxbApERUxFVEUgRlJPTSB0YWJsZV9uYW1lIFdIRVJFIGNvbmRpdGlvbjsKYGBgCgojIyMjIEV4YW1wbGU6IERlbGV0aW5nIGEgVXNlcgoKVG8gZGVsZXRlIGEgdXNlciB3aXRoIGEgc3BlY2lmaWMgKmlkKjoKCmBgYCBweXRob24KIyBEZWxldGUgdXNlciB3aXRoIGlkIDEKY3Vyc29yLmV4ZWN1dGUoIkRFTEVURSBGUk9NIHVzZXJzIFdIRVJFIGlkID0gPyIsICgxLCkpCgojIENvbW1pdCBjaGFuZ2VzCmNvbm4uY29tbWl0KCkKYGBgCgotICAgVGhpcyBzdGF0ZW1lbnQgZGVsZXRlcyB0aGUgcmVjb3JkIGZyb20gKnVzZXJzKiB3aGVyZSAqaWQqIGVxdWFscyAxLgotICAgQXMgd2l0aCAqVVBEQVRFKiwgaXQncyBjcnVjaWFsIHRvIHVzZSB0aGUgKldIRVJFKiBjbGF1c2UgdG8gc3BlY2lmeSB3aGljaCByZWNvcmRzIHRvIGRlbGV0ZSB0byBhdm9pZCByZW1vdmluZyBhbGwgcmVjb3JkcyBpbiB0aGUgdGFibGUuCgojIyMgSGFuZGxpbmcgRXhjZXB0aW9ucwoKV2hlbiB1cGRhdGluZyBvciBkZWxldGluZyBkYXRhLCBleGNlcHRpb25zIGNhbiBvY2N1ciwgcGFydGljdWxhcmx5IHJlbGF0aW5nIHRvIGNvbnN0cmFpbnRzIGxpa2UgZm9yZWlnbiBrZXlzLiBJdCdzIGEgZ29vZCBwcmFjdGljZSB0byBoYW5kbGUgdGhlc2UgZXhjZXB0aW9ucy4KCmBgYCBweXRob24KdHJ5OgogICAgY3Vyc29yLmV4ZWN1dGUoIkRFTEVURSBGUk9NIHVzZXJzIFdIRVJFIGlkID0gPyIsICgxLCkpCiAgICBjb25uLmNvbW1pdCgpCmV4Y2VwdCBzcWxpdGUzLkludGVncml0eUVycm9yIGFzIGU6CiAgICBwcmludChmIkFuIGVycm9yIG9jY3VycmVkOiB7ZX0iKQpgYGAKCkluIHRoaXMgc2VjdGlvbiwgd2UgY292ZXJlZCBob3cgdG8gdXBkYXRlIGFuZCBkZWxldGUgZGF0YSBpbiBhIFNRTGl0ZSBkYXRhYmFzZSB1c2luZyBQeXRob24uIFdlIGRpc2N1c3NlZCB0aGUgaW1wb3J0YW5jZSBvZiB1c2luZyB0aGUgKldIRVJFKiBjbGF1c2UgaW4gYm90aCAqVVBEQVRFKiBhbmQgKkRFTEVURSogc3RhdGVtZW50cyB0byB0YXJnZXQgc3BlY2lmaWMgcmVjb3JkcyBhbmQgYXZvaWQgdW5pbnRlbmRlZCBkYXRhIG1vZGlmaWNhdGlvbnMuIFdlIGFsc28gdG91Y2hlZCBvbiB0aGUgaW1wb3J0YW5jZSBvZiBleGNlcHRpb24gaGFuZGxpbmcgdG8gbWFuYWdlIGNvbnN0cmFpbnQgdmlvbGF0aW9ucy4gVGhlc2Ugb3BlcmF0aW9ucyBhcmUgZnVuZGFtZW50YWwgZm9yIG1haW50YWluaW5nIHRoZSBpbnRlZ3JpdHkgYW5kIHJlbGV2YW5jZSBvZiBkYXRhIHdpdGhpbiB5b3VyIGRhdGFiYXNlLiBOZXh0LCB3ZSB3aWxsIGxvb2sgaW50byBmaW5hbGl6aW5nIHRyYW5zYWN0aW9ucyBhbmQgcHJvcGVybHkgY2xvc2luZyB0aGUgZGF0YWJhc2UgY29ubmVjdGlvbi4KCiMjIEZpbmFsaXppbmcgVHJhbnNhY3Rpb25zIGFuZCBDbG9zaW5nIHRoZSBDb25uZWN0aW9uCgpBZnRlciBwZXJmb3JtaW5nIGRhdGFiYXNlIG9wZXJhdGlvbnMgc3VjaCBhcyBjcmVhdGluZyB0YWJsZXMsIGluc2VydGluZywgdXBkYXRpbmcsIG9yIGRlbGV0aW5nIGRhdGEsIGl0J3MgY3J1Y2lhbCB0byBmaW5hbGl6ZSB0cmFuc2FjdGlvbnMgYW5kIHByb3Blcmx5IGNsb3NlIHRoZSBjb25uZWN0aW9uIHRvIHRoZSBTUUxpdGUgZGF0YWJhc2UuIFRoaXMgc2VjdGlvbiBjb3ZlcnMgdGhlIHNpZ25pZmljYW5jZSBvZiBjb21taXR0aW5nIHRyYW5zYWN0aW9ucyBhbmQgc2FmZWx5IGNsb3NpbmcgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb24gdXNpbmcgUHl0aG9uJ3MgKipzcWxpdGUzKiogbW9kdWxlLgoKIyMjIENvbW1pdHRpbmcgVHJhbnNhY3Rpb25zCgpJbiBkYXRhYmFzZSBzeXN0ZW1zLCBhIHRyYW5zYWN0aW9uIGlzIGEgc2VxdWVuY2Ugb2YgZGF0YWJhc2Ugb3BlcmF0aW9ucyB0aGF0IGFyZSB0cmVhdGVkIGFzIGEgc2luZ2xlIHVuaXQuIEluIFNRTGl0ZSwgdGhlc2UgdHJhbnNhY3Rpb25zIGFyZSB1c2VkIHRvIGVuc3VyZSBkYXRhIGludGVncml0eS4gV2hlbiB5b3UgbW9kaWZ5IGRhdGEgKGluc2VydCwgdXBkYXRlLCBkZWxldGUpLCB0aGVzZSBjaGFuZ2VzIGFyZSBub3QgaW1tZWRpYXRlbHkgc2F2ZWQgdG8gdGhlIGRhdGFiYXNlLiBUaGV5IGFyZSBpbnN0ZWFkIGhlbGQgaW4gYSB0ZW1wb3Jhcnkgc3RhdGUgdW50aWwgeW91IGNvbW1pdCB0aGUgdHJhbnNhY3Rpb24uCgojIyMjIFVzaW5nIGBjb21taXQoKWAKClRoZSBgY29tbWl0KClgIG1ldGhvZCBvZiB0aGUgY29ubmVjdGlvbiBvYmplY3QgZmluYWxpemVzIHRoZSB0cmFuc2FjdGlvbiBhbmQgd3JpdGVzIGFsbCBjaGFuZ2VzIHRvIHRoZSBkYXRhYmFzZS4KCmBgYCBweXRob24KIyBQZXJmb3JtIHNvbWUgZGF0YSBtb2RpZmljYXRpb24KY3Vyc29yLmV4ZWN1dGUoIklOU0VSVCBJTlRPIHVzZXJzIChuYW1lLCBlbWFpbCkgVkFMVUVTICg/LCA/KSIsICgiSm9obiBEb2UiLCAiam9obkBleGFtcGxlLmNvbSIpKQoKIyBDb21taXQgdGhlIHRyYW5zYWN0aW9uCmNvbm4uY29tbWl0KCkKYGBgCgpGYWlsaW5nIHRvIGNhbGwgYGNvbW1pdCgpYCB3aWxsIHJlc3VsdCBpbiBjaGFuZ2VzIG5vdCBiZWluZyBzYXZlZCB3aGVuIHRoZSBjb25uZWN0aW9uIGlzIGNsb3NlZC4KCiMjIyMgQXV0by1Db21taXQgTW9kZQoKU1FMaXRlIGFsc28gc3VwcG9ydHMgYW4gYXV0by1jb21taXQgbW9kZSwgd2hpY2ggY2FuIGJlIGFjdGl2YXRlZCBieSBzZXR0aW5nICppc29sYXRpb25fbGV2ZWwqIHRvICpOb25lLiogSW4gYXV0by1jb21taXQgbW9kZSwgZWFjaCBzdGF0ZW1lbnQgaXMgdHJlYXRlZCBhcyBhIHRyYW5zYWN0aW9uIGFuZCBpcyBhdXRvbWF0aWNhbGx5IGNvbW1pdHRlZC4KCmBgYCBweXRob24KY29ubi5pc29sYXRpb25fbGV2ZWwgPSBOb25lCmBgYAoKSG93ZXZlciwgbWFudWFsIGNvbnRyb2wgb2YgdHJhbnNhY3Rpb25zIHVzaW5nIGBjb21taXQoKWAgaXMgcmVjb21tZW5kZWQgZm9yIGJldHRlciBlcnJvciBoYW5kbGluZyBhbmQgcGVyZm9ybWFuY2UsIGVzcGVjaWFsbHkgaW4gYXBwbGljYXRpb25zIHdpdGggbXVsdGlwbGUgb3BlcmF0aW9ucy4KCiMjIyMgUm9sbGluZyBCYWNrIFRyYW5zYWN0aW9ucwoKSWYgYW4gZXJyb3Igb2NjdXJzIGR1cmluZyBhIHRyYW5zYWN0aW9uLCB5b3UgbWF5IG5lZWQgdG8gdW5kbyBhbGwgdGhlIG9wZXJhdGlvbnMgaW4gdGhhdCB0cmFuc2FjdGlvbi4gVGhpcyBpcyBrbm93biBhcyByb2xsaW5nIGJhY2sgYSB0cmFuc2FjdGlvbi4KCmBgYCBweXRob24KdHJ5OgogICAgIyBQZXJmb3JtIGRhdGFiYXNlIG9wZXJhdGlvbnMKICAgIGN1cnNvci5leGVjdXRlKCJJTlNFUlQgSU5UTyB1c2VycyAobmFtZSwgZW1haWwpIFZBTFVFUyAoPywgPykiLCAoIkphbmUgRG9lIiwgImphbmVAZXhhbXBsZS5jb20iKSkKICAgICMgQ29tbWl0IGlmIHN1Y2Nlc3NmdWwKICAgIGNvbm4uY29tbWl0KCkKZXhjZXB0IHNxbGl0ZTMuRXJyb3IgYXMgZXJyb3I6CiAgICAjIFJvbGxiYWNrIGluIGNhc2Ugb2YgYW4gZXJyb3IKICAgIHByaW50KGYiRXJyb3I6IHtlcnJvcn0iKQogICAgY29ubi5yb2xsYmFjaygpCmBgYAoKLSAgIGByb2xsYmFjaygpYCByZXZlcnRzIHRoZSBkYXRhYmFzZSB0byB0aGUgc3RhdGUgYmVmb3JlIHRoZSB0cmFuc2FjdGlvbiBiZWdhbi4KCiMjIyBDbG9zaW5nIHRoZSBDb25uZWN0aW9uCgpQcm9wZXJseSBjbG9zaW5nIHRoZSBkYXRhYmFzZSBjb25uZWN0aW9uIGlzIHZpdGFsIHRvIGZyZWUgdXAgcmVzb3VyY2VzLiBVc2UgdGhlIGBjbG9zZSgpYCBtZXRob2Qgb24gdGhlIGNvbm5lY3Rpb24gb2JqZWN0IHRvIGNsb3NlIHRoZSBjb25uZWN0aW9uLgoKYGBgIHB5dGhvbgojIENsb3NlIHRoZSBjb25uZWN0aW9uCmNvbm4uY2xvc2UoKQpgYGAKCkFsd2F5cyBlbnN1cmUgdGhhdCB0aGUgY29ubmVjdGlvbiBpcyBjbG9zZWQgYWZ0ZXIgYWxsIGRhdGFiYXNlIG9wZXJhdGlvbnMgaGF2ZSBjb21wbGV0ZWQuCgojIyBTdW1tYXJ5CgpJbiB0aGlzIGxlc3Nvbiwgd2UgaGF2ZSBjb3ZlcmVkIHRoZSBmdW5kYW1lbnRhbHMgb2YgdXNpbmcgU1FMaXRlIHdpdGggUHl0aG9uLiBTdGFydGluZyBmcm9tIHNldHRpbmcgdXAgYSBTUUxpdGUgZGF0YWJhc2UgdG8gcGVyZm9ybWluZyB2YXJpb3VzIGRhdGFiYXNlIG9wZXJhdGlvbnMgc3VjaCBhcyBjcmVhdGluZyB0YWJsZXMsIGluc2VydGluZywgdXBkYXRpbmcsIGRlbGV0aW5nLCBhbmQgcmV0cmlldmluZyBkYXRhLCB3ZSBoYXZlIG5hdmlnYXRlZCB0aHJvdWdoIHRoZSBlc3NlbnRpYWwgZWxlbWVudHMgb2YgUHl0aG9uJ3MgYHNxbGl0ZTNgIG1vZHVsZS4KCkxldCdzIGJyaWVmbHkgcmVjYXAgdGhlIGtleSBwb2ludHM6CgoxLiAgKipTUUxpdGUgYW5kIFB5dGhvbiBTZXR1cCoqOiBXZSBiZWdhbiBieSBleHBsb3JpbmcgaG93IHRvIHNldCB1cCBhIFNRTGl0ZSBkYXRhYmFzZSB1c2luZyBQeXRob24ncyBgc3FsaXRlM2AgbW9kdWxlLiBXZSBkaXNjdXNzZWQgY3JlYXRpbmcgYSBuZXcgZGF0YWJhc2UgYW5kIGVzdGFibGlzaGluZyBhIGNvbm5lY3Rpb24gdG8gaXQuCgoyLiAgKipDcmVhdGluZyBUYWJsZXMqKjogV2UgbGVhcm5lZCBob3cgdG8gY3JlYXRlIHRhYmxlcyBpbiBTUUxpdGUsIGRlZmluaW5nIHByaW1hcnkga2V5cyBhbmQgc3RydWN0dXJpbmcgZGF0YSB3aXRoIHZhcmlvdXMgZGF0YSB0eXBlcy4KCjMuICAqKkluc2VydGluZyBEYXRhKio6IFRoZSB0dXRvcmlhbCBjb3ZlcmVkIHRoZSBpbnNlcnRpb24gb2YgZGF0YSBpbnRvIHRhYmxlcywgaW5jbHVkaW5nIGhhbmRsaW5nIGV4Y2VwdGlvbnMgYW5kIHBlcmZvcm1pbmcgYnVsayBpbnNlcnRpb25zIGZvciBlZmZpY2llbmN5LgoKNC4gICoqUmV0cmlldmluZyBEYXRhKio6IFdlIGRlbW9uc3RyYXRlZCBob3cgdG8gcmV0cmlldmUgZGF0YSB1c2luZyB0aGUgYFNFTEVDVGAgc3RhdGVtZW50LCBhcHBseWluZyBmaWx0ZXJzIHdpdGggdGhlIGBXSEVSRWAgY2xhdXNlLCBhbmQgc29ydGluZyBhbmQgbGltaXRpbmcgcmVzdWx0cy4KCjUuICAqKlByb2Nlc3NpbmcgUXVlcnkgUmVzdWx0cyoqOiBUaGUgaGFuZGxpbmcgYW5kIHByb2Nlc3Npbmcgb2YgcXVlcnkgcmVzdWx0cyByZXR1cm5lZCBhcyB0dXBsZXMsIGFuZCB0aGUgdXNlIG9mIG5hbWVkIHR1cGxlcyBhbmQgZGljdGlvbmFyaWVzIGZvciBiZXR0ZXIgZGF0YSBtYW5hZ2VtZW50LCB3ZXJlIGV4cGxvcmVkLgoKNi4gICoqVXBkYXRpbmcgYW5kIERlbGV0aW5nIERhdGEqKjogV2UgZGlzY3Vzc2VkIHVwZGF0aW5nIGV4aXN0aW5nIHJlY29yZHMgYW5kIGRlbGV0aW5nIGRhdGEgZnJvbSB0YWJsZXMsIGVtcGhhc2l6aW5nIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSBgV0hFUkVgIGNsYXVzZSB0byB0YXJnZXQgc3BlY2lmaWMgcmVjb3Jkcy4KCjcuICAqKlRyYW5zYWN0aW9ucyBhbmQgQ29ubmVjdGlvbiBNYW5hZ2VtZW50Kio6IFRoZSB0dXRvcmlhbCBoaWdobGlnaHRlZCB0aGUgc2lnbmlmaWNhbmNlIG9mIGNvbW1pdHRpbmcgdHJhbnNhY3Rpb25zLCByb2xsaW5nIGJhY2sgaW4gY2FzZSBvZiBlcnJvcnMsIGFuZCB0aGUgaW1wb3J0YW5jZSBvZiBwcm9wZXJseSBjbG9zaW5nIHRoZSBkYXRhYmFzZSBjb25uZWN0aW9uIHRvIGVuc3VyZSBkYXRhIGludGVncml0eSBhbmQgcmVzb3VyY2UgbWFuYWdlbWVudC4KClRvIHNvbGlkaWZ5IHlvdXIgdW5kZXJzdGFuZGluZywgaXQncyBiZW5lZmljaWFsIHRvIGVuZ2FnZSBpbiBoYW5kcy1vbiBwcmFjdGljZToKCi0gICBFeHBlcmltZW50IHdpdGggZGlmZmVyZW50IGRhdGEgdHlwZXMgYW5kIHRhYmxlIHN0cnVjdHVyZXMuCi0gICBUcnkgbW9yZSBjb21wbGV4IFNRTCBxdWVyaWVzLCBqb2lucywgYW5kIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMuCi0gICBJbXBsZW1lbnQgZXJyb3IgaGFuZGxpbmcgYW5kIHRyYW5zYWN0aW9uIG1hbmFnZW1lbnQgaW4gbW9yZSBjb21wbGV4IHNjZW5hcmlvcy4KClNRTGl0ZSwgd2l0aCBpdHMgc2ltcGxpY2l0eSBhbmQgbGlnaHR3ZWlnaHQgbmF0dXJlLCBjb21iaW5lZCB3aXRoIFB5dGhvbidzIHZlcnNhdGlsaXR5LCBvZmZlcnMgYSBwb3dlcmZ1bCBzZXQgb2YgdG9vbHMgZm9yIG1hbmFnaW5nIGFuZCBtYW5pcHVsYXRpbmcgZGF0YWJhc2UtZHJpdmVuIGFwcGxpY2F0aW9ucy4gVGhlIHNraWxscyB5b3UndmUgYWNxdWlyZWQgaGVyZSBzaG91bGQgc2VydmUgYXMgYSByb2J1c3QgZm91bmRhdGlvbiBmb3IgeW91ciBqb3VybmV5IGluIGRhdGFiYXNlIHByb2dyYW1taW5nIHdpdGggUHl0aG9uIGFuZCBTUUxpdGUuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEZpbGVzICYgUmVzb3VyY2VzCgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBTZWUgQWxzbwoKLSAgIFs3MC44MDEgLSBUaGUgU1FMaXRlIERhdGFiYXNlXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzcwLnNxbC9sLTcwLTgwMS1pbnRyby1zcWxpdGUvbC03MC04MDEuaHRtbCkKCiMjIFJlZmVyZW5jZXMKCk5vbmUgeWV0LgoKIyMgRXJyYXRhCgpbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==