Introduction
In this lesson we will demonstrate how to execute XPath queries against an XML document (or XML store).
Role of XPath
XPath is a powerful language for navigating and querying XML documents. Its role and significance lie in its ability to efficiently and precisely locate elements and data within XML documents. Here’s an overview of the role and significance of XPath in the context of querying XML documents:
Navigating the XML Tree Structure: XML documents are hierarchical in nature, organized as a tree-like structure with elements, attributes, and text nodes. XPath provides a standardized way to traverse this tree structure, allowing you to pinpoint specific nodes and relationships within the document.
Selecting Elements and Attributes: XPath allows you to select XML elements and attributes based on their names, positions, or characteristics. For example, you can easily select all elements of a particular name or filter elements based on their attributes and values.
Path Expressions: XPath uses path expressions that resemble file system paths to specify the location of elements in an XML document. These expressions make it intuitive to traverse the hierarchy and access the desired data.
Filtering and Predicates: XPath supports filtering using predicates, which enable you to refine your selections further. Predicates allow you to specify conditions that must be met for an element to be included in the result set. For example, you can select all <book>
elements where the <author>
is “John Doe.”
Relative and Absolute Paths: XPath supports both relative and absolute paths. Relative paths are specified in relation to the current context node, making it easy to navigate within specific sections of the document. Absolute paths start from the root of the document.
Accessing Text Content: XPath can retrieve the text content of elements and text nodes, allowing you to extract the data contained within XML tags. This is particularly useful when you want to extract values for further processing.
Support for Functions: XPath includes a wide range of built-in functions for performing operations on data, such as string manipulation, mathematical calculations, and date/time handling. These functions enhance the querying capabilities of XPath.
Integration with Other Technologies: XPath is not limited to standalone querying. It plays a crucial role in various XML-related technologies like XSLT (Extensible Stylesheet Language Transformations), XQuery (for querying XML documents), and XPointer (for addressing specific parts of XML documents). XPath expressions can be embedded within these technologies to achieve specific tasks.
Cross-Platform Compatibility: XPath is platform-independent and widely supported in various programming languages and tools. This ensures that XPath queries can be used in different environments and applications.
Standardization: XPath is a W3C (World Wide Web Consortium) standard, which means it has a well-defined syntax and behavior. This standardization promotes consistency and interoperability across different XML processing tools and libraries.
In short, XPath is a fundamental component of XML processing that facilitates the precise and flexible extraction of data from XML documents. Its standardized syntax and rich set of features make it a valuable tool for developers and data analysts working with XML data, enabling them to efficiently query and manipulate XML documents for various purposes, including data extraction, transformation, and validation.
XPath vs SQL
XPath and SQL are both query languages, but they are designed for querying and manipulating data in different contexts. Here are some key differences between XPath and SQL:
- Data Model:
- XPath: XPath is primarily used for querying and navigating XML and HTML documents. It operates on a tree-like structure, where elements, attributes, and text nodes are organized hierarchically.
- SQL: SQL (Structured Query Language) is used for querying relational databases. It works with tables consisting of rows and columns, and it represents data in a tabular format.
- Data Source:
- XPath: XPath is used to query semi-structured and hierarchical data, mainly XML documents. It’s suitable for navigating the complex structure of XML files.
- SQL: SQL is used for querying structured data stored in relational databases. It excels at handling large datasets with well-defined schemas.
- Query Syntax:
- XPath: XPath uses path expressions and functions to navigate and query XML documents. Path expressions resemble directory paths and are used to specify the location of elements or attributes within the document.
- SQL: SQL employs declarative statements that specify what data to retrieve, update, or manipulate. SQL queries consist of SELECT, INSERT, UPDATE, DELETE, and other commands.
- Data Manipulation:
- XPath: While XPath primarily focuses on querying and selecting data within XML documents, it lacks the ability to perform data modification operations like insertion, deletion, or updating.
- SQL: SQL is a comprehensive language that supports not only querying but also data modification operations such as INSERT, UPDATE, and DELETE, making it suitable for maintaining relational databases.
- Use Cases:
- XPath: It is commonly used in web scraping, XML document processing, and XML-based technologies like XSLT and XQuery for transforming and extracting data from XML sources.
- SQL: SQL is widely used for database management, data retrieval, reporting, and data manipulation in applications ranging from e-commerce platforms to financial systems.
- Data Complexity:
- XPath: XPath excels in handling complex hierarchies and relationships within XML documents, making it ideal for tasks like extracting data from deeply nested XML structures.
- SQL: SQL is designed for managing structured data with well-defined relationships, which is suitable for handling complex data dependencies in relational databases.
- Standardization:
- XPath: XPath is a W3C (World Wide Web Consortium) standard, ensuring consistency and compatibility among different XML processing tools.
- SQL: SQL is an ANSI/ISO standard with different dialects for various database systems. While there is a common core of SQL, each database may have its own extensions and variations.
To recap, XPath and SQL are specialized query languages tailored for different data models and use cases. XPath is focused on navigating and querying hierarchical, semi-structured data in XML documents, while SQL is designed for managing structured data in relational databases with support for data manipulation operations. The choice between XPath and SQL depends on the nature of the data and the specific requirements of the task at hand.
XPath Expression Evaluation
Evaluating an XPath expression against an XML document involves using a parser or library that supports XPath to search, navigate, and retrieve data from the XML document based on the specified XPath query.
There are two common ways to execute XPath queries against an XML document:
- write a program that loads the XML and evealuates the XPath query expression
- load the XML document into a tool to execute ad hoc queries
Ad-Hoc XPath Expression Evaluation
There are several online tools and websites that allow you to evaluate ad hoc XPath queries against XML documents without the need for setting up local development environments or writing code. These tools are handy for quick XPath testing and experimentation. Here are some common websites for evaluating ad hoc XPath queries:
FreeFormatter XPath Tester: FreeFormatter XPath Tester is a straightforward online tool for testing XPath expressions against XML data. It provides a clear and simple interface for entering XML data and XPath queries and viewing the results.
XPath Visualizer and Tester: XPath Visualizer and Tester offers a user-friendly interface for evaluating XPath expressions. It allows you to upload XML files, enter XPath queries, and see the results in a structured format.
CodeBeautify XPath Tester: CodeBeautify XPath Tester provides an online XPath evaluator with XML data input and an output pane to display the results of your XPath queries.
Online XPath Tester by DevGuru: Online XPath Tester by DevGuru is a simple online tool that lets you enter XML data and XPath expressions, and it displays the matching nodes.
XPath Evaluator by W3Schools: XPath Evaluator by W3Schools is part of the W3Schools website, known for its web development tutorials. It allows you to enter XML data and XPath expressions to see the results.
XPathFiddle: XPathFiddle is an online XPath tester with a clean and minimalistic interface. You can input your XML data and XPath queries and instantly view the results.
Online XML Tools: Online XML Tools offers a suite of XML-related tools, including an XPath tester. You can input your XML data and XPath expressions and visualize the results.
Online XPath Tester by TutorialsPoint: Online XPath Tester by TutorialsPoint is a handy tool for testing XPath expressions against XML data. It provides a simple editor and result display.
Oxygen XML Web Author: Oxygen XML Web Author is a powerful online XML editor that includes an XPath evaluator. It’s more comprehensive and feature-rich than some of the other tools listed here.
These websites provide convenient and accessible environments for trying out XPath queries against sample XML data. Depending on your specific needs and preferences, you can choose the one that best suits your workflow and requirements.
XPath within Programs
Here’s a programming language indepedent step-by-step guide on how to evaluate an XPath expression against an XML document:
- Load the XML Document:
- First, you need to load the XML document into memory. This can be done using an XML parsing library or tool available in your programming language of choice. Common choices include libraries like lxml in Python, XmlDocument in C#, or built-in functions like
DOMDocument
in PHP.
- Initialize the XPath Processor:
- Next, you need to initialize the XPath processor or create an XPath object. This object allows you to compile and evaluate XPath expressions against the loaded XML document. The specific method for initializing the processor may vary depending on the programming language and libraries used.
- Compile the XPath Expression:
- Compile the XPath expression you want to evaluate. XPath expressions can vary in complexity and specificity, and they are used to define what data you want to retrieve from the XML document.
- Evaluate the XPath Expression:
- Use the XPath processor to evaluate the compiled XPath expression against the loaded XML document. The evaluation process will return a result, which can be one or more of the following:
- A single node (e.g., element, attribute, text node)
- A node list (multiple nodes matching the expression)
- A boolean value (true or false, depending on whether the expression matches)
- A string value (e.g., the text content of a selected element)
- Handle the Result:
- Depending on the result of the XPath evaluation, you can perform various actions:
- If the result is a single node, you can access its data or attributes.
- If the result is a node list, you can iterate through the list to process each matching node.
- If the result is a boolean value, you can use it to make conditional decisions.
- If the result is a string value, you can access the selected text content.
- Repeat as Needed:
- You can evaluate multiple XPath expressions against the same XML document to retrieve different sets of data or information.
- Error Handling:
- Implement error handling to handle cases where the XPath expression is invalid or does not match any data in the XML document. Most XPath processors provide mechanisms to catch and handle exceptions or errors.
- Release Resources:
- After you’ve finished evaluating XPath expressions and processing the XML document, it’s essential to release any resources or memory associated with the XPath processor and XML document to prevent memory leaks.
Here’s a simplified example in Python (so you can see how it compares to R which is used in the remainder of the tutorial) using the lxml library to illustrate how you might evaluate an XPath expression against an XML document:
from lxml import etree
# Load the XML document
xml_data = """
<root>
<item id="1">Apple</item>
<item id="2">Banana</item>
</root>
"""
root = etree.fromstring(xml_data)
# Initialize the XPath processor
xpath_processor = etree.XPath("//item[@id='1']")
# Evaluate the XPath expression
result = xpath_processor(root)
# Handle the result
if result:
print(result[0].text) # Output: Apple
else:
print("No matching node found.")
This code loads an XML document, initializes the XPath processor, evaluates the XPath expression, and handles the result.
XPath Evaluation in R
XPath can be used in any programming language that provide XML support, including Python, Java, JavaScript, C#, among many others. In this lesson, we will focus on using XPath within R, although the XPath expressions are language independent.
To use any of the XML parsing or any of the XPath function you will need an XML package. The XML package is one of several packages and the one we are using in this lesson, so be sure to install it first.
Note that the XML package only supports XPath Version 1.0 and not the newer 2.0 and 3.1 versions.
Loading an XML Document
Let’s start by loading an XML document. There are several functions for loading them which generally all work the same way, although some create different in-memory structures representing the XML tree and thus some are more and some are less efficient. XML documents (or files) can be loaded from the local file system or from a URL.
Load XML from File
library(XML)
xmlFile <- "CDCatalog2.xml"
xmlObj <- xmlParse(xmlFile)
xmlObjTree <- xmlTreeParse(xmlFile)
The error Error: XML content does not seem to be XML: ’’ is often caused by a file that cannot be found and is often due to a misspelled file or path name.
Load XML via URL
xmlURL <-"http://d396qusza40orc.cloudfront.net/getdata%2Fdata%2Frestaurants.xml"
xmlObjTree <- xmlTreeParse(xmlURL)
Note that the R parsing functions do not support https so be sure that any URL starts with http:// rather than https://. If you get the error “XML content does not seem to be XML” then that is often the cause.
xmlParse vs xmlTreeParse
xmlParse
is a version of xmlTreeParse
where argument useInternalNodes is set to TRUE. If you want to get an R object use xmlTreeParse
. While this is generally not very efficient for large document and often unnecessary if you want to extract only parts of the XML document, it has the benefit that you can traverse the XML tree using named traversal, e.g., root\(child1\)child$…
Using xmlParse
is generally more efficient as it returns a pointer to a C structure. To access this structure requires XPath, although xmlTreeParse
supports XPath as well.
You can see the different class types in the code below.
## [1] "XMLInternalDocument" "XMLAbstractDocument"
## [1] "XMLDocument" "XMLAbstractDocument"
Applying an XPath Expression
There are several ways to apply an XPath expression to a parsed XML object, the most common of which to use the function xmlPathSApply
. The code chunks below presumes that fp
is the path or URL to an XML document.
The xmlPathSApply
function applies the function passed as a parameter to all matching elements of an XPath expression rather than returning the elements. In the code chunk below, each matching element has the xmlValue
function applied to it and thus the value of the matching elements are extracted. Recall that the value of an element is everything that is between the opening and closing tags. For example, the value of {xml} <tag>some value</tag>
is someValue. Note that the returned object is a vector of characters (like an array of strings in other programming languages) and thus can be accessed as such.
xmlObj <- xmlParse(xmlFile)
xpathEx <- "//cd/title"
artists <- xpathSApply(xmlObj, xpathEx, xmlValue)
head(artists, 3)
[1] "Empire Burlesque" "Hide your heart" "Greatest Hits"
[1] "character"
# access the second element
print(paste("The second artist is: ",artists[2]))
[1] "The second artist is: Hide your heart"
Retrieving XML Attributes
There are two ways to retrieve an element’s attributes. One, use an XPath expression with xpathSApply
(but without applying the xmlValue
function). Two, use the xmlAttrs
function from a specific node – which requires traversing the tree.
The use of an XPath expression is generally preferable and more maintainable.
xmlObj <- xmlParse(xmlFile)
# Approach 1: use an XPath expression to get the attribute country
xpathEx <- "//cd/company/@country"
countries <- xpathSApply(xmlObj, xpathEx)
head(countries, 3)
country country country
"USA" "UK" "USA"
Using Values in R
All of the values retrieved from XML are text and must be converted to strings, often after parsing the text.
xpathEx <- "//cd/price"
prices <- xpathSApply(xmlObj, xpathEx, xmlValue)
# the values in the vector "prices" are character strings
# mean(prices) results in an error
prices.n <- as.numeric(prices)
avg <- mean(prices.n)
print(paste0("The average price is $", round(avg,2)))
[1] "The average price is $9.12"
Review
So, to summarize, the way that XML documents are processed in R is as follows:
- install the package XML
- load the package XML
- set up an XPath expression
- call
xpathSApply()
- use the result node or call
xmlValue()
to get the value of the node
And, remember that all values are characters (text or strings) in R and must be coerced to the correct data type – perhaps after extracting parts of the returned character value. See Lesson 6.112 Basics of Text & String Processing in R.
Significance of XPath
Within the framework of this tutorial, you may have observed a recurrent emphasis on the term “select” when elucidating the functioning of XPath expressions in effectively pinpointing sections of an XML document. Nonetheless, it is crucial to underscore that XPath does not operate in isolation; it is inherently intertwined with other complementary technologies such as XSLT, XPointer, or XLink. The XPath illustrations presented in the preceding sections of this tutorial necessitate their integration with additional code to manifest their full utility.
For instance, consider the following code snippet, which demonstrates the utilization of an XPath expression in an XSLT stylesheet:
<xsl:value-of select="*/session[@type='running']" />
In this code excerpt, the XPath expression is embedded within the select
attribute of the xsl:value-of
element. This element assumes the responsibility of extracting content from a source XML document and incorporating it into an output document during the transformation process of the source document. For comprehensive insights into XSLT stylesheets and their operational dynamics, I recommend revisiting Tutorials 12 and 13.
The pivotal takeaway here is that the XPath expression derives its significance and functionality from its integration within the XSLT context. XPath plays an integral role in the realm of XSLT, as you may recall from the material covered in Tutorial 13.
In a manner analogous to its function within XSLT, XPath serves as the foundational addressing mechanism in XPointer. XPointer’s primary objective is to pinpoint specific sections within XML documents, and it assumes a central role in the broader context of XLink, a concept we will delve into shortly. XPointer harnesses XPath’s capabilities to facilitate navigation through the hierarchy of nodes comprising an XML document. This may sound reminiscent of XPath’s role thus far. However, XPointer elevates the capabilities of XPath by introducing a syntax for fragment identifiers, which are subsequently employed to precisely specify segments within documents. Through this innovation, XPointer empowers users with an exceptional degree of control over the addressing and manipulation of XML documents.
More Examples
The examples below retrieve data from the XML document TeamRosters.xml. Take a moment to open that file and inspect it. You can download the files in the section Files & resource or from the link.
xmlFile <- "TeamRosters.xml"
xmlObj <- xmlParse(xmlFile)
Find the goals scored by “McAvoy”
This path uses a full absolute path expression starting at the root (/) and specifying a filter through a predicate expression.
xpathEx <- "/rosters/team/player[lastname='McAvoy']/points/goals"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
5
Alternatively, rather than passing the function xmlValue()
as a parameter to xpathSApply()
, it can also be called separately on the returned full node.
xpathEx <- "/rosters/team/player[lastname='McAvoy']/points/goals"
result <- xpathSApply(xmlObj, xpathEx)
print(result)
[[1]]
<goals>5</goals>
value <- xmlValue(result)
cat(value)
5
Find the goals scored by all players
xpathEx <- "/rosters/team/player/points/goals"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
29 23 5 0 1
How many goals were scored in total by all players?
The first solution uses R to calculate the sum.
xpathEx <- "/rosters/team/player/points/goals"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
result <- as.numeric(result)
cat(sum(result))
58
The second solution uses the sum
aggregation function of XPath.
xpathEx <- "sum(/rosters/team/player/points/goals)"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
58
What are the last names of everyone?
This path expression is not a global path. It starts with // so it matches any lastname element regardless of where it is in the tree.
xpathEx <- "//lastname"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
Cassidy Marchand Bergeron McAvoy Rask D'Or
What are the last names of all players?
Unlike the previous example, this path is also not a global path but it is more restrictive as it only matches lastname elements that are direct child nodes of a player node.
xpathEx <- "//player/lastname"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
Marchand Bergeron McAvoy Rask D'Or
What are the first and last names of all players?
The XPath below is a correct XPath 2.0 expression, although it does not evaluate in R because the XML package only supports XPath 1.0.
//player/concat(firstname,',',lastname)
An R solution would be to retrieve the elements individually and then concatenate them in R.
xpathEx <- "//player/firstname"
r.fn <- xpathSApply(xmlObj, xpathEx, xmlValue)
xpathEx <- "//player/lastname"
r.ln <- xpathSApply(xmlObj, xpathEx, xmlValue)
result <- paste0(r.ln, ', ', r.fn)
What are the names of the players who did not score a goal?
For this query two paths are presented: the first path is an absolute path while the second path is not. For this XML document they produce the same result.
xpathEx <- "/rosters/team/player[points/goals = 0]/lastname"
# same result as
xpathEx <- "//player[points/goals = 0]/lastname"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
Rask
How many goals did player 63 score?
Note the predicate expression specifying the value of the num attribute of player. The XML for a player node looks like this:
<player num="63">
...
</player>
<player num="37">
...
</player>
xpathEx <- "/rosters/team/player[@num = '63']/points/goals"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
29
How many points did player 63 score?
Note the use of the OR operator | to find elements that match either path expression, which means that we receive both. Alternatively, we could have executed each path expression individually and concatenated the result in R.
xpathEx <- "sum(/rosters/team/player[@num='63']/points/goals | /rosters/team/player[@num='63']/points/assists)"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
69
How many players scored more than 20 goals?
Once again, a predicate expression but this time the result is provided as an argument to the count function of XPath, which, like its counterpart in SQL, will return the number of nodes in the result.
xpathEx <- "count(/rosters/team/player[points/goals > 20]/points/goals)"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
2
What is the lowest number of goals any player scored?
xpathEx <- "min(/rosters/team/player/points/goals)"
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
0
How many goals did Laurent D’Or score?
This query is a bit “tricky” as the last name includes an apostrophe which we often use in XPath as a string delimiter. Of course, we could use the player’s number but we want to use the name as the number may not be fixed for some types of players.
One approach is the reverse the way we use string delimiters. Notice how the XPath expression below uses single quotes (‘) for the XPath expression in R and the double quote (“) for the value in the XPath condition. Finally, notice how the’ is escaped by adding a backslash before it. By writing \’, R and the XPath expression do not interpret the character but pass it on uninterpreted.
xpathEx <- '/rosters/team/player[lastname = "D\'Or"]/points/goals'
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
1
An alternative would be to build the string from the parts and use ” and ’ quotes judiciously and then embed the ’ within a string surrounded by ” and the ” quotes within a string surrounded by ’.
xpathEx <- paste0('/rosters/team/player[lastname = "D',
"'Or",
'"]/points/goals')
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
1
We could have also placed the value into a variable and then pasted that into the XPath expression; this may be a bit simpler looking but has the same effect.
lastname <- "D'Or"
xpathEx <- paste0('/rosters/team/player[lastname = "',
lastname,
'"]/points/goals')
result <- xpathSApply(xmlObj, xpathEx, xmlValue)
cat(result)
1
Practice Queries
- What are the last names of all goalies?
- What is the total number of points scored by the entire team?
- What is the average number of goals for all players who are not goalies?
- Which player has the highest salary?
- Which players have an above average salary?
Tutorial
The video tutorial below uses different XML files to revisit the concepts and demonstrating them by walking through R code. Follow along and type in the statements yourself. A link to the XML file used in the tutorial is below:
Before you start, remember to install the package XML if you haven’t already done so.
LS0tCnRpdGxlOiAiRGF0YSBSZXRyaWV2YWwgZnJvbSBYTUwgdmlhIFhQYXRoIGluIFIiCnBhcmFtczoKICBjYXRlZ29yeTogNgogIG51bWJlcjogMzAzCiAgdGltZTogNDUKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAicix4cGF0aCx4bWwiCiAgZGVzY3JpcHRpb246ICJFeHBsYWlucyBob3cgdG8gcmV0cmlldmUgZGF0YSBmcm9tIGFuIFhNTCBkb2N1bWVudCBvcgogICAgICAgICAgICAgICAgcmVwb3NpdG9yeSB1c2luZyBYUGF0aCBleHByZXNzaW9ucy4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKSW4gdGhpcyBsZXNzb24gd2Ugd2lsbCBkZW1vbnN0cmF0ZSBob3cgdG8gZXhlY3V0ZSBYUGF0aCBxdWVyaWVzIGFnYWluc3QgYW4gWE1MIGRvY3VtZW50IChvciBYTUwgc3RvcmUpLgoKIyMgUm9sZSBvZiBYUGF0aAoKWFBhdGggaXMgYSBwb3dlcmZ1bCBsYW5ndWFnZSBmb3IgbmF2aWdhdGluZyBhbmQgcXVlcnlpbmcgWE1MIGRvY3VtZW50cy4gSXRzIHJvbGUgYW5kIHNpZ25pZmljYW5jZSBsaWUgaW4gaXRzIGFiaWxpdHkgdG8gZWZmaWNpZW50bHkgYW5kIHByZWNpc2VseSBsb2NhdGUgZWxlbWVudHMgYW5kIGRhdGEgd2l0aGluIFhNTCBkb2N1bWVudHMuIEhlcmUncyBhbiBvdmVydmlldyBvZiB0aGUgcm9sZSBhbmQgc2lnbmlmaWNhbmNlIG9mIFhQYXRoIGluIHRoZSBjb250ZXh0IG9mIHF1ZXJ5aW5nIFhNTCBkb2N1bWVudHM6CgoxLiAgKipOYXZpZ2F0aW5nIHRoZSBYTUwgVHJlZSBTdHJ1Y3R1cmUqKjogWE1MIGRvY3VtZW50cyBhcmUgaGllcmFyY2hpY2FsIGluIG5hdHVyZSwgb3JnYW5pemVkIGFzIGEgdHJlZS1saWtlIHN0cnVjdHVyZSB3aXRoIGVsZW1lbnRzLCBhdHRyaWJ1dGVzLCBhbmQgdGV4dCBub2Rlcy4gWFBhdGggcHJvdmlkZXMgYSBzdGFuZGFyZGl6ZWQgd2F5IHRvIHRyYXZlcnNlIHRoaXMgdHJlZSBzdHJ1Y3R1cmUsIGFsbG93aW5nIHlvdSB0byBwaW5wb2ludCBzcGVjaWZpYyBub2RlcyBhbmQgcmVsYXRpb25zaGlwcyB3aXRoaW4gdGhlIGRvY3VtZW50LgoKMi4gICoqU2VsZWN0aW5nIEVsZW1lbnRzIGFuZCBBdHRyaWJ1dGVzKio6IFhQYXRoIGFsbG93cyB5b3UgdG8gc2VsZWN0IFhNTCBlbGVtZW50cyBhbmQgYXR0cmlidXRlcyBiYXNlZCBvbiB0aGVpciBuYW1lcywgcG9zaXRpb25zLCBvciBjaGFyYWN0ZXJpc3RpY3MuIEZvciBleGFtcGxlLCB5b3UgY2FuIGVhc2lseSBzZWxlY3QgYWxsIGVsZW1lbnRzIG9mIGEgcGFydGljdWxhciBuYW1lIG9yIGZpbHRlciBlbGVtZW50cyBiYXNlZCBvbiB0aGVpciBhdHRyaWJ1dGVzIGFuZCB2YWx1ZXMuCgozLiAgKipQYXRoIEV4cHJlc3Npb25zKio6IFhQYXRoIHVzZXMgcGF0aCBleHByZXNzaW9ucyB0aGF0IHJlc2VtYmxlIGZpbGUgc3lzdGVtIHBhdGhzIHRvIHNwZWNpZnkgdGhlIGxvY2F0aW9uIG9mIGVsZW1lbnRzIGluIGFuIFhNTCBkb2N1bWVudC4gVGhlc2UgZXhwcmVzc2lvbnMgbWFrZSBpdCBpbnR1aXRpdmUgdG8gdHJhdmVyc2UgdGhlIGhpZXJhcmNoeSBhbmQgYWNjZXNzIHRoZSBkZXNpcmVkIGRhdGEuCgo0LiAgKipGaWx0ZXJpbmcgYW5kIFByZWRpY2F0ZXMqKjogWFBhdGggc3VwcG9ydHMgZmlsdGVyaW5nIHVzaW5nIHByZWRpY2F0ZXMsIHdoaWNoIGVuYWJsZSB5b3UgdG8gcmVmaW5lIHlvdXIgc2VsZWN0aW9ucyBmdXJ0aGVyLiBQcmVkaWNhdGVzIGFsbG93IHlvdSB0byBzcGVjaWZ5IGNvbmRpdGlvbnMgdGhhdCBtdXN0IGJlIG1ldCBmb3IgYW4gZWxlbWVudCB0byBiZSBpbmNsdWRlZCBpbiB0aGUgcmVzdWx0IHNldC4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gc2VsZWN0IGFsbCBgPGJvb2s+YCBlbGVtZW50cyB3aGVyZSB0aGUgYDxhdXRob3I+YCBpcyAiSm9obiBEb2UuIgoKNS4gICoqUmVsYXRpdmUgYW5kIEFic29sdXRlIFBhdGhzKio6IFhQYXRoIHN1cHBvcnRzIGJvdGggcmVsYXRpdmUgYW5kIGFic29sdXRlIHBhdGhzLiBSZWxhdGl2ZSBwYXRocyBhcmUgc3BlY2lmaWVkIGluIHJlbGF0aW9uIHRvIHRoZSBjdXJyZW50IGNvbnRleHQgbm9kZSwgbWFraW5nIGl0IGVhc3kgdG8gbmF2aWdhdGUgd2l0aGluIHNwZWNpZmljIHNlY3Rpb25zIG9mIHRoZSBkb2N1bWVudC4gQWJzb2x1dGUgcGF0aHMgc3RhcnQgZnJvbSB0aGUgcm9vdCBvZiB0aGUgZG9jdW1lbnQuCgo2LiAgKipBY2Nlc3NpbmcgVGV4dCBDb250ZW50Kio6IFhQYXRoIGNhbiByZXRyaWV2ZSB0aGUgdGV4dCBjb250ZW50IG9mIGVsZW1lbnRzIGFuZCB0ZXh0IG5vZGVzLCBhbGxvd2luZyB5b3UgdG8gZXh0cmFjdCB0aGUgZGF0YSBjb250YWluZWQgd2l0aGluIFhNTCB0YWdzLiBUaGlzIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgd2hlbiB5b3Ugd2FudCB0byBleHRyYWN0IHZhbHVlcyBmb3IgZnVydGhlciBwcm9jZXNzaW5nLgoKNy4gICoqU3VwcG9ydCBmb3IgRnVuY3Rpb25zKio6IFhQYXRoIGluY2x1ZGVzIGEgd2lkZSByYW5nZSBvZiBidWlsdC1pbiBmdW5jdGlvbnMgZm9yIHBlcmZvcm1pbmcgb3BlcmF0aW9ucyBvbiBkYXRhLCBzdWNoIGFzIHN0cmluZyBtYW5pcHVsYXRpb24sIG1hdGhlbWF0aWNhbCBjYWxjdWxhdGlvbnMsIGFuZCBkYXRlL3RpbWUgaGFuZGxpbmcuIFRoZXNlIGZ1bmN0aW9ucyBlbmhhbmNlIHRoZSBxdWVyeWluZyBjYXBhYmlsaXRpZXMgb2YgWFBhdGguCgo4LiAgKipJbnRlZ3JhdGlvbiB3aXRoIE90aGVyIFRlY2hub2xvZ2llcyoqOiBYUGF0aCBpcyBub3QgbGltaXRlZCB0byBzdGFuZGFsb25lIHF1ZXJ5aW5nLiBJdCBwbGF5cyBhIGNydWNpYWwgcm9sZSBpbiB2YXJpb3VzIFhNTC1yZWxhdGVkIHRlY2hub2xvZ2llcyBsaWtlIFhTTFQgKEV4dGVuc2libGUgU3R5bGVzaGVldCBMYW5ndWFnZSBUcmFuc2Zvcm1hdGlvbnMpLCBYUXVlcnkgKGZvciBxdWVyeWluZyBYTUwgZG9jdW1lbnRzKSwgYW5kIFhQb2ludGVyIChmb3IgYWRkcmVzc2luZyBzcGVjaWZpYyBwYXJ0cyBvZiBYTUwgZG9jdW1lbnRzKS4gWFBhdGggZXhwcmVzc2lvbnMgY2FuIGJlIGVtYmVkZGVkIHdpdGhpbiB0aGVzZSB0ZWNobm9sb2dpZXMgdG8gYWNoaWV2ZSBzcGVjaWZpYyB0YXNrcy4KCjkuICAqKkNyb3NzLVBsYXRmb3JtIENvbXBhdGliaWxpdHkqKjogWFBhdGggaXMgcGxhdGZvcm0taW5kZXBlbmRlbnQgYW5kIHdpZGVseSBzdXBwb3J0ZWQgaW4gdmFyaW91cyBwcm9ncmFtbWluZyBsYW5ndWFnZXMgYW5kIHRvb2xzLiBUaGlzIGVuc3VyZXMgdGhhdCBYUGF0aCBxdWVyaWVzIGNhbiBiZSB1c2VkIGluIGRpZmZlcmVudCBlbnZpcm9ubWVudHMgYW5kIGFwcGxpY2F0aW9ucy4KCjEwLiAqKlN0YW5kYXJkaXphdGlvbioqOiBYUGF0aCBpcyBhIFczQyAoV29ybGQgV2lkZSBXZWIgQ29uc29ydGl1bSkgc3RhbmRhcmQsIHdoaWNoIG1lYW5zIGl0IGhhcyBhIHdlbGwtZGVmaW5lZCBzeW50YXggYW5kIGJlaGF2aW9yLiBUaGlzIHN0YW5kYXJkaXphdGlvbiBwcm9tb3RlcyBjb25zaXN0ZW5jeSBhbmQgaW50ZXJvcGVyYWJpbGl0eSBhY3Jvc3MgZGlmZmVyZW50IFhNTCBwcm9jZXNzaW5nIHRvb2xzIGFuZCBsaWJyYXJpZXMuCgpJbiBzaG9ydCwgWFBhdGggaXMgYSBmdW5kYW1lbnRhbCBjb21wb25lbnQgb2YgWE1MIHByb2Nlc3NpbmcgdGhhdCBmYWNpbGl0YXRlcyB0aGUgcHJlY2lzZSBhbmQgZmxleGlibGUgZXh0cmFjdGlvbiBvZiBkYXRhIGZyb20gWE1MIGRvY3VtZW50cy4gSXRzIHN0YW5kYXJkaXplZCBzeW50YXggYW5kIHJpY2ggc2V0IG9mIGZlYXR1cmVzIG1ha2UgaXQgYSB2YWx1YWJsZSB0b29sIGZvciBkZXZlbG9wZXJzIGFuZCBkYXRhIGFuYWx5c3RzIHdvcmtpbmcgd2l0aCBYTUwgZGF0YSwgZW5hYmxpbmcgdGhlbSB0byBlZmZpY2llbnRseSBxdWVyeSBhbmQgbWFuaXB1bGF0ZSBYTUwgZG9jdW1lbnRzIGZvciB2YXJpb3VzIHB1cnBvc2VzLCBpbmNsdWRpbmcgZGF0YSBleHRyYWN0aW9uLCB0cmFuc2Zvcm1hdGlvbiwgYW5kIHZhbGlkYXRpb24uCgojIyBYUGF0aCB2cyBTUUwKClhQYXRoIGFuZCBTUUwgYXJlIGJvdGggcXVlcnkgbGFuZ3VhZ2VzLCBidXQgdGhleSBhcmUgZGVzaWduZWQgZm9yIHF1ZXJ5aW5nIGFuZCBtYW5pcHVsYXRpbmcgZGF0YSBpbiBkaWZmZXJlbnQgY29udGV4dHMuIEhlcmUgYXJlIHNvbWUga2V5IGRpZmZlcmVuY2VzIGJldHdlZW4gWFBhdGggYW5kIFNRTDoKCjEuICAqKkRhdGEgTW9kZWwqKjoKICAgIC0gICAqKlhQYXRoKio6IFhQYXRoIGlzIHByaW1hcmlseSB1c2VkIGZvciBxdWVyeWluZyBhbmQgbmF2aWdhdGluZyBYTUwgYW5kIEhUTUwgZG9jdW1lbnRzLiBJdCBvcGVyYXRlcyBvbiBhIHRyZWUtbGlrZSBzdHJ1Y3R1cmUsIHdoZXJlIGVsZW1lbnRzLCBhdHRyaWJ1dGVzLCBhbmQgdGV4dCBub2RlcyBhcmUgb3JnYW5pemVkIGhpZXJhcmNoaWNhbGx5LgogICAgLSAgICoqU1FMKio6IFNRTCAoU3RydWN0dXJlZCBRdWVyeSBMYW5ndWFnZSkgaXMgdXNlZCBmb3IgcXVlcnlpbmcgcmVsYXRpb25hbCBkYXRhYmFzZXMuIEl0IHdvcmtzIHdpdGggdGFibGVzIGNvbnNpc3Rpbmcgb2Ygcm93cyBhbmQgY29sdW1ucywgYW5kIGl0IHJlcHJlc2VudHMgZGF0YSBpbiBhIHRhYnVsYXIgZm9ybWF0LgoyLiAgKipEYXRhIFNvdXJjZSoqOgogICAgLSAgICoqWFBhdGgqKjogWFBhdGggaXMgdXNlZCB0byBxdWVyeSBzZW1pLXN0cnVjdHVyZWQgYW5kIGhpZXJhcmNoaWNhbCBkYXRhLCBtYWlubHkgWE1MIGRvY3VtZW50cy4gSXQncyBzdWl0YWJsZSBmb3IgbmF2aWdhdGluZyB0aGUgY29tcGxleCBzdHJ1Y3R1cmUgb2YgWE1MIGZpbGVzLgogICAgLSAgICoqU1FMKio6IFNRTCBpcyB1c2VkIGZvciBxdWVyeWluZyBzdHJ1Y3R1cmVkIGRhdGEgc3RvcmVkIGluIHJlbGF0aW9uYWwgZGF0YWJhc2VzLiBJdCBleGNlbHMgYXQgaGFuZGxpbmcgbGFyZ2UgZGF0YXNldHMgd2l0aCB3ZWxsLWRlZmluZWQgc2NoZW1hcy4KMy4gICoqUXVlcnkgU3ludGF4Kio6CiAgICAtICAgKipYUGF0aCoqOiBYUGF0aCB1c2VzIHBhdGggZXhwcmVzc2lvbnMgYW5kIGZ1bmN0aW9ucyB0byBuYXZpZ2F0ZSBhbmQgcXVlcnkgWE1MIGRvY3VtZW50cy4gUGF0aCBleHByZXNzaW9ucyByZXNlbWJsZSBkaXJlY3RvcnkgcGF0aHMgYW5kIGFyZSB1c2VkIHRvIHNwZWNpZnkgdGhlIGxvY2F0aW9uIG9mIGVsZW1lbnRzIG9yIGF0dHJpYnV0ZXMgd2l0aGluIHRoZSBkb2N1bWVudC4KICAgIC0gICAqKlNRTCoqOiBTUUwgZW1wbG95cyBkZWNsYXJhdGl2ZSBzdGF0ZW1lbnRzIHRoYXQgc3BlY2lmeSB3aGF0IGRhdGEgdG8gcmV0cmlldmUsIHVwZGF0ZSwgb3IgbWFuaXB1bGF0ZS4gU1FMIHF1ZXJpZXMgY29uc2lzdCBvZiBTRUxFQ1QsIElOU0VSVCwgVVBEQVRFLCBERUxFVEUsIGFuZCBvdGhlciBjb21tYW5kcy4KNC4gICoqRGF0YSBNYW5pcHVsYXRpb24qKjoKICAgIC0gICAqKlhQYXRoKio6IFdoaWxlIFhQYXRoIHByaW1hcmlseSBmb2N1c2VzIG9uIHF1ZXJ5aW5nIGFuZCBzZWxlY3RpbmcgZGF0YSB3aXRoaW4gWE1MIGRvY3VtZW50cywgaXQgbGFja3MgdGhlIGFiaWxpdHkgdG8gcGVyZm9ybSBkYXRhIG1vZGlmaWNhdGlvbiBvcGVyYXRpb25zIGxpa2UgaW5zZXJ0aW9uLCBkZWxldGlvbiwgb3IgdXBkYXRpbmcuCiAgICAtICAgKipTUUwqKjogU1FMIGlzIGEgY29tcHJlaGVuc2l2ZSBsYW5ndWFnZSB0aGF0IHN1cHBvcnRzIG5vdCBvbmx5IHF1ZXJ5aW5nIGJ1dCBhbHNvIGRhdGEgbW9kaWZpY2F0aW9uIG9wZXJhdGlvbnMgc3VjaCBhcyBJTlNFUlQsIFVQREFURSwgYW5kIERFTEVURSwgbWFraW5nIGl0IHN1aXRhYmxlIGZvciBtYWludGFpbmluZyByZWxhdGlvbmFsIGRhdGFiYXNlcy4KNS4gICoqVXNlIENhc2VzKio6CiAgICAtICAgKipYUGF0aCoqOiBJdCBpcyBjb21tb25seSB1c2VkIGluIHdlYiBzY3JhcGluZywgWE1MIGRvY3VtZW50IHByb2Nlc3NpbmcsIGFuZCBYTUwtYmFzZWQgdGVjaG5vbG9naWVzIGxpa2UgWFNMVCBhbmQgWFF1ZXJ5IGZvciB0cmFuc2Zvcm1pbmcgYW5kIGV4dHJhY3RpbmcgZGF0YSBmcm9tIFhNTCBzb3VyY2VzLgogICAgLSAgICoqU1FMKio6IFNRTCBpcyB3aWRlbHkgdXNlZCBmb3IgZGF0YWJhc2UgbWFuYWdlbWVudCwgZGF0YSByZXRyaWV2YWwsIHJlcG9ydGluZywgYW5kIGRhdGEgbWFuaXB1bGF0aW9uIGluIGFwcGxpY2F0aW9ucyByYW5naW5nIGZyb20gZS1jb21tZXJjZSBwbGF0Zm9ybXMgdG8gZmluYW5jaWFsIHN5c3RlbXMuCjYuICAqKkRhdGEgQ29tcGxleGl0eSoqOgogICAgLSAgICoqWFBhdGgqKjogWFBhdGggZXhjZWxzIGluIGhhbmRsaW5nIGNvbXBsZXggaGllcmFyY2hpZXMgYW5kIHJlbGF0aW9uc2hpcHMgd2l0aGluIFhNTCBkb2N1bWVudHMsIG1ha2luZyBpdCBpZGVhbCBmb3IgdGFza3MgbGlrZSBleHRyYWN0aW5nIGRhdGEgZnJvbSBkZWVwbHkgbmVzdGVkIFhNTCBzdHJ1Y3R1cmVzLgogICAgLSAgICoqU1FMKio6IFNRTCBpcyBkZXNpZ25lZCBmb3IgbWFuYWdpbmcgc3RydWN0dXJlZCBkYXRhIHdpdGggd2VsbC1kZWZpbmVkIHJlbGF0aW9uc2hpcHMsIHdoaWNoIGlzIHN1aXRhYmxlIGZvciBoYW5kbGluZyBjb21wbGV4IGRhdGEgZGVwZW5kZW5jaWVzIGluIHJlbGF0aW9uYWwgZGF0YWJhc2VzLgo3LiAgKipTdGFuZGFyZGl6YXRpb24qKjoKICAgIC0gICAqKlhQYXRoKio6IFhQYXRoIGlzIGEgVzNDIChXb3JsZCBXaWRlIFdlYiBDb25zb3J0aXVtKSBzdGFuZGFyZCwgZW5zdXJpbmcgY29uc2lzdGVuY3kgYW5kIGNvbXBhdGliaWxpdHkgYW1vbmcgZGlmZmVyZW50IFhNTCBwcm9jZXNzaW5nIHRvb2xzLgogICAgLSAgICoqU1FMKio6IFNRTCBpcyBhbiBBTlNJL0lTTyBzdGFuZGFyZCB3aXRoIGRpZmZlcmVudCBkaWFsZWN0cyBmb3IgdmFyaW91cyBkYXRhYmFzZSBzeXN0ZW1zLiBXaGlsZSB0aGVyZSBpcyBhIGNvbW1vbiBjb3JlIG9mIFNRTCwgZWFjaCBkYXRhYmFzZSBtYXkgaGF2ZSBpdHMgb3duIGV4dGVuc2lvbnMgYW5kIHZhcmlhdGlvbnMuCgpUbyByZWNhcCwgWFBhdGggYW5kIFNRTCBhcmUgc3BlY2lhbGl6ZWQgcXVlcnkgbGFuZ3VhZ2VzIHRhaWxvcmVkIGZvciBkaWZmZXJlbnQgZGF0YSBtb2RlbHMgYW5kIHVzZSBjYXNlcy4gWFBhdGggaXMgZm9jdXNlZCBvbiBuYXZpZ2F0aW5nIGFuZCBxdWVyeWluZyBoaWVyYXJjaGljYWwsIHNlbWktc3RydWN0dXJlZCBkYXRhIGluIFhNTCBkb2N1bWVudHMsIHdoaWxlIFNRTCBpcyBkZXNpZ25lZCBmb3IgbWFuYWdpbmcgc3RydWN0dXJlZCBkYXRhIGluIHJlbGF0aW9uYWwgZGF0YWJhc2VzIHdpdGggc3VwcG9ydCBmb3IgZGF0YSBtYW5pcHVsYXRpb24gb3BlcmF0aW9ucy4gVGhlIGNob2ljZSBiZXR3ZWVuIFhQYXRoIGFuZCBTUUwgZGVwZW5kcyBvbiB0aGUgbmF0dXJlIG9mIHRoZSBkYXRhIGFuZCB0aGUgc3BlY2lmaWMgcmVxdWlyZW1lbnRzIG9mIHRoZSB0YXNrIGF0IGhhbmQuCgojIyBYUGF0aCBFeHByZXNzaW9uIEV2YWx1YXRpb24KCkV2YWx1YXRpbmcgYW4gWFBhdGggZXhwcmVzc2lvbiBhZ2FpbnN0IGFuIFhNTCBkb2N1bWVudCBpbnZvbHZlcyB1c2luZyBhIHBhcnNlciBvciBsaWJyYXJ5IHRoYXQgc3VwcG9ydHMgWFBhdGggdG8gc2VhcmNoLCBuYXZpZ2F0ZSwgYW5kIHJldHJpZXZlIGRhdGEgZnJvbSB0aGUgWE1MIGRvY3VtZW50IGJhc2VkIG9uIHRoZSBzcGVjaWZpZWQgWFBhdGggcXVlcnkuCgpUaGVyZSBhcmUgdHdvIGNvbW1vbiB3YXlzIHRvIGV4ZWN1dGUgWFBhdGggcXVlcmllcyBhZ2FpbnN0IGFuIFhNTCBkb2N1bWVudDoKCjEuICB3cml0ZSBhIHByb2dyYW0gdGhhdCBsb2FkcyB0aGUgWE1MIGFuZCBldmVhbHVhdGVzIHRoZSBYUGF0aCBxdWVyeSBleHByZXNzaW9uCjIuICBsb2FkIHRoZSBYTUwgZG9jdW1lbnQgaW50byBhIHRvb2wgdG8gZXhlY3V0ZSAqYWQgaG9jKiBxdWVyaWVzCgojIyMgQWQtSG9jIFhQYXRoIEV4cHJlc3Npb24gRXZhbHVhdGlvbgoKVGhlcmUgYXJlIHNldmVyYWwgb25saW5lIHRvb2xzIGFuZCB3ZWJzaXRlcyB0aGF0IGFsbG93IHlvdSB0byBldmFsdWF0ZSAqYWQgaG9jKiBYUGF0aCBxdWVyaWVzIGFnYWluc3QgWE1MIGRvY3VtZW50cyB3aXRob3V0IHRoZSBuZWVkIGZvciBzZXR0aW5nIHVwIGxvY2FsIGRldmVsb3BtZW50IGVudmlyb25tZW50cyBvciB3cml0aW5nIGNvZGUuIFRoZXNlIHRvb2xzIGFyZSBoYW5keSBmb3IgcXVpY2sgWFBhdGggdGVzdGluZyBhbmQgZXhwZXJpbWVudGF0aW9uLiBIZXJlIGFyZSBzb21lIGNvbW1vbiB3ZWJzaXRlcyBmb3IgZXZhbHVhdGluZyAqYWQgaG9jKiBYUGF0aCBxdWVyaWVzOgoKMS4gICoqRnJlZUZvcm1hdHRlciBYUGF0aCBUZXN0ZXIqKjogW0ZyZWVGb3JtYXR0ZXIgWFBhdGggVGVzdGVyXShodHRwczovL3d3dy5mcmVlZm9ybWF0dGVyLmNvbS94cGF0aC10ZXN0ZXIuaHRtbCkgaXMgYSBzdHJhaWdodGZvcndhcmQgb25saW5lIHRvb2wgZm9yIHRlc3RpbmcgWFBhdGggZXhwcmVzc2lvbnMgYWdhaW5zdCBYTUwgZGF0YS4gSXQgcHJvdmlkZXMgYSBjbGVhciBhbmQgc2ltcGxlIGludGVyZmFjZSBmb3IgZW50ZXJpbmcgWE1MIGRhdGEgYW5kIFhQYXRoIHF1ZXJpZXMgYW5kIHZpZXdpbmcgdGhlIHJlc3VsdHMuCgoyLiAgKipYUGF0aCBWaXN1YWxpemVyIGFuZCBUZXN0ZXIqKjogW1hQYXRoIFZpc3VhbGl6ZXIgYW5kIFRlc3Rlcl0oaHR0cDovL3d3dy54cGF0aHRlc3Rlci5jb20veHBhdGgpIG9mZmVycyBhIHVzZXItZnJpZW5kbHkgaW50ZXJmYWNlIGZvciBldmFsdWF0aW5nIFhQYXRoIGV4cHJlc3Npb25zLiBJdCBhbGxvd3MgeW91IHRvIHVwbG9hZCBYTUwgZmlsZXMsIGVudGVyIFhQYXRoIHF1ZXJpZXMsIGFuZCBzZWUgdGhlIHJlc3VsdHMgaW4gYSBzdHJ1Y3R1cmVkIGZvcm1hdC4KCjMuICAqKkNvZGVCZWF1dGlmeSBYUGF0aCBUZXN0ZXIqKjogW0NvZGVCZWF1dGlmeSBYUGF0aCBUZXN0ZXJdKGh0dHBzOi8vY29kZWJlYXV0aWZ5Lm9yZy9YcGF0aC1UZXN0ZXIpIHByb3ZpZGVzIGFuIG9ubGluZSBYUGF0aCBldmFsdWF0b3Igd2l0aCBYTUwgZGF0YSBpbnB1dCBhbmQgYW4gb3V0cHV0IHBhbmUgdG8gZGlzcGxheSB0aGUgcmVzdWx0cyBvZiB5b3VyIFhQYXRoIHF1ZXJpZXMuCgo0LiAgKipPbmxpbmUgWFBhdGggVGVzdGVyIGJ5IERldkd1cnUqKjogW09ubGluZSBYUGF0aCBUZXN0ZXIgYnkgRGV2R3VydV0oaHR0cHM6Ly93d3cuZGV2Z3VydS5jb20veHBhdGgvZGVmYXVsdC5hc3ApIGlzIGEgc2ltcGxlIG9ubGluZSB0b29sIHRoYXQgbGV0cyB5b3UgZW50ZXIgWE1MIGRhdGEgYW5kIFhQYXRoIGV4cHJlc3Npb25zLCBhbmQgaXQgZGlzcGxheXMgdGhlIG1hdGNoaW5nIG5vZGVzLgoKNS4gICoqWFBhdGggRXZhbHVhdG9yIGJ5IFczU2Nob29scyoqOiBbWFBhdGggRXZhbHVhdG9yIGJ5IFczU2Nob29sc10oaHR0cHM6Ly93d3cudzNzY2hvb2xzLmNvbS94bWwveHBhdGhfaW50cm8uYXNwKSBpcyBwYXJ0IG9mIHRoZSBXM1NjaG9vbHMgd2Vic2l0ZSwga25vd24gZm9yIGl0cyB3ZWIgZGV2ZWxvcG1lbnQgdHV0b3JpYWxzLiBJdCBhbGxvd3MgeW91IHRvIGVudGVyIFhNTCBkYXRhIGFuZCBYUGF0aCBleHByZXNzaW9ucyB0byBzZWUgdGhlIHJlc3VsdHMuCgo2LiAgKipYUGF0aEZpZGRsZSoqOiBbWFBhdGhGaWRkbGVdKGh0dHBzOi8veHBhdGhmaWRkbGUuc3VyZ2Uuc2gvKSBpcyBhbiBvbmxpbmUgWFBhdGggdGVzdGVyIHdpdGggYSBjbGVhbiBhbmQgbWluaW1hbGlzdGljIGludGVyZmFjZS4gWW91IGNhbiBpbnB1dCB5b3VyIFhNTCBkYXRhIGFuZCBYUGF0aCBxdWVyaWVzIGFuZCBpbnN0YW50bHkgdmlldyB0aGUgcmVzdWx0cy4KCjcuICAqKk9ubGluZSBYTUwgVG9vbHMqKjogW09ubGluZSBYTUwgVG9vbHNdKGh0dHBzOi8vb25saW5leG1sdG9vbHMuY29tL3htbC10by14cGF0aC1nZW5lcmF0b3IpIG9mZmVycyBhIHN1aXRlIG9mIFhNTC1yZWxhdGVkIHRvb2xzLCBpbmNsdWRpbmcgYW4gWFBhdGggdGVzdGVyLiBZb3UgY2FuIGlucHV0IHlvdXIgWE1MIGRhdGEgYW5kIFhQYXRoIGV4cHJlc3Npb25zIGFuZCB2aXN1YWxpemUgdGhlIHJlc3VsdHMuCgo4LiAgKipPbmxpbmUgWFBhdGggVGVzdGVyIGJ5IFR1dG9yaWFsc1BvaW50Kio6IFtPbmxpbmUgWFBhdGggVGVzdGVyIGJ5IFR1dG9yaWFsc1BvaW50XShodHRwczovL3d3dy50dXRvcmlhbHNwb2ludC5jb20veHBhdGhfb25saW5lLnBocCkgaXMgYSBoYW5keSB0b29sIGZvciB0ZXN0aW5nIFhQYXRoIGV4cHJlc3Npb25zIGFnYWluc3QgWE1MIGRhdGEuIEl0IHByb3ZpZGVzIGEgc2ltcGxlIGVkaXRvciBhbmQgcmVzdWx0IGRpc3BsYXkuCgo5LiAgKipPeHlnZW4gWE1MIFdlYiBBdXRob3IqKjogW094eWdlbiBYTUwgV2ViIEF1dGhvcl0oaHR0cHM6Ly93d3cub3h5Z2VueG1sLmNvbS93ZWJhcHAtZGVtby1hd3MvYXBwL294eWdlbi5odG1sKSBpcyBhIHBvd2VyZnVsIG9ubGluZSBYTUwgZWRpdG9yIHRoYXQgaW5jbHVkZXMgYW4gWFBhdGggZXZhbHVhdG9yLiBJdCdzIG1vcmUgY29tcHJlaGVuc2l2ZSBhbmQgZmVhdHVyZS1yaWNoIHRoYW4gc29tZSBvZiB0aGUgb3RoZXIgdG9vbHMgbGlzdGVkIGhlcmUuCgpUaGVzZSB3ZWJzaXRlcyBwcm92aWRlIGNvbnZlbmllbnQgYW5kIGFjY2Vzc2libGUgZW52aXJvbm1lbnRzIGZvciB0cnlpbmcgb3V0IFhQYXRoIHF1ZXJpZXMgYWdhaW5zdCBzYW1wbGUgWE1MIGRhdGEuIERlcGVuZGluZyBvbiB5b3VyIHNwZWNpZmljIG5lZWRzIGFuZCBwcmVmZXJlbmNlcywgeW91IGNhbiBjaG9vc2UgdGhlIG9uZSB0aGF0IGJlc3Qgc3VpdHMgeW91ciB3b3JrZmxvdyBhbmQgcmVxdWlyZW1lbnRzLgoKIyMjIFhQYXRoIHdpdGhpbiBQcm9ncmFtcwoKSGVyZSdzIGEgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgaW5kZXBlZGVudCBzdGVwLWJ5LXN0ZXAgZ3VpZGUgb24gaG93IHRvIGV2YWx1YXRlIGFuIFhQYXRoIGV4cHJlc3Npb24gYWdhaW5zdCBhbiBYTUwgZG9jdW1lbnQ6CgoxLiAgKipMb2FkIHRoZSBYTUwgRG9jdW1lbnQqKjoKICAgIC0gICBGaXJzdCwgeW91IG5lZWQgdG8gbG9hZCB0aGUgWE1MIGRvY3VtZW50IGludG8gbWVtb3J5LiBUaGlzIGNhbiBiZSBkb25lIHVzaW5nIGFuIFhNTCBwYXJzaW5nIGxpYnJhcnkgb3IgdG9vbCBhdmFpbGFibGUgaW4geW91ciBwcm9ncmFtbWluZyBsYW5ndWFnZSBvZiBjaG9pY2UuIENvbW1vbiBjaG9pY2VzIGluY2x1ZGUgbGlicmFyaWVzIGxpa2UgbHhtbCBpbiBQeXRob24sIFhtbERvY3VtZW50IGluIEMjLCBvciBidWlsdC1pbiBmdW5jdGlvbnMgbGlrZSBgRE9NRG9jdW1lbnRgIGluIFBIUC4KMi4gICoqSW5pdGlhbGl6ZSB0aGUgWFBhdGggUHJvY2Vzc29yKio6CiAgICAtICAgTmV4dCwgeW91IG5lZWQgdG8gaW5pdGlhbGl6ZSB0aGUgWFBhdGggcHJvY2Vzc29yIG9yIGNyZWF0ZSBhbiBYUGF0aCBvYmplY3QuIFRoaXMgb2JqZWN0IGFsbG93cyB5b3UgdG8gY29tcGlsZSBhbmQgZXZhbHVhdGUgWFBhdGggZXhwcmVzc2lvbnMgYWdhaW5zdCB0aGUgbG9hZGVkIFhNTCBkb2N1bWVudC4gVGhlIHNwZWNpZmljIG1ldGhvZCBmb3IgaW5pdGlhbGl6aW5nIHRoZSBwcm9jZXNzb3IgbWF5IHZhcnkgZGVwZW5kaW5nIG9uIHRoZSBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbmQgbGlicmFyaWVzIHVzZWQuCjMuICAqKkNvbXBpbGUgdGhlIFhQYXRoIEV4cHJlc3Npb24qKjoKICAgIC0gICBDb21waWxlIHRoZSBYUGF0aCBleHByZXNzaW9uIHlvdSB3YW50IHRvIGV2YWx1YXRlLiBYUGF0aCBleHByZXNzaW9ucyBjYW4gdmFyeSBpbiBjb21wbGV4aXR5IGFuZCBzcGVjaWZpY2l0eSwgYW5kIHRoZXkgYXJlIHVzZWQgdG8gZGVmaW5lIHdoYXQgZGF0YSB5b3Ugd2FudCB0byByZXRyaWV2ZSBmcm9tIHRoZSBYTUwgZG9jdW1lbnQuCjQuICAqKkV2YWx1YXRlIHRoZSBYUGF0aCBFeHByZXNzaW9uKio6CiAgICAtICAgVXNlIHRoZSBYUGF0aCBwcm9jZXNzb3IgdG8gZXZhbHVhdGUgdGhlIGNvbXBpbGVkIFhQYXRoIGV4cHJlc3Npb24gYWdhaW5zdCB0aGUgbG9hZGVkIFhNTCBkb2N1bWVudC4gVGhlIGV2YWx1YXRpb24gcHJvY2VzcyB3aWxsIHJldHVybiBhIHJlc3VsdCwgd2hpY2ggY2FuIGJlIG9uZSBvciBtb3JlIG9mIHRoZSBmb2xsb3dpbmc6CiAgICAgICAgLSAgIEEgc2luZ2xlIG5vZGUgKGUuZy4sIGVsZW1lbnQsIGF0dHJpYnV0ZSwgdGV4dCBub2RlKQogICAgICAgIC0gICBBIG5vZGUgbGlzdCAobXVsdGlwbGUgbm9kZXMgbWF0Y2hpbmcgdGhlIGV4cHJlc3Npb24pCiAgICAgICAgLSAgIEEgYm9vbGVhbiB2YWx1ZSAodHJ1ZSBvciBmYWxzZSwgZGVwZW5kaW5nIG9uIHdoZXRoZXIgdGhlIGV4cHJlc3Npb24gbWF0Y2hlcykKICAgICAgICAtICAgQSBzdHJpbmcgdmFsdWUgKGUuZy4sIHRoZSB0ZXh0IGNvbnRlbnQgb2YgYSBzZWxlY3RlZCBlbGVtZW50KQo1LiAgKipIYW5kbGUgdGhlIFJlc3VsdCoqOgogICAgLSAgIERlcGVuZGluZyBvbiB0aGUgcmVzdWx0IG9mIHRoZSBYUGF0aCBldmFsdWF0aW9uLCB5b3UgY2FuIHBlcmZvcm0gdmFyaW91cyBhY3Rpb25zOgogICAgICAgIC0gICBJZiB0aGUgcmVzdWx0IGlzIGEgc2luZ2xlIG5vZGUsIHlvdSBjYW4gYWNjZXNzIGl0cyBkYXRhIG9yIGF0dHJpYnV0ZXMuCiAgICAgICAgLSAgIElmIHRoZSByZXN1bHQgaXMgYSBub2RlIGxpc3QsIHlvdSBjYW4gaXRlcmF0ZSB0aHJvdWdoIHRoZSBsaXN0IHRvIHByb2Nlc3MgZWFjaCBtYXRjaGluZyBub2RlLgogICAgICAgIC0gICBJZiB0aGUgcmVzdWx0IGlzIGEgYm9vbGVhbiB2YWx1ZSwgeW91IGNhbiB1c2UgaXQgdG8gbWFrZSBjb25kaXRpb25hbCBkZWNpc2lvbnMuCiAgICAgICAgLSAgIElmIHRoZSByZXN1bHQgaXMgYSBzdHJpbmcgdmFsdWUsIHlvdSBjYW4gYWNjZXNzIHRoZSBzZWxlY3RlZCB0ZXh0IGNvbnRlbnQuCjYuICAqKlJlcGVhdCBhcyBOZWVkZWQqKjoKICAgIC0gICBZb3UgY2FuIGV2YWx1YXRlIG11bHRpcGxlIFhQYXRoIGV4cHJlc3Npb25zIGFnYWluc3QgdGhlIHNhbWUgWE1MIGRvY3VtZW50IHRvIHJldHJpZXZlIGRpZmZlcmVudCBzZXRzIG9mIGRhdGEgb3IgaW5mb3JtYXRpb24uCjcuICAqKkVycm9yIEhhbmRsaW5nKio6CiAgICAtICAgSW1wbGVtZW50IGVycm9yIGhhbmRsaW5nIHRvIGhhbmRsZSBjYXNlcyB3aGVyZSB0aGUgWFBhdGggZXhwcmVzc2lvbiBpcyBpbnZhbGlkIG9yIGRvZXMgbm90IG1hdGNoIGFueSBkYXRhIGluIHRoZSBYTUwgZG9jdW1lbnQuIE1vc3QgWFBhdGggcHJvY2Vzc29ycyBwcm92aWRlIG1lY2hhbmlzbXMgdG8gY2F0Y2ggYW5kIGhhbmRsZSBleGNlcHRpb25zIG9yIGVycm9ycy4KOC4gICoqUmVsZWFzZSBSZXNvdXJjZXMqKjoKICAgIC0gICBBZnRlciB5b3UndmUgZmluaXNoZWQgZXZhbHVhdGluZyBYUGF0aCBleHByZXNzaW9ucyBhbmQgcHJvY2Vzc2luZyB0aGUgWE1MIGRvY3VtZW50LCBpdCdzIGVzc2VudGlhbCB0byByZWxlYXNlIGFueSByZXNvdXJjZXMgb3IgbWVtb3J5IGFzc29jaWF0ZWQgd2l0aCB0aGUgWFBhdGggcHJvY2Vzc29yIGFuZCBYTUwgZG9jdW1lbnQgdG8gcHJldmVudCBtZW1vcnkgbGVha3MuCgpIZXJlJ3MgYSBzaW1wbGlmaWVkIGV4YW1wbGUgaW4gUHl0aG9uIChzbyB5b3UgY2FuIHNlZSBob3cgaXQgY29tcGFyZXMgdG8gUiB3aGljaCBpcyB1c2VkIGluIHRoZSByZW1haW5kZXIgb2YgdGhlIHR1dG9yaWFsKSB1c2luZyB0aGUgKmx4bWwqIGxpYnJhcnkgdG8gaWxsdXN0cmF0ZSBob3cgeW91IG1pZ2h0IGV2YWx1YXRlIGFuIFhQYXRoIGV4cHJlc3Npb24gYWdhaW5zdCBhbiBYTUwgZG9jdW1lbnQ6CgpgYGAgcHl0aG9uCmZyb20gbHhtbCBpbXBvcnQgZXRyZWUKCiMgTG9hZCB0aGUgWE1MIGRvY3VtZW50CnhtbF9kYXRhID0gIiIiCjxyb290PgogICAgPGl0ZW0gaWQ9IjEiPkFwcGxlPC9pdGVtPgogICAgPGl0ZW0gaWQ9IjIiPkJhbmFuYTwvaXRlbT4KPC9yb290PgoiIiIKCnJvb3QgPSBldHJlZS5mcm9tc3RyaW5nKHhtbF9kYXRhKQoKIyBJbml0aWFsaXplIHRoZSBYUGF0aCBwcm9jZXNzb3IKeHBhdGhfcHJvY2Vzc29yID0gZXRyZWUuWFBhdGgoIi8vaXRlbVtAaWQ9JzEnXSIpCgojIEV2YWx1YXRlIHRoZSBYUGF0aCBleHByZXNzaW9uCnJlc3VsdCA9IHhwYXRoX3Byb2Nlc3Nvcihyb290KQoKIyBIYW5kbGUgdGhlIHJlc3VsdAppZiByZXN1bHQ6CiAgICBwcmludChyZXN1bHRbMF0udGV4dCkgICMgT3V0cHV0OiBBcHBsZQplbHNlOgogICAgcHJpbnQoIk5vIG1hdGNoaW5nIG5vZGUgZm91bmQuIikKYGBgCgpUaGlzIGNvZGUgbG9hZHMgYW4gWE1MIGRvY3VtZW50LCBpbml0aWFsaXplcyB0aGUgWFBhdGggcHJvY2Vzc29yLCBldmFsdWF0ZXMgdGhlIFhQYXRoIGV4cHJlc3Npb24sIGFuZCBoYW5kbGVzIHRoZSByZXN1bHQuCgojIyBYUGF0aCBFdmFsdWF0aW9uIGluIFIKClhQYXRoIGNhbiBiZSB1c2VkIGluIGFueSBwcm9ncmFtbWluZyBsYW5ndWFnZSB0aGF0IHByb3ZpZGUgWE1MIHN1cHBvcnQsIGluY2x1ZGluZyBQeXRob24sIEphdmEsIEphdmFTY3JpcHQsIEMjLCBhbW9uZyBtYW55IG90aGVycy4gSW4gdGhpcyBsZXNzb24sIHdlIHdpbGwgZm9jdXMgb24gdXNpbmcgWFBhdGggd2l0aGluIFIsIGFsdGhvdWdoIHRoZSBYUGF0aCBleHByZXNzaW9ucyBhcmUgbGFuZ3VhZ2UgaW5kZXBlbmRlbnQuCgpUbyB1c2UgYW55IG9mIHRoZSBYTUwgcGFyc2luZyBvciBhbnkgb2YgdGhlIFhQYXRoIGZ1bmN0aW9uIHlvdSB3aWxsIG5lZWQgYW4gWE1MIHBhY2thZ2UuIFRoZSAqKlhNTCoqIHBhY2thZ2UgaXMgb25lIG9mIHNldmVyYWwgcGFja2FnZXMgYW5kIHRoZSBvbmUgd2UgYXJlIHVzaW5nIGluIHRoaXMgbGVzc29uLCBzbyBiZSBzdXJlIHRvIGluc3RhbGwgaXQgZmlyc3QuCgo+IE5vdGUgdGhhdCB0aGUgKipYTUwqKiBwYWNrYWdlIG9ubHkgc3VwcG9ydHMgWFBhdGggVmVyc2lvbiAxLjAgYW5kIG5vdCB0aGUgbmV3ZXIgMi4wIGFuZCAzLjEgdmVyc2lvbnMuCgojIyBMb2FkaW5nIGFuIFhNTCBEb2N1bWVudAoKTGV0J3Mgc3RhcnQgYnkgbG9hZGluZyBhbiBYTUwgZG9jdW1lbnQuIFRoZXJlIGFyZSBzZXZlcmFsIGZ1bmN0aW9ucyBmb3IgbG9hZGluZyB0aGVtIHdoaWNoIGdlbmVyYWxseSBhbGwgd29yayB0aGUgc2FtZSB3YXksIGFsdGhvdWdoIHNvbWUgY3JlYXRlIGRpZmZlcmVudCBpbi1tZW1vcnkgc3RydWN0dXJlcyByZXByZXNlbnRpbmcgdGhlIFhNTCB0cmVlIGFuZCB0aHVzIHNvbWUgYXJlIG1vcmUgYW5kIHNvbWUgYXJlIGxlc3MgZWZmaWNpZW50LiBYTUwgZG9jdW1lbnRzIChvciBmaWxlcykgY2FuIGJlIGxvYWRlZCBmcm9tIHRoZSBsb2NhbCBmaWxlIHN5c3RlbSBvciBmcm9tIGEgVVJMLgoKIyMjIExvYWQgWE1MIGZyb20gRmlsZQoKYGBge3J9CmxpYnJhcnkoWE1MKQoKeG1sRmlsZSA8LSAiQ0RDYXRhbG9nMi54bWwiCgp4bWxPYmogPC0geG1sUGFyc2UoeG1sRmlsZSkKeG1sT2JqVHJlZSA8LSB4bWxUcmVlUGFyc2UoeG1sRmlsZSkKYGBgCgo+IFRoZSBlcnJvciAqKkVycm9yOiBYTUwgY29udGVudCBkb2VzIG5vdCBzZWVtIHRvIGJlIFhNTDogJycqKiBpcyBvZnRlbiBjYXVzZWQgYnkgYSBmaWxlIHRoYXQgY2Fubm90IGJlIGZvdW5kIGFuZCBpcyBvZnRlbiBkdWUgdG8gYSBtaXNzcGVsbGVkIGZpbGUgb3IgcGF0aCBuYW1lLgoKIyMjIExvYWQgWE1MIHZpYSBVUkwKCmBgYHtyfQp4bWxVUkwgPC0iaHR0cDovL2QzOTZxdXN6YTQwb3JjLmNsb3VkZnJvbnQubmV0L2dldGRhdGElMkZkYXRhJTJGcmVzdGF1cmFudHMueG1sIgp4bWxPYmpUcmVlIDwtIHhtbFRyZWVQYXJzZSh4bWxVUkwpCmBgYAoKPiBOb3RlIHRoYXQgdGhlIFIgcGFyc2luZyBmdW5jdGlvbnMgZG8gbm90IHN1cHBvcnQgKmh0dHBzKiBzbyBiZSBzdXJlIHRoYXQgYW55IFVSTCBzdGFydHMgd2l0aCBbKmh0dHA6Ly8qXShodHRwOi8vKXsudXJpfSByYXRoZXIgdGhhbiBbKmh0dHBzOi8vKl0oaHR0cHM6Ly8pey51cml9LiBJZiB5b3UgZ2V0IHRoZSBlcnJvciAiWE1MIGNvbnRlbnQgZG9lcyBub3Qgc2VlbSB0byBiZSBYTUwiIHRoZW4gdGhhdCBpcyBvZnRlbiB0aGUgY2F1c2UuCgojIyMgeG1sUGFyc2UgKnZzKiB4bWxUcmVlUGFyc2UKCjxjb2RlPnhtbFBhcnNlPC9jb2RlPiBpcyBhIHZlcnNpb24gb2YgPGNvZGU+eG1sVHJlZVBhcnNlPC9jb2RlPiB3aGVyZSBhcmd1bWVudCAqdXNlSW50ZXJuYWxOb2RlcyogaXMgc2V0IHRvICpUUlVFKi4gSWYgeW91IHdhbnQgdG8gZ2V0IGFuIFIgb2JqZWN0IHVzZSA8Y29kZT54bWxUcmVlUGFyc2U8L2NvZGU+LiBXaGlsZSB0aGlzIGlzIGdlbmVyYWxseSBub3QgdmVyeSBlZmZpY2llbnQgZm9yIGxhcmdlIGRvY3VtZW50IGFuZCBvZnRlbiB1bm5lY2Vzc2FyeSBpZiB5b3Ugd2FudCB0byBleHRyYWN0IG9ubHkgcGFydHMgb2YgdGhlIFhNTCBkb2N1bWVudCwgaXQgaGFzIHRoZSBiZW5lZml0IHRoYXQgeW91IGNhbiB0cmF2ZXJzZSB0aGUgWE1MIHRyZWUgdXNpbmcgbmFtZWQgdHJhdmVyc2FsLCAqZS5nLiosIHJvb3QkY2hpbGQxJGNoaWxkXCQuLi4KClVzaW5nIDxjb2RlPnhtbFBhcnNlPC9jb2RlPiBpcyBnZW5lcmFsbHkgbW9yZSBlZmZpY2llbnQgYXMgaXQgcmV0dXJucyBhIHBvaW50ZXIgdG8gYSBDIHN0cnVjdHVyZS4gVG8gYWNjZXNzIHRoaXMgc3RydWN0dXJlIHJlcXVpcmVzIFhQYXRoLCBhbHRob3VnaCA8Y29kZT54bWxUcmVlUGFyc2U8L2NvZGU+IHN1cHBvcnRzIFhQYXRoIGFzIHdlbGwuCgpZb3UgY2FuIHNlZSB0aGUgZGlmZmVyZW50IGNsYXNzIHR5cGVzIGluIHRoZSBjb2RlIGJlbG93LgoKYGBge3J9CmNsYXNzKHhtbE9iaikKY2xhc3MoeG1sT2JqVHJlZSkKYGBgCgojIyBBcHBseWluZyBhbiBYUGF0aCBFeHByZXNzaW9uCgpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIGFwcGx5IGFuIFhQYXRoIGV4cHJlc3Npb24gdG8gYSBwYXJzZWQgWE1MIG9iamVjdCwgdGhlIG1vc3QgY29tbW9uIG9mIHdoaWNoIHRvIHVzZSB0aGUgZnVuY3Rpb24gPGNvZGU+eG1sUGF0aFNBcHBseTwvY29kZT4uIFRoZSBjb2RlIGNodW5rcyBiZWxvdyBwcmVzdW1lcyB0aGF0IDxjb2RlPmZwPC9jb2RlPiBpcyB0aGUgcGF0aCBvciBVUkwgdG8gYW4gWE1MIGRvY3VtZW50LgoKVGhlIDxjb2RlPnhtbFBhdGhTQXBwbHk8L2NvZGU+IGZ1bmN0aW9uIGFwcGxpZXMgdGhlIGZ1bmN0aW9uIHBhc3NlZCBhcyBhIHBhcmFtZXRlciB0byBhbGwgbWF0Y2hpbmcgZWxlbWVudHMgb2YgYW4gWFBhdGggZXhwcmVzc2lvbiByYXRoZXIgdGhhbiByZXR1cm5pbmcgdGhlIGVsZW1lbnRzLiBJbiB0aGUgY29kZSBjaHVuayBiZWxvdywgZWFjaCBtYXRjaGluZyBlbGVtZW50IGhhcyB0aGUgPGNvZGU+eG1sVmFsdWU8L2NvZGU+IGZ1bmN0aW9uIGFwcGxpZWQgdG8gaXQgYW5kIHRodXMgdGhlIHZhbHVlIG9mIHRoZSBtYXRjaGluZyBlbGVtZW50cyBhcmUgZXh0cmFjdGVkLiBSZWNhbGwgdGhhdCB0aGUgdmFsdWUgb2YgYW4gZWxlbWVudCBpcyBldmVyeXRoaW5nIHRoYXQgaXMgYmV0d2VlbiB0aGUgb3BlbmluZyBhbmQgY2xvc2luZyB0YWdzLiBGb3IgZXhhbXBsZSwgdGhlIHZhbHVlIG9mIGB7eG1sfSA8dGFnPnNvbWUgdmFsdWU8L3RhZz5gIGlzICpzb21lVmFsdWUqLiBOb3RlIHRoYXQgdGhlIHJldHVybmVkIG9iamVjdCBpcyBhIHZlY3RvciBvZiBjaGFyYWN0ZXJzIChsaWtlIGFuIGFycmF5IG9mIHN0cmluZ3MgaW4gb3RoZXIgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzKSBhbmQgdGh1cyBjYW4gYmUgYWNjZXNzZWQgYXMgc3VjaC4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhtbE9iaiA8LSB4bWxQYXJzZSh4bWxGaWxlKQoKeHBhdGhFeCA8LSAiLy9jZC90aXRsZSIKYXJ0aXN0cyA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgsIHhtbFZhbHVlKQoKaGVhZChhcnRpc3RzLCAzKQoKY2xhc3MoYXJ0aXN0cykKCiMgYWNjZXNzIHRoZSBzZWNvbmQgZWxlbWVudApwcmludChwYXN0ZSgiVGhlIHNlY29uZCBhcnRpc3QgaXM6ICIsYXJ0aXN0c1syXSkpCmBgYAoKIyMjIFJldHJpZXZpbmcgWE1MIEF0dHJpYnV0ZXMKClRoZXJlIGFyZSB0d28gd2F5cyB0byByZXRyaWV2ZSBhbiBlbGVtZW50J3MgYXR0cmlidXRlcy4gT25lLCB1c2UgYW4gWFBhdGggZXhwcmVzc2lvbiB3aXRoIDxjb2RlPnhwYXRoU0FwcGx5PC9jb2RlPiAoYnV0IHdpdGhvdXQgYXBwbHlpbmcgdGhlIDxjb2RlPnhtbFZhbHVlPC9jb2RlPiBmdW5jdGlvbikuIFR3bywgdXNlIHRoZSA8Y29kZT54bWxBdHRyczwvY29kZT4gZnVuY3Rpb24gZnJvbSBhIHNwZWNpZmljIG5vZGUgLS0gd2hpY2ggcmVxdWlyZXMgdHJhdmVyc2luZyB0aGUgdHJlZS4KClRoZSB1c2Ugb2YgYW4gWFBhdGggZXhwcmVzc2lvbiBpcyBnZW5lcmFsbHkgcHJlZmVyYWJsZSBhbmQgbW9yZSBtYWludGFpbmFibGUuCgpgYGB7ciBjb21tZW50PSIifQp4bWxPYmogPC0geG1sUGFyc2UoeG1sRmlsZSkKCiMgQXBwcm9hY2ggMTogdXNlIGFuIFhQYXRoIGV4cHJlc3Npb24gdG8gZ2V0IHRoZSBhdHRyaWJ1dGUgY291bnRyeQoKeHBhdGhFeCA8LSAiLy9jZC9jb21wYW55L0Bjb3VudHJ5Igpjb3VudHJpZXMgPC0geHBhdGhTQXBwbHkoeG1sT2JqLCB4cGF0aEV4KQoKaGVhZChjb3VudHJpZXMsIDMpCmBgYAoKIyMgVXNpbmcgVmFsdWVzIGluIFIKCkFsbCBvZiB0aGUgdmFsdWVzIHJldHJpZXZlZCBmcm9tIFhNTCBhcmUgdGV4dCBhbmQgbXVzdCBiZSBjb252ZXJ0ZWQgdG8gc3RyaW5ncywgb2Z0ZW4gYWZ0ZXIgcGFyc2luZyB0aGUgdGV4dC4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIi8vY2QvcHJpY2UiCnByaWNlcyA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgsIHhtbFZhbHVlKQoKIyB0aGUgdmFsdWVzIGluIHRoZSB2ZWN0b3IgInByaWNlcyIgYXJlIGNoYXJhY3RlciBzdHJpbmdzCiMgbWVhbihwcmljZXMpIHJlc3VsdHMgaW4gYW4gZXJyb3IKCnByaWNlcy5uIDwtIGFzLm51bWVyaWMocHJpY2VzKQphdmcgPC0gbWVhbihwcmljZXMubikKCnByaW50KHBhc3RlMCgiVGhlIGF2ZXJhZ2UgcHJpY2UgaXMgJCIsIHJvdW5kKGF2ZywyKSkpCgpgYGAKCiMjIFJldmlldwoKU28sIHRvIHN1bW1hcml6ZSwgdGhlIHdheSB0aGF0IFhNTCBkb2N1bWVudHMgYXJlIHByb2Nlc3NlZCBpbiBSIGlzIGFzIGZvbGxvd3M6CgoxLiAgaW5zdGFsbCB0aGUgcGFja2FnZSAqKlhNTCoqCjIuICBsb2FkIHRoZSBwYWNrYWdlICoqWE1MKioKMy4gIHNldCB1cCBhbiBYUGF0aCBleHByZXNzaW9uCjQuICBjYWxsIDxjb2RlPnhwYXRoU0FwcGx5KCk8L2NvZGU+CjUuICB1c2UgdGhlIHJlc3VsdCBub2RlIG9yIGNhbGwgPGNvZGU+eG1sVmFsdWUoKTwvY29kZT4gdG8gZ2V0IHRoZSB2YWx1ZSBvZiB0aGUgbm9kZQoKQW5kLCByZW1lbWJlciB0aGF0IGFsbCB2YWx1ZXMgYXJlIGNoYXJhY3RlcnMgKHRleHQgb3Igc3RyaW5ncykgaW4gUiBhbmQgbXVzdCBiZSBjb2VyY2VkIHRvIHRoZSBjb3JyZWN0IGRhdGEgdHlwZSAtLSBwZXJoYXBzIGFmdGVyIGV4dHJhY3RpbmcgcGFydHMgb2YgdGhlIHJldHVybmVkIGNoYXJhY3RlciB2YWx1ZS4gU2VlIExlc3NvbiBbNi4xMTIgQmFzaWNzIG9mIFRleHQgJiBTdHJpbmcgUHJvY2Vzc2luZyBpbiBSXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTMwMy14cGF0aC1pbi1yL2wtNi0zMDMuaHRtbCkuCgojIyBTaWduaWZpY2FuY2Ugb2YgWFBhdGgKCldpdGhpbiB0aGUgZnJhbWV3b3JrIG9mIHRoaXMgdHV0b3JpYWwsIHlvdSBtYXkgaGF2ZSBvYnNlcnZlZCBhIHJlY3VycmVudCBlbXBoYXNpcyBvbiB0aGUgdGVybSAic2VsZWN0IiB3aGVuIGVsdWNpZGF0aW5nIHRoZSBmdW5jdGlvbmluZyBvZiBYUGF0aCBleHByZXNzaW9ucyBpbiBlZmZlY3RpdmVseSBwaW5wb2ludGluZyBzZWN0aW9ucyBvZiBhbiBYTUwgZG9jdW1lbnQuIE5vbmV0aGVsZXNzLCBpdCBpcyBjcnVjaWFsIHRvIHVuZGVyc2NvcmUgdGhhdCBYUGF0aCBkb2VzIG5vdCBvcGVyYXRlIGluIGlzb2xhdGlvbjsgaXQgaXMgaW5oZXJlbnRseSBpbnRlcnR3aW5lZCB3aXRoIG90aGVyIGNvbXBsZW1lbnRhcnkgdGVjaG5vbG9naWVzIHN1Y2ggYXMgWFNMVCwgWFBvaW50ZXIsIG9yIFhMaW5rLiBUaGUgWFBhdGggaWxsdXN0cmF0aW9ucyBwcmVzZW50ZWQgaW4gdGhlIHByZWNlZGluZyBzZWN0aW9ucyBvZiB0aGlzIHR1dG9yaWFsIG5lY2Vzc2l0YXRlIHRoZWlyIGludGVncmF0aW9uIHdpdGggYWRkaXRpb25hbCBjb2RlIHRvIG1hbmlmZXN0IHRoZWlyIGZ1bGwgdXRpbGl0eS4KCkZvciBpbnN0YW5jZSwgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBjb2RlIHNuaXBwZXQsIHdoaWNoIGRlbW9uc3RyYXRlcyB0aGUgdXRpbGl6YXRpb24gb2YgYW4gWFBhdGggZXhwcmVzc2lvbiBpbiBhbiBYU0xUIHN0eWxlc2hlZXQ6CgpgYGAgeG1sCjx4c2w6dmFsdWUtb2Ygc2VsZWN0PSIqL3Nlc3Npb25bQHR5cGU9J3J1bm5pbmcnXSIgLz4KYGBgCgpJbiB0aGlzIGNvZGUgZXhjZXJwdCwgdGhlIFhQYXRoIGV4cHJlc3Npb24gaXMgZW1iZWRkZWQgd2l0aGluIHRoZSBgc2VsZWN0YCBhdHRyaWJ1dGUgb2YgdGhlIGB4c2w6dmFsdWUtb2ZgIGVsZW1lbnQuIFRoaXMgZWxlbWVudCBhc3N1bWVzIHRoZSByZXNwb25zaWJpbGl0eSBvZiBleHRyYWN0aW5nIGNvbnRlbnQgZnJvbSBhIHNvdXJjZSBYTUwgZG9jdW1lbnQgYW5kIGluY29ycG9yYXRpbmcgaXQgaW50byBhbiBvdXRwdXQgZG9jdW1lbnQgZHVyaW5nIHRoZSB0cmFuc2Zvcm1hdGlvbiBwcm9jZXNzIG9mIHRoZSBzb3VyY2UgZG9jdW1lbnQuIEZvciBjb21wcmVoZW5zaXZlIGluc2lnaHRzIGludG8gWFNMVCBzdHlsZXNoZWV0cyBhbmQgdGhlaXIgb3BlcmF0aW9uYWwgZHluYW1pY3MsIEkgcmVjb21tZW5kIHJldmlzaXRpbmcgVHV0b3JpYWxzIDEyIGFuZCAxMy4KClRoZSBwaXZvdGFsIHRha2Vhd2F5IGhlcmUgaXMgdGhhdCB0aGUgWFBhdGggZXhwcmVzc2lvbiBkZXJpdmVzIGl0cyBzaWduaWZpY2FuY2UgYW5kIGZ1bmN0aW9uYWxpdHkgZnJvbSBpdHMgaW50ZWdyYXRpb24gd2l0aGluIHRoZSBYU0xUIGNvbnRleHQuIFhQYXRoIHBsYXlzIGFuIGludGVncmFsIHJvbGUgaW4gdGhlIHJlYWxtIG9mIFhTTFQsIGFzIHlvdSBtYXkgcmVjYWxsIGZyb20gdGhlIG1hdGVyaWFsIGNvdmVyZWQgaW4gVHV0b3JpYWwgMTMuCgpJbiBhIG1hbm5lciBhbmFsb2dvdXMgdG8gaXRzIGZ1bmN0aW9uIHdpdGhpbiBYU0xULCBYUGF0aCBzZXJ2ZXMgYXMgdGhlIGZvdW5kYXRpb25hbCBhZGRyZXNzaW5nIG1lY2hhbmlzbSBpbiBYUG9pbnRlci4gWFBvaW50ZXIncyBwcmltYXJ5IG9iamVjdGl2ZSBpcyB0byBwaW5wb2ludCBzcGVjaWZpYyBzZWN0aW9ucyB3aXRoaW4gWE1MIGRvY3VtZW50cywgYW5kIGl0IGFzc3VtZXMgYSBjZW50cmFsIHJvbGUgaW4gdGhlIGJyb2FkZXIgY29udGV4dCBvZiBYTGluaywgYSBjb25jZXB0IHdlIHdpbGwgZGVsdmUgaW50byBzaG9ydGx5LiBYUG9pbnRlciBoYXJuZXNzZXMgWFBhdGgncyBjYXBhYmlsaXRpZXMgdG8gZmFjaWxpdGF0ZSBuYXZpZ2F0aW9uIHRocm91Z2ggdGhlIGhpZXJhcmNoeSBvZiBub2RlcyBjb21wcmlzaW5nIGFuIFhNTCBkb2N1bWVudC4gVGhpcyBtYXkgc291bmQgcmVtaW5pc2NlbnQgb2YgWFBhdGgncyByb2xlIHRodXMgZmFyLiBIb3dldmVyLCBYUG9pbnRlciBlbGV2YXRlcyB0aGUgY2FwYWJpbGl0aWVzIG9mIFhQYXRoIGJ5IGludHJvZHVjaW5nIGEgc3ludGF4IGZvciBmcmFnbWVudCBpZGVudGlmaWVycywgd2hpY2ggYXJlIHN1YnNlcXVlbnRseSBlbXBsb3llZCB0byBwcmVjaXNlbHkgc3BlY2lmeSBzZWdtZW50cyB3aXRoaW4gZG9jdW1lbnRzLiBUaHJvdWdoIHRoaXMgaW5ub3ZhdGlvbiwgWFBvaW50ZXIgZW1wb3dlcnMgdXNlcnMgd2l0aCBhbiBleGNlcHRpb25hbCBkZWdyZWUgb2YgY29udHJvbCBvdmVyIHRoZSBhZGRyZXNzaW5nIGFuZCBtYW5pcHVsYXRpb24gb2YgWE1MIGRvY3VtZW50cy4KCiMjIE1vcmUgRXhhbXBsZXMKClRoZSBleGFtcGxlcyBiZWxvdyByZXRyaWV2ZSBkYXRhIGZyb20gdGhlIFhNTCBkb2N1bWVudCBbKlRlYW1Sb3N0ZXJzLnhtbCpdKFRlYW1Sb3N0ZXJzLnhtbCkuIFRha2UgYSBtb21lbnQgdG8gb3BlbiB0aGF0IGZpbGUgYW5kIGluc3BlY3QgaXQuIFlvdSBjYW4gZG93bmxvYWQgdGhlIGZpbGVzIGluIHRoZSBzZWN0aW9uIFtGaWxlcyAmIHJlc291cmNlXSgjZmFyKSBvciBmcm9tIHRoZSBsaW5rLgoKYGBge3IgY29tbWVudD0iIn0KeG1sRmlsZSA8LSAiVGVhbVJvc3RlcnMueG1sIgoKeG1sT2JqIDwtIHhtbFBhcnNlKHhtbEZpbGUpCmBgYAoKIyMjIyBGaW5kIHRoZSBnb2FscyBzY29yZWQgYnkgIk1jQXZveSIKClRoaXMgcGF0aCB1c2VzIGEgZnVsbCBhYnNvbHV0ZSBwYXRoIGV4cHJlc3Npb24gc3RhcnRpbmcgYXQgdGhlIHJvb3QgKC8pIGFuZCBzcGVjaWZ5aW5nIGEgZmlsdGVyIHRocm91Z2ggYSBwcmVkaWNhdGUgZXhwcmVzc2lvbi4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIi9yb3N0ZXJzL3RlYW0vcGxheWVyW2xhc3RuYW1lPSdNY0F2b3knXS9wb2ludHMvZ29hbHMiCnJlc3VsdCA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgsIHhtbFZhbHVlKQoKY2F0KHJlc3VsdCkKYGBgCgpBbHRlcm5hdGl2ZWx5LCByYXRoZXIgdGhhbiBwYXNzaW5nIHRoZSBmdW5jdGlvbiA8Y29kZT54bWxWYWx1ZSgpPC9jb2RlPiBhcyBhIHBhcmFtZXRlciB0byA8Y29kZT54cGF0aFNBcHBseSgpPC9jb2RlPiwgaXQgY2FuIGFsc28gYmUgY2FsbGVkIHNlcGFyYXRlbHkgb24gdGhlIHJldHVybmVkIGZ1bGwgbm9kZS4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIi9yb3N0ZXJzL3RlYW0vcGxheWVyW2xhc3RuYW1lPSdNY0F2b3knXS9wb2ludHMvZ29hbHMiCnJlc3VsdCA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgpCnByaW50KHJlc3VsdCkKCnZhbHVlIDwtIHhtbFZhbHVlKHJlc3VsdCkKY2F0KHZhbHVlKQpgYGAKCiMjIyMgRmluZCB0aGUgZ29hbHMgc2NvcmVkIGJ5IGFsbCBwbGF5ZXJzCgpgYGB7ciBjb21tZW50PSIifQp4cGF0aEV4IDwtICIvcm9zdGVycy90ZWFtL3BsYXllci9wb2ludHMvZ29hbHMiCnJlc3VsdCA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgsIHhtbFZhbHVlKQoKY2F0KHJlc3VsdCkKYGBgCgojIyMjIEhvdyBtYW55IGdvYWxzIHdlcmUgc2NvcmVkIGluIHRvdGFsIGJ5IGFsbCBwbGF5ZXJzPwoKVGhlIGZpcnN0IHNvbHV0aW9uIHVzZXMgUiB0byBjYWxjdWxhdGUgdGhlIHN1bS4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIi9yb3N0ZXJzL3RlYW0vcGxheWVyL3BvaW50cy9nb2FscyIKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpyZXN1bHQgPC0gYXMubnVtZXJpYyhyZXN1bHQpCgpjYXQoc3VtKHJlc3VsdCkpCmBgYAoKVGhlIHNlY29uZCBzb2x1dGlvbiB1c2VzIHRoZSA8Y29kZT5zdW08L2NvZGU+IGFnZ3JlZ2F0aW9uIGZ1bmN0aW9uIG9mIFhQYXRoLgoKYGBge3IgY29tbWVudD0iIn0KeHBhdGhFeCA8LSAic3VtKC9yb3N0ZXJzL3RlYW0vcGxheWVyL3BvaW50cy9nb2FscykiCnJlc3VsdCA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgsIHhtbFZhbHVlKQoKY2F0KHJlc3VsdCkKYGBgCgojIyMjIFdoYXQgYXJlIHRoZSBsYXN0IG5hbWVzIG9mIGV2ZXJ5b25lPwoKVGhpcyBwYXRoIGV4cHJlc3Npb24gaXMgbm90IGEgZ2xvYmFsIHBhdGguIEl0IHN0YXJ0cyB3aXRoIC8vIHNvIGl0IG1hdGNoZXMgYW55ICpsYXN0bmFtZSogZWxlbWVudCByZWdhcmRsZXNzIG9mIHdoZXJlIGl0IGlzIGluIHRoZSB0cmVlLgoKYGBge3IgY29tbWVudD0iIn0KeHBhdGhFeCA8LSAiLy9sYXN0bmFtZSIKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCiMjIyMgV2hhdCBhcmUgdGhlIGxhc3QgbmFtZXMgb2YgYWxsIHBsYXllcnM/CgpVbmxpa2UgdGhlIHByZXZpb3VzIGV4YW1wbGUsIHRoaXMgcGF0aCBpcyBhbHNvIG5vdCBhIGdsb2JhbCBwYXRoIGJ1dCBpdCBpcyBtb3JlIHJlc3RyaWN0aXZlIGFzIGl0IG9ubHkgbWF0Y2hlcyAqbGFzdG5hbWUqIGVsZW1lbnRzIHRoYXQgYXJlIGRpcmVjdCBjaGlsZCBub2RlcyBvZiBhICpwbGF5ZXIqIG5vZGUuCgpgYGB7ciBjb21tZW50PSIifQp4cGF0aEV4IDwtICIvL3BsYXllci9sYXN0bmFtZSIKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCiMjIyMgV2hhdCBhcmUgdGhlIGZpcnN0IGFuZCBsYXN0IG5hbWVzIG9mIGFsbCBwbGF5ZXJzPwoKVGhlIFhQYXRoIGJlbG93IGlzIGEgY29ycmVjdCBYUGF0aCAyLjAgZXhwcmVzc2lvbiwgYWx0aG91Z2ggaXQgZG9lcyBub3QgZXZhbHVhdGUgaW4gUiBiZWNhdXNlIHRoZSAqKlhNTCoqIHBhY2thZ2Ugb25seSBzdXBwb3J0cyBYUGF0aCAxLjAuCgpgYGB7PWh0bWx9CjxwcmU+Ci8vcGxheWVyL2NvbmNhdChmaXJzdG5hbWUsJywnLGxhc3RuYW1lKQo8L3ByZT4KYGBgCkFuIFIgc29sdXRpb24gd291bGQgYmUgdG8gcmV0cmlldmUgdGhlIGVsZW1lbnRzIGluZGl2aWR1YWxseSBhbmQgdGhlbiBjb25jYXRlbmF0ZSB0aGVtIGluIFIuCgpgYGB7ciBldmFsPUZ9CnhwYXRoRXggPC0gIi8vcGxheWVyL2ZpcnN0bmFtZSIKci5mbiA8LSB4cGF0aFNBcHBseSh4bWxPYmosIHhwYXRoRXgsIHhtbFZhbHVlKQoKeHBhdGhFeCA8LSAiLy9wbGF5ZXIvbGFzdG5hbWUiCnIubG4gPC0geHBhdGhTQXBwbHkoeG1sT2JqLCB4cGF0aEV4LCB4bWxWYWx1ZSkKCnJlc3VsdCA8LSBwYXN0ZTAoci5sbiwgJywgJywgci5mbikKYGBgCgojIyMjIFdoYXQgYXJlIHRoZSBuYW1lcyBvZiB0aGUgcGxheWVycyB3aG8gZGlkIG5vdCBzY29yZSBhIGdvYWw/CgpGb3IgdGhpcyBxdWVyeSB0d28gcGF0aHMgYXJlIHByZXNlbnRlZDogdGhlIGZpcnN0IHBhdGggaXMgYW4gYWJzb2x1dGUgcGF0aCB3aGlsZSB0aGUgc2Vjb25kIHBhdGggaXMgbm90LiBGb3IgdGhpcyBYTUwgZG9jdW1lbnQgdGhleSBwcm9kdWNlIHRoZSBzYW1lIHJlc3VsdC4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIi9yb3N0ZXJzL3RlYW0vcGxheWVyW3BvaW50cy9nb2FscyA9IDBdL2xhc3RuYW1lIgojIHNhbWUgcmVzdWx0IGFzCnhwYXRoRXggPC0gIi8vcGxheWVyW3BvaW50cy9nb2FscyA9IDBdL2xhc3RuYW1lIgoKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCiMjIyMgSG93IG1hbnkgZ29hbHMgZGlkIHBsYXllciA2MyBzY29yZT8KCk5vdGUgdGhlIHByZWRpY2F0ZSBleHByZXNzaW9uIHNwZWNpZnlpbmcgdGhlIHZhbHVlIG9mIHRoZSAqbnVtKiBhdHRyaWJ1dGUgb2YgKnBsYXllciouIFRoZSBYTUwgZm9yIGEgcGxheWVyIG5vZGUgbG9va3MgbGlrZSB0aGlzOgoKYGBge3htbH0KPHBsYXllciBudW09IjYzIj4KICAuLi4KPC9wbGF5ZXI+CjxwbGF5ZXIgbnVtPSIzNyI+CiAgLi4uCjwvcGxheWVyPgpgYGAKCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIi9yb3N0ZXJzL3RlYW0vcGxheWVyW0BudW0gPSAnNjMnXS9wb2ludHMvZ29hbHMiCgpyZXN1bHQgPC0geHBhdGhTQXBwbHkoeG1sT2JqLCB4cGF0aEV4LCB4bWxWYWx1ZSkKCmNhdChyZXN1bHQpCmBgYAoKIyMjIyBIb3cgbWFueSBwb2ludHMgZGlkIHBsYXllciA2MyBzY29yZT8KCk5vdGUgdGhlIHVzZSBvZiB0aGUgT1Igb3BlcmF0b3IgXHwgdG8gZmluZCBlbGVtZW50cyB0aGF0IG1hdGNoIGVpdGhlciBwYXRoIGV4cHJlc3Npb24sIHdoaWNoIG1lYW5zIHRoYXQgd2UgcmVjZWl2ZSBib3RoLiBBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCBoYXZlIGV4ZWN1dGVkIGVhY2ggcGF0aCBleHByZXNzaW9uIGluZGl2aWR1YWxseSBhbmQgY29uY2F0ZW5hdGVkIHRoZSByZXN1bHQgaW4gUi4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gInN1bSgvcm9zdGVycy90ZWFtL3BsYXllcltAbnVtPSc2MyddL3BvaW50cy9nb2FscyB8IC9yb3N0ZXJzL3RlYW0vcGxheWVyW0BudW09JzYzJ10vcG9pbnRzL2Fzc2lzdHMpIgoKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCiMjIyMgSG93IG1hbnkgcGxheWVycyBzY29yZWQgbW9yZSB0aGFuIDIwIGdvYWxzPwoKT25jZSBhZ2FpbiwgYSBwcmVkaWNhdGUgZXhwcmVzc2lvbiBidXQgdGhpcyB0aW1lIHRoZSByZXN1bHQgaXMgcHJvdmlkZWQgYXMgYW4gYXJndW1lbnQgdG8gdGhlICpjb3VudCogZnVuY3Rpb24gb2YgWFBhdGgsIHdoaWNoLCBsaWtlIGl0cyBjb3VudGVycGFydCBpbiBTUUwsIHdpbGwgcmV0dXJuIHRoZSBudW1iZXIgb2Ygbm9kZXMgaW4gdGhlIHJlc3VsdC4KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gImNvdW50KC9yb3N0ZXJzL3RlYW0vcGxheWVyW3BvaW50cy9nb2FscyA+IDIwXS9wb2ludHMvZ29hbHMpIgoKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCiMjIyMgV2hhdCBpcyB0aGUgbG93ZXN0IG51bWJlciBvZiBnb2FscyBhbnkgcGxheWVyIHNjb3JlZD8KCmBgYHtyIGNvbW1lbnQ9IiJ9CnhwYXRoRXggPC0gIm1pbigvcm9zdGVycy90ZWFtL3BsYXllci9wb2ludHMvZ29hbHMpIgoKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCiMjIyMgSG93IG1hbnkgZ29hbHMgZGlkIExhdXJlbnQgRCdPciBzY29yZT8KClRoaXMgcXVlcnkgaXMgYSBiaXQgInRyaWNreSIgYXMgdGhlIGxhc3QgbmFtZSBpbmNsdWRlcyBhbiBhcG9zdHJvcGhlIHdoaWNoIHdlIG9mdGVuIHVzZSBpbiBYUGF0aCBhcyBhIHN0cmluZyBkZWxpbWl0ZXIuIE9mIGNvdXJzZSwgd2UgY291bGQgdXNlIHRoZSBwbGF5ZXIncyBudW1iZXIgYnV0IHdlIHdhbnQgdG8gdXNlIHRoZSBuYW1lIGFzIHRoZSBudW1iZXIgbWF5IG5vdCBiZSBmaXhlZCBmb3Igc29tZSB0eXBlcyBvZiBwbGF5ZXJzLgoKT25lIGFwcHJvYWNoIGlzIHRoZSByZXZlcnNlIHRoZSB3YXkgd2UgdXNlIHN0cmluZyBkZWxpbWl0ZXJzLiBOb3RpY2UgaG93IHRoZSBYUGF0aCBleHByZXNzaW9uIGJlbG93IHVzZXMgc2luZ2xlIHF1b3RlcyAoJykgZm9yIHRoZSBYUGF0aCBleHByZXNzaW9uIGluIFIgYW5kIHRoZSBkb3VibGUgcXVvdGUgKCIpIGZvciB0aGUgdmFsdWUgaW4gdGhlIFhQYXRoIGNvbmRpdGlvbi4gRmluYWxseSwgbm90aWNlIGhvdyB0aGUnIGlzIGVzY2FwZWQgYnkgYWRkaW5nIGEgYmFja3NsYXNoIGJlZm9yZSBpdC4gQnkgd3JpdGluZyBcXCcsIFIgYW5kIHRoZSBYUGF0aCBleHByZXNzaW9uIGRvIG5vdCBpbnRlcnByZXQgdGhlIGNoYXJhY3RlciBidXQgcGFzcyBpdCBvbiB1bmludGVycHJldGVkLgoKYGBge3IgY29tbWVudD0iIn0KeHBhdGhFeCA8LSAnL3Jvc3RlcnMvdGVhbS9wbGF5ZXJbbGFzdG5hbWUgPSAiRFwnT3IiXS9wb2ludHMvZ29hbHMnCgpyZXN1bHQgPC0geHBhdGhTQXBwbHkoeG1sT2JqLCB4cGF0aEV4LCB4bWxWYWx1ZSkKCmNhdChyZXN1bHQpCmBgYAoKQW4gYWx0ZXJuYXRpdmUgd291bGQgYmUgdG8gYnVpbGQgdGhlIHN0cmluZyBmcm9tIHRoZSBwYXJ0cyBhbmQgdXNlICIgYW5kICcgcXVvdGVzIGp1ZGljaW91c2x5IGFuZCB0aGVuIGVtYmVkIHRoZSAnIHdpdGhpbiBhIHN0cmluZyBzdXJyb3VuZGVkIGJ5ICIgYW5kIHRoZSAiIHF1b3RlcyB3aXRoaW4gYSBzdHJpbmcgc3Vycm91bmRlZCBieSAnLgoKYGBge3IgY29tbWVudD0iIn0KeHBhdGhFeCA8LSBwYXN0ZTAoJy9yb3N0ZXJzL3RlYW0vcGxheWVyW2xhc3RuYW1lID0gIkQnLAogICAgICAgICAgICAgICAgICAiJ09yIiwKICAgICAgICAgICAgICAgICAgJyJdL3BvaW50cy9nb2FscycpCgpyZXN1bHQgPC0geHBhdGhTQXBwbHkoeG1sT2JqLCB4cGF0aEV4LCB4bWxWYWx1ZSkKCmNhdChyZXN1bHQpCmBgYAoKV2UgY291bGQgaGF2ZSBhbHNvIHBsYWNlZCB0aGUgdmFsdWUgaW50byBhIHZhcmlhYmxlIGFuZCB0aGVuIHBhc3RlZCB0aGF0IGludG8gdGhlIFhQYXRoIGV4cHJlc3Npb247IHRoaXMgbWF5IGJlIGEgYml0IHNpbXBsZXIgbG9va2luZyBidXQgaGFzIHRoZSBzYW1lIGVmZmVjdC4KCmBgYHtyIGNvbW1lbnQ9IiJ9Cmxhc3RuYW1lIDwtICJEJ09yIgp4cGF0aEV4IDwtIHBhc3RlMCgnL3Jvc3RlcnMvdGVhbS9wbGF5ZXJbbGFzdG5hbWUgPSAiJywKICAgICAgICAgICAgICAgICAgbGFzdG5hbWUsCiAgICAgICAgICAgICAgICAgICciXS9wb2ludHMvZ29hbHMnKQoKcmVzdWx0IDwtIHhwYXRoU0FwcGx5KHhtbE9iaiwgeHBhdGhFeCwgeG1sVmFsdWUpCgpjYXQocmVzdWx0KQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUHJhY3RpY2UgUXVlcmllcwoKLSAgIFdoYXQgYXJlIHRoZSBsYXN0IG5hbWVzIG9mIGFsbCBnb2FsaWVzPwotICAgV2hhdCBpcyB0aGUgdG90YWwgbnVtYmVyIG9mIHBvaW50cyBzY29yZWQgYnkgdGhlIGVudGlyZSB0ZWFtPwotICAgV2hhdCBpcyB0aGUgYXZlcmFnZSBudW1iZXIgb2YgZ29hbHMgZm9yIGFsbCBwbGF5ZXJzIHdobyBhcmUgbm90IGdvYWxpZXM/Ci0gICBXaGljaCBwbGF5ZXIgaGFzIHRoZSBoaWdoZXN0IHNhbGFyeT8KLSAgIFdoaWNoIHBsYXllcnMgaGF2ZSBhbiBhYm92ZSBhdmVyYWdlIHNhbGFyeT8KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgVHV0b3JpYWwKClRoZSB2aWRlbyB0dXRvcmlhbCBiZWxvdyB1c2VzIGRpZmZlcmVudCBYTUwgZmlsZXMgdG8gcmV2aXNpdCB0aGUgY29uY2VwdHMgYW5kIGRlbW9uc3RyYXRpbmcgdGhlbSBieSB3YWxraW5nIHRocm91Z2ggUiBjb2RlLiBGb2xsb3cgYWxvbmcgYW5kIHR5cGUgaW4gdGhlIHN0YXRlbWVudHMgeW91cnNlbGYuIEEgbGluayB0byB0aGUgWE1MIGZpbGUgdXNlZCBpbiB0aGUgdHV0b3JpYWwgaXMgYmVsb3c6CgotICAgW1BPRGF0YVhNTC54bWxdKFBPRGF0YVhNTC54bWwpCgpCZWZvcmUgeW91IHN0YXJ0LCByZW1lbWJlciB0byBpbnN0YWxsIHRoZSBwYWNrYWdlICoqWE1MKiogaWYgeW91IGhhdmVuJ3QgYWxyZWFkeSBkb25lIHNvLgoKYGBgez1odG1sfQo8aWZyYW1lIHN0eWxlPSJib3JkZXI6IDFweCBzb2xpZCAjNDY0NjQ2OyIgc3JjPSJodHRwczovL25vcnRoZWFzdGVybi5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9FbWJlZC5hc3B4P2lkPWVlNmVhMmU5LWMwYTItNDFjMi1hNDZlLWFjZGIwMTQwNjQxYSZhbXA7YXV0b3BsYXk9ZmFsc2UmYW1wO29mZmVydmlld2VyPXRydWUmYW1wO3Nob3d0aXRsZT1mYWxzZSZhbXA7c2hvd2JyYW5kPWZhbHNlJmFtcDtzdGFydD0wJmFtcDtpbnRlcmFjdGl2aXR5PWFsbCIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyNzAiIGFsbG93ZnVsbHNjcmVlbj0iYWxsb3dmdWxsc2NyZWVuIiBhbGxvdz0iYXV0b3BsYXkiIGRhdGEtZXh0ZXJuYWw9IjEiPjwvaWZyYW1lPgpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmlsZXMgJiBSZXNvdXJjZXMgeyNmYXJ9CgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBTZWUgQWxzbwoKLSAgIFs2LjMyMyBMb2FkIFNpbXBsZSBYTUwgaW50byBEYXRhZnJhbWUgaW4gUiB1c2luZyB4bWxUb0RhdGFGcmFtZSgpXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTMyMy1sb2FkLXhtbC14bWxUb0RhdGFGcmFtZS9sLTYtMzIzLmh0bWwpCi0gICBbODAuMTEyIFByaW1lciBvbiBYUGF0aCBGdW5jdGlvbnNdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvODAueG1sL2wtODAtMTEyLXhwYXRoLWZ1bmN0aW9ucy9sLTgwLTExMi5odG1sKQotICAgWzYuMzI0IFRyYXZlcnNlIGFuZCBQYXJzZSBYTUwgRE9NIGluIFJdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMDYuci9sLTYtMzI0LXBhcnNlLXhtbC1kb20vbC02LTMyNC5odG1sKQotICAgWzYuMTEyIEJhc2ljcyBvZiBUZXh0ICYgU3RyaW5nIFByb2Nlc3NpbmcgaW4gUl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0xMTItdGV4dC1wcm9jL2wtNi0xMTIuaHRtbCkKCiMjIFJlZmVyZW5jZXMKCi0gICBbUG9ydGllciwgQi4gKDIwMDQpLiBYUGF0aCBUdXRvcmlhbC4gSUJNIERldmVsb3BlcldvcmtzLl0oSUJNJTIwWFBhdGglMjBUdXRvcmlhbC5wZGYpCgotICAgW0xpc3Qgb2YgWFBhdGggRnVuY3Rpb25zXShodHRwczovL3d3dy5icmFpbmJlbGwuY29tL3R1dG9yaWFscy9YTUwvVXNpbmdfWFBhdGhfRnVuY3Rpb25zLmh0bSM6fjp0ZXh0PVRoZSUyMFhQYXRoJTIwc3RyaW5nJTIwZnVuY3Rpb25zJTIwYXJlJTIwdXNlZCUyMHRvJTIwbWFuaXB1bGF0ZSxmdW5jdGlvbnMlMjBpbiUyMFhQYXRoJTNBJTIwY29uY2F0JTI4JTI5JTIwQ29uY2F0ZW5hdGUlMjB0d28lMjBzdHJpbmdzJTIwdG9nZXRoZXIuKQoKIyMgUmVzb3VyY2VzCgotICAgPGh0dHA6Ly94cGF0aGVyLmNvbS8+CgojIyBFcnJhdGEKCk5vbmUgY29sbGVjdGVkIHlldC4gTGV0IHVzIGtub3cuCgpbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==