Introduction
XSL, which stands for Extensible Stylesheet Language, is a family of languages used for transforming and rendering XML documents. XSL is primarily composed of two main components: XSLT (XSL Transformations) and XSL-FO (XSL Formatting Objects).
- XSLT (XSL Transformations):
- XSLT is a language used to transform XML documents into other formats, such as HTML, plain text, CSV, SQL, or another XML structure.
- It allows you to define rules and templates for how the input XML should be processed and transformed into the desired output format.
- XSLT uses a declarative approach, where you specify what should happen to the input data rather than writing procedural code.
- XSLT is often used for tasks like format conversion, generating reports, and extracting specific information from XML documents.
- XSL-FO (XSL Formatting Objects):
- XSL-FO is a language used for specifying the layout and formatting of XML content when it needs to be presented or printed, such as generating PDF documents.
- It allows you to define rules for pagination, fonts, page headers, footers, and other formatting aspects of a document.
- XSL-FO is especially useful for generating well-structured, print-ready documents from XML data.
XSL is essential for several reasons:
Data Transformation: XML is often used to represent structured data, and XSLT provides a way to transform this data into different formats, making it more accessible to various applications and systems. For example, you can convert XML data into XML for schema conversion, HTML for web display, JSON for API responses, or CSV for data export.
Data Extraction: XSLT allows you to extract specific information from an XML document by defining templates that match the desired elements or attributes. This is particularly useful when dealing with large XML datasets.
Document Presentation: XSL-FO enables the generation of professionally formatted documents, such as PDFs, from XML content. This is valuable for creating reports, invoices, manuals, and other documents with specific layout and styling requirements.
Separation of Content and Presentation: XSL helps maintain a clear separation between the content (XML data) and its presentation (XSLT and XSL-FO). This separation is crucial for reusability and maintainability, as changes to the presentation can be made independently of the underlying data.
Cross-Platform Compatibility: XML and XSLT are platform-agnostic and can be used on various operating systems and programming languages. This makes them suitable for interoperability in heterogeneous environments.
Standards Compliance: XSL follows W3C (World Wide Web Consortium) standards, ensuring that your XML processing and transformation tasks adhere to established best practices.
In short, XSL, comprising XSLT and XSL-FO, is useful for working with XML data, enabling data transformation, extraction, and document formatting. It helps bridge the gap between structured data and its presentation in different formats, making it a fundamental technology for a wide range of applications in web development, data integration, and document generation.
This lesson explains how to build an XSL to transform XML from one XML format to another.
Applying an XSL
An XSL is a text file containing the transformation rules (expressed as XML). The XSL is fed to a transformation engine (see below) along with the source XML. The transformation engine produces an output file. The flow is shown in the diagram below.
XSL in R
To transform XML documents with XSL in R, you will need to install and then load the package xslt.
To perform the transformation, load the source XML, load the XSL, apply the transform, and then save the result.
xmlFn <- "XML/TeamRosters.xml"
xslFn <- "XSL/Rosters2StatsXSL.xsl"
txnFn <- "XML/BruinsStats.xml"
# Step 1: load the source XML
xmlDoc <- read_xml(xmlFn)
# Step 2: load the XSL transformation
xslStyle <- read_xml(xslFn)
# Step 3: apply the XSL to the XML
transformedXML <- xml_xslt(xmlDoc, xslStyle)
# Step 4: write the resulting XML to a file
status <- write_xml(transformedXML, file = txnFn)
R / R Studio Programming Hint: Occasionally during XSL processing an error occurs and the XSLT processor goes into an infinite loop. To stop the XSLT, you need to restart R. In R Studio (Posit), click on Session/Restart R…
Building XSL Transformations
Example XML
The source XML used in the examples contains information about players on team rosters and might be imported into a team analytics system or to play in a fantasy sports league. We want to extract the player’s last name, their goals and assists. We want to exclude goalies. The result should be a XML that can be read into Excel (using its XML import wizard) or directly into a data frame, so it should have a simple two-level structure. A portion of the XML (TeamRosters.xml) is reproduced below:
<?xml version="1.0" encoding="UTF-8"?>
<rosters season="2021">
<team name="Boston Bruins" division="MassMutual East">
<date>2021-02-19</date>
<team-info>
<city>Boston</city>
...
</team-info>
<season-info>
<gp>15</gp>
<pts w="10" l="3" ot="2">22</pts>
</season-info>
<player num="63">
<firstname>Brad</firstname>
<lastname>Marchand</lastname>
<position>Forward</position>
<assistantcaptain/>
<points>
<goals>9</goals>
<assists>10</assists>
</points>
</player>
…
<player num="73">
<firstname>Charlie</firstname>
<lastname>McAvoy</lastname>
...
</player>
<player num="40">
<firstname>Tuuka</firstname>
<lastname>Rask</lastname>
<position>Goalie</position>
...
<stats>
<GA>23</GA>
<SV>0.906</SV>
</stats>
</player>
</team>
</rosters>
The resulting XML should look like this (sorted by player last name and excluding goalies):
<?xml version="1.0" encoding="UTF-8"?>
<roster>
<row>
<name number="37">Bergeron</name>
<goals>7</goals>
<assists>11</assists>
</row>
<row>
<name number="63">Marchand</name>
<goals>9</goals>
<assists>10</assists>
</row>
...
</roster>
Define Preamble for XSL
Since an XSL style sheet is an XML document, it begins with the XML declaration:
<?xml version="1.0" encoding="UTF-8"?>
The next element, <xsl:transform>
defines that the XML document is an XSLT style sheet document.
The element <xsl:stylesheet>
is synonymous with <xsl:transform>
.
The xmlns:xsl attribute defines a unique namespace and may point to any URL, but most commonly the one shown. The XSL fragment is standard, so simply copy this code.
Key Rule Operators
Key <xsl:operators>
:
<xsl:template>
<xsl:value-of>
<xsl:for-each>
<xsl:sort>
<xsl:if>
<xsl:choose>
<xsl:key>
<xsl:message>
<xsl:apply-template>
<xsl:import>
<xsl:output>
<xsl:text>
<xsl:attribute>
<xsl:template>
An XSL style sheet consists of one or more sets of rules called templates. Each <xsl:template>
element defines a section of the target file. The value of the match attribute is an XPath expression. The result document is built top to bottom. Any text that is not <xsl: …>
is part of the target document (output of XSLT).
Example XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<roster>
Boston Bruins
</roster>
</xsl:template>
</xsl:stylesheet>
Result XML
<?xml version="1.0" encoding="UTF-8"?>
<roster>
Boston Bruins
</roster>
Full Syntax: <xsl:template match="<xpath-expr>">
<xsl:template
name = QName
match = Pattern
priority = number
mode = QName >
</xsl:template>
- name: Name of the element to which template is to be applied.
- match: XPath pattern which matches the element(s) on which template is to be applied.
- priority: Priority of a template in case of conflicting templates.
- mode: Allows element to be processed multiple times to produce a different result each time.
<xsl:for-each>
<xsl:for-each select="<xpath-expr>">
processes a set of XML elements that match the XPath expression. It loops (i.e., iterates) through each element in the matched set of elements one by one: it is an iterator. The context of the XPath expression is set by any parent <xsl:operation>
.
<xsl:for-each
select = XPath-Expression>
process each node
</xsl:for-each>
In the example fragment below, the XPath expression within “xsl:for-each” binds to each <player> element under <team> and the rule is applied to each of them in turn.
Example XSL
...
<xsl:template match="//team[@name='Boston Bruins']">
<roster>
<xsl:for-each select="player">
<row>
<cell>Brad Marchand</cell>
<cell>9</cell>
<cell>9</cell>
</row>
</xsl:for-each>
</roster>
</xsl:template>
...
The resulting XML would be as follows:
<?xml version="1.0" encoding="UTF-8"?>
<roster>
<row>
<cell>Brad Marchand</cell>
<cell>9</cell>
<cell>9</cell>
</row>
<row>
<cell>Brad Marchand</cell>
<cell>9</cell>
<cell>9</cell>
</row>
...
<xsl:value-of>
<xsl:value-of select="xpath-expr">
extracts the value of the selected node determined by the XPath expression, as text. The context of the XPath expression is set by any parent <xsl:operation>
. The value of a node or element is everything between the element’s opening and closing tags, e.g., the value of “goals” for <goals>9</goals>
is 9 and the value of “stats” for <stats><goals>9</goals></stats>
is “<goals>9</goals>”.
The full syntax of <xsl:value-of>
is:
<xsl:value-of
select = Expression
disable-output-escaping = "yes" | "no"/>
Example XSL
<xsl:template match="//team[@name='Boston Bruins']">
<roster>
<xsl:for-each select="player">
<row>
<name>
<xsl:value-of select="lastname" />
</name>
<goals>
<xsl:value-of select="points/goals" />
</goals>
<assists>
<xsl:value-of select="points/assists" />
</assists>
</row>
</xsl:for-each>
</roster>
</xsl:template>
...
Result XML
<?xml version="1.0" encoding="UTF-8"?>
<roster>
<row>
<name>Brad Marchand</name>
<goals>9</goals>
<assists>10</assists>
</row>
<row>
<name>Patrice Bergeron</name>
<goals>7</goals>
<assists>11</assists>
</row>
...
<xsl:if>
<xsl:if test="<xpath-condition>">
allows for conditional processing of nodes based on the result of an XPath conditional expression. The context of the XPath expression is set by any parent <xsl:operation>
.
<xsl:if
test = boolean-expression >
</xsl:if>
In the example below, the rule after “xsl:if” is only applied if the conditional expression, the the value of the player’s <position> in the source XML element does not have the value “Goalie”, evaluates to true. This means that only those players whose position is not “Goalie” would be emitted to the result XML.
...
<xsl:template match="//team[@name='Boston Bruins']">
<roster>
<xsl:for-each select="player">
<xsl:if test = "position != 'Goalie'">
<row>
<name>
<xsl:value-of select="lastname" />
</name>
<goals>
<xsl:value-of select="points/goals" />
</goals>
<assists>
<xsl:value-of select="points/assists" />
</assists>
</row>
</xsl:if>
</xsl:for-each>
</roster>
</xsl:template>
...
<xsl:choose>
<xsl:choose>
and <xsl:when test="xpath-condition">
allows for multiple conditions and “if-then-else” like syntax. It is like a “switch-case” statement in some programming languages. Each condition is part of an <xsl:when>
element. If none of the test conditions are true, then an optional <xsl:otherwise>
element can be specified. The context of the XPath operation is set by any parent <xsl:operation>
.
<xsl:choose>
<xsl:when test = "xpath-condition1">
...
</xsl:when>
<xsl:when test = "xpath-condition2">
...
</xsl:when>
<xsl:otherwise>
...
</xsl:otherwise>
</xsl:choose>
<xsl:attribute>
<xsl:attribute name="text">
produces an attribute for an element in an output XML. The context of the XPath expression is set by any parent <xsl:operation>
. The attribute is added to the tag for which <xsl:attribute>
is a child.
<someElement>
<xsl:attribute
name = "someName" >
</xsl:attribute>
</someElement>
The illustration below shows how attributes to elements emerge.
<xsl:sort>
<xsl:sort name="text">
orders the output. <xsl:sort>
is always within <xsl:for-each>
or <xsl:apply-templates>
. The full syntax is:
<xsl:sort
select="expression"
lang="language-code"
data-type="text|number|qname"
order="ascending|descending"
case-order="upper-first|lower-first"/>
The fragment below demonstrates the use. Note that “xsl:sort” is a self-closing tag.
...
<xsl:template match="//team[@name='Boston Bruins']">
<roster>
<xsl:for-each select="player">
<xsl:sort select="lastname"/>
<xsl:if test = "position != 'Goalie'">
<row>
<playername>
<xsl:attribute name="number">
<xsl:value-of select="./@num" />
</xsl:attribute>
<xsl:value-of select="lastname" />
</playername>
<goals>
<xsl:value-of select="points/goals" />
</goals>
<assists>
<xsl:value-of select="points/assists" />
</assists>
</row>
</xsl:if>
</xsl:for-each>
</roster>
</xsl:template>
<xsl:output>
<xsl:output>
defines the output file format of the target file. The default is XML which means that XSLT will insert the standard XML preamble <?xml version="1.0"?>
. It must appear right after the “xsl:transform” or “xsl:stylesheet” element and before any “xsl:template” operator.
The full syntax is:
<xsl:output
method = "xml|html|text|name"
version = "string"
encoding = "string"
omit-xml-declaration = "yes|no"
standalone = "yes|no"
doctype-public = "string"
doctype-system = "string"
cdata-section-elements = "namelist"
indent = "yes|no"
media-type = "string"/>
The example XSL fragment below demonstrates its use:
<?xml version="1.0"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"
encoding="UTF-8"
indent="no"
omit-xml-declaration="yes"/>
<xsl:template match="//plants">
...
Omitting the declaration and suppressing indentation might be appropriate when producing a JSON or CSV file as output rather than XML.
<xsl:text>
<xsl:text>
inserts specific unparsed text into the output file that is ignored by XSLT. This allows any text to be sent to the output file. For example, to send special characters such as space or newline to the output file, use XML entities, e.g.,
for newline and 
for space. This can also be used to include an embedded DTD or a link to an external DTD into the output XML.
The full syntax is:
<xsl:text [xml:whitespace = "preserve"]>
any text here
</xsl:text>
<xsl:variable>
<xsl:variable name = "var" select = "xpath-expression">
creates a variable that can be accessed in XPath expressions. Use $varName to access a variable with the name varName.
The full syntax is:
<xsl:variable name = "variable"
select = "xpath-expression" />
An XSL fragment demonstrating its use is below. Note the use of an aggregation function in the XPath expression to get a single value for the value of the variable “numRecords”.
<?xml version="1.0"?>
<xsl:transform
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="numRecords"
select="count(records/record)"/>
<transactions>
<xsl:attribute name="numTxns">
<xsl:value-of select="$numRecords" />
</xsl:attribute>
...
Math Extensions to XSLT
To perform mathematical calculations in XSLT, use either XSL 2.0 or a math library.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:math="http://exslt.org/math"
extension-element-prefixes="math" >
<xsl:value-of select="(floor(math:random()*8) mod 8) + 1" />
<xsl:variable name="someRecord"
select="(floor(math:random() * $numRecords)
mod $numRecords) + 1"/>
<product><xsl:value-of
select="../record[$someRecord]/product"/></product>
...
Tutorial
The tutorial below provides an overview of the concepts in this lesson. The files and slide deck referenced in the tutorial are linked below and should be downloaded prior to watching the tutorial so that you can follow along.
Resources for Tutorial
Right-click (or open the context menu) and save the files rather than clicking on the links to open the files within your browser.
Summary
XSL is a set of rules expressed as XML elements used for transforming and formatting XML documents. XSL is essential for tasks like converting XML data into different formats, extracting specific information, creating data files (e.g., PDFs), and maintaining a clear separation between content and presentation. It follows W3C standards and is widely used for working with XML data in various applications.
Resources
None yet. Let us know if you have favorites.
LS0tCnRpdGxlOiAiWE1MIFRyYW5zZm9ybWF0aW9uIHZpYSBYU0xUIGluIFIiCnBhcmFtczoKICBjYXRlZ29yeTogNgogIG51bWJlcjogMzEwCiAgdGltZTogNDUKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAicix4cGF0aCx4bWwiCiAgZGVzY3JpcHRpb246ICJFeHBsYWlucyBob3cgdG8gdHJhbnNmb3JtIHN0cnVjdHVyZWQgaW5mb3JtYXRpb24gb2JqZWN0cwogICAgICAgICAgICAgICAgY29udGFpbmVkIGluIGFuIFhNTCBkb2N1bWVudCB0byBhbm90aGVyIHRleHQgCiAgICAgICAgICAgICAgICByZXByZXNlbnRhdGlvbiwgc3VjaCBhcyBYTUwsIEhUTUwsIENTViwgSlNPTiwgb3IgU1FMLgogICAgICAgICAgICAgICAgRGVtb25zdHJhdGVzIGhvdyB0byB1c2UgdGhlIFhTTFQgdHJhbnNmb3JtZXIgaW4gUi4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCiMjIEludHJvZHVjdGlvbgoKWFNMLCB3aGljaCBzdGFuZHMgZm9yIEV4dGVuc2libGUgU3R5bGVzaGVldCBMYW5ndWFnZSwgaXMgYSBmYW1pbHkgb2YgbGFuZ3VhZ2VzIHVzZWQgZm9yIHRyYW5zZm9ybWluZyBhbmQgcmVuZGVyaW5nIFhNTCBkb2N1bWVudHMuIFhTTCBpcyBwcmltYXJpbHkgY29tcG9zZWQgb2YgdHdvIG1haW4gY29tcG9uZW50czogWFNMVCAoWFNMIFRyYW5zZm9ybWF0aW9ucykgYW5kIFhTTC1GTyAoWFNMIEZvcm1hdHRpbmcgT2JqZWN0cykuCgoxLiAgKipYU0xUIChYU0wgVHJhbnNmb3JtYXRpb25zKSoqOgogICAgLSAgIFhTTFQgaXMgYSBsYW5ndWFnZSB1c2VkIHRvIHRyYW5zZm9ybSBYTUwgZG9jdW1lbnRzIGludG8gb3RoZXIgZm9ybWF0cywgc3VjaCBhcyBIVE1MLCBwbGFpbiB0ZXh0LCBDU1YsIFNRTCwgb3IgYW5vdGhlciBYTUwgc3RydWN0dXJlLgogICAgLSAgIEl0IGFsbG93cyB5b3UgdG8gZGVmaW5lIHJ1bGVzIGFuZCB0ZW1wbGF0ZXMgZm9yIGhvdyB0aGUgaW5wdXQgWE1MIHNob3VsZCBiZSBwcm9jZXNzZWQgYW5kIHRyYW5zZm9ybWVkIGludG8gdGhlIGRlc2lyZWQgb3V0cHV0IGZvcm1hdC4KICAgIC0gICBYU0xUIHVzZXMgYSBkZWNsYXJhdGl2ZSBhcHByb2FjaCwgd2hlcmUgeW91IHNwZWNpZnkgd2hhdCBzaG91bGQgaGFwcGVuIHRvIHRoZSBpbnB1dCBkYXRhIHJhdGhlciB0aGFuIHdyaXRpbmcgcHJvY2VkdXJhbCBjb2RlLgogICAgLSAgIFhTTFQgaXMgb2Z0ZW4gdXNlZCBmb3IgdGFza3MgbGlrZSBmb3JtYXQgY29udmVyc2lvbiwgZ2VuZXJhdGluZyByZXBvcnRzLCBhbmQgZXh0cmFjdGluZyBzcGVjaWZpYyBpbmZvcm1hdGlvbiBmcm9tIFhNTCBkb2N1bWVudHMuCjIuICAqKlhTTC1GTyAoWFNMIEZvcm1hdHRpbmcgT2JqZWN0cykqKjoKICAgIC0gICBYU0wtRk8gaXMgYSBsYW5ndWFnZSB1c2VkIGZvciBzcGVjaWZ5aW5nIHRoZSBsYXlvdXQgYW5kIGZvcm1hdHRpbmcgb2YgWE1MIGNvbnRlbnQgd2hlbiBpdCBuZWVkcyB0byBiZSBwcmVzZW50ZWQgb3IgcHJpbnRlZCwgc3VjaCBhcyBnZW5lcmF0aW5nIFBERiBkb2N1bWVudHMuCiAgICAtICAgSXQgYWxsb3dzIHlvdSB0byBkZWZpbmUgcnVsZXMgZm9yIHBhZ2luYXRpb24sIGZvbnRzLCBwYWdlIGhlYWRlcnMsIGZvb3RlcnMsIGFuZCBvdGhlciBmb3JtYXR0aW5nIGFzcGVjdHMgb2YgYSBkb2N1bWVudC4KICAgIC0gICBYU0wtRk8gaXMgZXNwZWNpYWxseSB1c2VmdWwgZm9yIGdlbmVyYXRpbmcgd2VsbC1zdHJ1Y3R1cmVkLCBwcmludC1yZWFkeSBkb2N1bWVudHMgZnJvbSBYTUwgZGF0YS4KClhTTCBpcyBlc3NlbnRpYWwgZm9yIHNldmVyYWwgcmVhc29uczoKCjEuICAqKkRhdGEgVHJhbnNmb3JtYXRpb24qKjogWE1MIGlzIG9mdGVuIHVzZWQgdG8gcmVwcmVzZW50IHN0cnVjdHVyZWQgZGF0YSwgYW5kIFhTTFQgcHJvdmlkZXMgYSB3YXkgdG8gdHJhbnNmb3JtIHRoaXMgZGF0YSBpbnRvIGRpZmZlcmVudCBmb3JtYXRzLCBtYWtpbmcgaXQgbW9yZSBhY2Nlc3NpYmxlIHRvIHZhcmlvdXMgYXBwbGljYXRpb25zIGFuZCBzeXN0ZW1zLiBGb3IgZXhhbXBsZSwgeW91IGNhbiBjb252ZXJ0IFhNTCBkYXRhIGludG8gWE1MIGZvciBzY2hlbWEgY29udmVyc2lvbiwgSFRNTCBmb3Igd2ViIGRpc3BsYXksIEpTT04gZm9yIEFQSSByZXNwb25zZXMsIG9yIENTViBmb3IgZGF0YSBleHBvcnQuCgoyLiAgKipEYXRhIEV4dHJhY3Rpb24qKjogWFNMVCBhbGxvd3MgeW91IHRvIGV4dHJhY3Qgc3BlY2lmaWMgaW5mb3JtYXRpb24gZnJvbSBhbiBYTUwgZG9jdW1lbnQgYnkgZGVmaW5pbmcgdGVtcGxhdGVzIHRoYXQgbWF0Y2ggdGhlIGRlc2lyZWQgZWxlbWVudHMgb3IgYXR0cmlidXRlcy4gVGhpcyBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIHdoZW4gZGVhbGluZyB3aXRoIGxhcmdlIFhNTCBkYXRhc2V0cy4KCjMuICAqKkRvY3VtZW50IFByZXNlbnRhdGlvbioqOiBYU0wtRk8gZW5hYmxlcyB0aGUgZ2VuZXJhdGlvbiBvZiBwcm9mZXNzaW9uYWxseSBmb3JtYXR0ZWQgZG9jdW1lbnRzLCBzdWNoIGFzIFBERnMsIGZyb20gWE1MIGNvbnRlbnQuIFRoaXMgaXMgdmFsdWFibGUgZm9yIGNyZWF0aW5nIHJlcG9ydHMsIGludm9pY2VzLCBtYW51YWxzLCBhbmQgb3RoZXIgZG9jdW1lbnRzIHdpdGggc3BlY2lmaWMgbGF5b3V0IGFuZCBzdHlsaW5nIHJlcXVpcmVtZW50cy4KCjQuICAqKlNlcGFyYXRpb24gb2YgQ29udGVudCBhbmQgUHJlc2VudGF0aW9uKio6IFhTTCBoZWxwcyBtYWludGFpbiBhIGNsZWFyIHNlcGFyYXRpb24gYmV0d2VlbiB0aGUgY29udGVudCAoWE1MIGRhdGEpIGFuZCBpdHMgcHJlc2VudGF0aW9uIChYU0xUIGFuZCBYU0wtRk8pLiBUaGlzIHNlcGFyYXRpb24gaXMgY3J1Y2lhbCBmb3IgcmV1c2FiaWxpdHkgYW5kIG1haW50YWluYWJpbGl0eSwgYXMgY2hhbmdlcyB0byB0aGUgcHJlc2VudGF0aW9uIGNhbiBiZSBtYWRlIGluZGVwZW5kZW50bHkgb2YgdGhlIHVuZGVybHlpbmcgZGF0YS4KCjUuICAqKkNyb3NzLVBsYXRmb3JtIENvbXBhdGliaWxpdHkqKjogWE1MIGFuZCBYU0xUIGFyZSBwbGF0Zm9ybS1hZ25vc3RpYyBhbmQgY2FuIGJlIHVzZWQgb24gdmFyaW91cyBvcGVyYXRpbmcgc3lzdGVtcyBhbmQgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzLiBUaGlzIG1ha2VzIHRoZW0gc3VpdGFibGUgZm9yIGludGVyb3BlcmFiaWxpdHkgaW4gaGV0ZXJvZ2VuZW91cyBlbnZpcm9ubWVudHMuCgo2LiAgKipTdGFuZGFyZHMgQ29tcGxpYW5jZSoqOiBYU0wgZm9sbG93cyBXM0MgKFdvcmxkIFdpZGUgV2ViIENvbnNvcnRpdW0pIHN0YW5kYXJkcywgZW5zdXJpbmcgdGhhdCB5b3VyIFhNTCBwcm9jZXNzaW5nIGFuZCB0cmFuc2Zvcm1hdGlvbiB0YXNrcyBhZGhlcmUgdG8gZXN0YWJsaXNoZWQgYmVzdCBwcmFjdGljZXMuCgpJbiBzaG9ydCwgWFNMLCBjb21wcmlzaW5nIFhTTFQgYW5kIFhTTC1GTywgaXMgdXNlZnVsIGZvciB3b3JraW5nIHdpdGggWE1MIGRhdGEsIGVuYWJsaW5nIGRhdGEgdHJhbnNmb3JtYXRpb24sIGV4dHJhY3Rpb24sIGFuZCBkb2N1bWVudCBmb3JtYXR0aW5nLiBJdCBoZWxwcyBicmlkZ2UgdGhlIGdhcCBiZXR3ZWVuIHN0cnVjdHVyZWQgZGF0YSBhbmQgaXRzIHByZXNlbnRhdGlvbiBpbiBkaWZmZXJlbnQgZm9ybWF0cywgbWFraW5nIGl0IGEgZnVuZGFtZW50YWwgdGVjaG5vbG9neSBmb3IgYSB3aWRlIHJhbmdlIG9mIGFwcGxpY2F0aW9ucyBpbiB3ZWIgZGV2ZWxvcG1lbnQsIGRhdGEgaW50ZWdyYXRpb24sIGFuZCBkb2N1bWVudCBnZW5lcmF0aW9uLgoKVGhpcyBsZXNzb24gZXhwbGFpbnMgaG93IHRvIGJ1aWxkIGFuIFhTTCB0byB0cmFuc2Zvcm0gWE1MIGZyb20gb25lIFhNTCBmb3JtYXQgdG8gYW5vdGhlci4KCiMjIEFwcGx5aW5nIGFuIFhTTAoKQW4gWFNMIGlzIGEgdGV4dCBmaWxlIGNvbnRhaW5pbmcgdGhlIHRyYW5zZm9ybWF0aW9uIHJ1bGVzIChleHByZXNzZWQgYXMgWE1MKS4gVGhlIFhTTCBpcyBmZWQgdG8gYSB0cmFuc2Zvcm1hdGlvbiBlbmdpbmUgKHNlZSBiZWxvdykgYWxvbmcgd2l0aCB0aGUgc291cmNlIFhNTC4gVGhlIHRyYW5zZm9ybWF0aW9uIGVuZ2luZSBwcm9kdWNlcyBhbiBvdXRwdXQgZmlsZS4gVGhlIGZsb3cgaXMgc2hvd24gaW4gdGhlIGRpYWdyYW0gYmVsb3cuCgohW10oaW1hZ2VzL3hzbHQtZmxvdy5wbmcpe3dpZHRoPSJcIjkwJSJ9CgojIyMgVXNpbmcgVG9vbHMKClRoZXJlIGFyZSBudW1lcm91cyB0b29scyBmb3IgYXBwbHlpbmcgWFNMIHRyYW5zZm9ybXMgdG8gWE1MIHNvdXJjZSBmaWxlcy4gT25lIHN1Y2ggdG9vbCBpcyBbZnJlZWZvcm1hdHRlci5jb21dKGh0dHBzOi8vZnJlZWZvcm1hdHRlci5jb20veHNsLXRyYW5zZm9ybWVyLmh0bWwpLgoKIyMjIFhTTCBpbiBSCgpUbyB0cmFuc2Zvcm0gWE1MIGRvY3VtZW50cyB3aXRoIFhTTCBpbiBSLCB5b3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgYW5kIHRoZW4gbG9hZCB0aGUgcGFja2FnZSAqKnhzbHQqKi4KCmBgYHtyfQpsaWJyYXJ5KHhzbHQpCmBgYAoKVG8gcGVyZm9ybSB0aGUgdHJhbnNmb3JtYXRpb24sIGxvYWQgdGhlIHNvdXJjZSBYTUwsIGxvYWQgdGhlIFhTTCwgYXBwbHkgdGhlIHRyYW5zZm9ybSwgYW5kIHRoZW4gc2F2ZSB0aGUgcmVzdWx0LgoKYGBge3J9CnhtbEZuIDwtICJYTUwvVGVhbVJvc3RlcnMueG1sIgp4c2xGbiA8LSAiWFNML1Jvc3RlcnMyU3RhdHNYU0wueHNsIgp0eG5GbiA8LSAiWE1ML0JydWluc1N0YXRzLnhtbCIKCiMgU3RlcCAxOiBsb2FkIHRoZSBzb3VyY2UgWE1MCnhtbERvYyA8LSByZWFkX3htbCh4bWxGbikKCiMgU3RlcCAyOiBsb2FkIHRoZSBYU0wgdHJhbnNmb3JtYXRpb24KeHNsU3R5bGUgPC0gcmVhZF94bWwoeHNsRm4pCgojIFN0ZXAgMzogYXBwbHkgdGhlIFhTTCB0byB0aGUgWE1MCnRyYW5zZm9ybWVkWE1MIDwtIHhtbF94c2x0KHhtbERvYywgeHNsU3R5bGUpCgojIFN0ZXAgNDogd3JpdGUgdGhlIHJlc3VsdGluZyBYTUwgdG8gYSBmaWxlCnN0YXR1cyA8LSB3cml0ZV94bWwodHJhbnNmb3JtZWRYTUwsIGZpbGUgPSB0eG5GbikKYGBgCgo+ICoqUiAvIFIgU3R1ZGlvIFByb2dyYW1taW5nIEhpbnQqKjogT2NjYXNpb25hbGx5IGR1cmluZyBYU0wgcHJvY2Vzc2luZyBhbiBlcnJvciBvY2N1cnMgYW5kIHRoZSBYU0xUIHByb2Nlc3NvciBnb2VzIGludG8gYW4gaW5maW5pdGUgbG9vcC4gVG8gc3RvcCB0aGUgWFNMVCwgeW91IG5lZWQgdG8gcmVzdGFydCBSLiBJbiBSIFN0dWRpbyAoUG9zaXQpLCBjbGljayBvbiBTZXNzaW9uL1Jlc3RhcnQgUi4uLgoKIyMgQnVpbGRpbmcgWFNMIFRyYW5zZm9ybWF0aW9ucwoKIyMjIEV4YW1wbGUgWE1MCgpUaGUgc291cmNlIFhNTCB1c2VkIGluIHRoZSBleGFtcGxlcyBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCBwbGF5ZXJzIG9uIHRlYW0gcm9zdGVycyBhbmQgbWlnaHQgYmUgaW1wb3J0ZWQgaW50byBhIHRlYW0gYW5hbHl0aWNzIHN5c3RlbSBvciB0byBwbGF5IGluIGEgZmFudGFzeSBzcG9ydHMgbGVhZ3VlLiBXZSB3YW50IHRvIGV4dHJhY3QgdGhlIHBsYXllcidzIGxhc3QgbmFtZSwgdGhlaXIgZ29hbHMgYW5kIGFzc2lzdHMuIFdlIHdhbnQgdG8gZXhjbHVkZSBnb2FsaWVzLiBUaGUgcmVzdWx0IHNob3VsZCBiZSBhIFhNTCB0aGF0IGNhbiBiZSByZWFkIGludG8gRXhjZWwgKHVzaW5nIGl0cyBYTUwgaW1wb3J0IHdpemFyZCkgb3IgZGlyZWN0bHkgaW50byBhIGRhdGEgZnJhbWUsIHNvIGl0IHNob3VsZCBoYXZlIGEgc2ltcGxlIHR3by1sZXZlbCBzdHJ1Y3R1cmUuIEEgcG9ydGlvbiBvZiB0aGUgWE1MIChbVGVhbVJvc3RlcnMueG1sXShYTUwvVGVhbVJvc3RlcnMueG1sKSkgaXMgcmVwcm9kdWNlZCBiZWxvdzoKCmBgYCB4bWwKPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KCjxyb3N0ZXJzIHNlYXNvbj0iMjAyMSI+CiAgIDx0ZWFtIG5hbWU9IkJvc3RvbiBCcnVpbnMiIGRpdmlzaW9uPSJNYXNzTXV0dWFsIEVhc3QiPgogICAgICA8ZGF0ZT4yMDIxLTAyLTE5PC9kYXRlPgogICAgICA8dGVhbS1pbmZvPgogICAgICAgICA8Y2l0eT5Cb3N0b248L2NpdHk+CiAgICAgICAgIC4uLgogICAgICA8L3RlYW0taW5mbz4KICAgICAgPHNlYXNvbi1pbmZvPgogICAgICAgICA8Z3A+MTU8L2dwPgogICAgICAgICA8cHRzIHc9IjEwIiBsPSIzIiBvdD0iMiI+MjI8L3B0cz4KICAgICAgPC9zZWFzb24taW5mbz4KICAgICAgPHBsYXllciBudW09IjYzIj4KICAgICAgICAgPGZpcnN0bmFtZT5CcmFkPC9maXJzdG5hbWU+CiAgICAgICAgIDxsYXN0bmFtZT5NYXJjaGFuZDwvbGFzdG5hbWU+CiAgICAgICAgIDxwb3NpdGlvbj5Gb3J3YXJkPC9wb3NpdGlvbj4KICAgICAgICAgPGFzc2lzdGFudGNhcHRhaW4vPgogICAgICAgICA8cG9pbnRzPgogICAgICAgICAgICA8Z29hbHM+OTwvZ29hbHM+CiAgICAgICAgICAgIDxhc3Npc3RzPjEwPC9hc3Npc3RzPgogICAgICAgICA8L3BvaW50cz4KICAgICAgPC9wbGF5ZXI+CiAgICAgIOKApgogICAgICA8cGxheWVyIG51bT0iNzMiPgogICAgICAgICA8Zmlyc3RuYW1lPkNoYXJsaWU8L2ZpcnN0bmFtZT4KICAgICAgICAgPGxhc3RuYW1lPk1jQXZveTwvbGFzdG5hbWU+CiAgICAgICAgIC4uLgogICAgICA8L3BsYXllcj4KICAgICAgPHBsYXllciBudW09IjQwIj4KICAgICAgICAgPGZpcnN0bmFtZT5UdXVrYTwvZmlyc3RuYW1lPgogICAgICAgICA8bGFzdG5hbWU+UmFzazwvbGFzdG5hbWU+CiAgICAgICAgIDxwb3NpdGlvbj5Hb2FsaWU8L3Bvc2l0aW9uPgogICAgICAgICAuLi4KICAgICAgICAgPHN0YXRzPgogICAgICAgICAgICA8R0E+MjM8L0dBPgogICAgICAgICAgICA8U1Y+MC45MDY8L1NWPgogICAgICAgICA8L3N0YXRzPgogICAgICA8L3BsYXllcj4KICAgPC90ZWFtPgo8L3Jvc3RlcnM+CmBgYAoKVGhlIHJlc3VsdGluZyBYTUwgc2hvdWxkIGxvb2sgbGlrZSB0aGlzIChzb3J0ZWQgYnkgcGxheWVyIGxhc3QgbmFtZSBhbmQgZXhjbHVkaW5nIGdvYWxpZXMpOgoKYGBgIHhtbAo8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/Pgo8cm9zdGVyPgogIDxyb3c+CiAgICA8bmFtZSBudW1iZXI9IjM3Ij5CZXJnZXJvbjwvbmFtZT4KICAgIDxnb2Fscz43PC9nb2Fscz4KICAgIDxhc3Npc3RzPjExPC9hc3Npc3RzPgogIDwvcm93PgogIDxyb3c+CiAgICA8bmFtZSBudW1iZXI9IjYzIj5NYXJjaGFuZDwvbmFtZT4KICAgIDxnb2Fscz45PC9nb2Fscz4KICAgIDxhc3Npc3RzPjEwPC9hc3Npc3RzPgogIDwvcm93PgogIC4uLgo8L3Jvc3Rlcj4KYGBgCgojIyMgRGVmaW5lIFByZWFtYmxlIGZvciBYU0wKClNpbmNlIGFuIFhTTCBzdHlsZSBzaGVldCBpcyBhbiBYTUwgZG9jdW1lbnQsIGl0IGJlZ2lucyB3aXRoIHRoZSBYTUwgZGVjbGFyYXRpb246CgpgYGAgeG1sCjw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+CmBgYAoKVGhlIG5leHQgZWxlbWVudCwgYDx4c2w6dHJhbnNmb3JtPmAgZGVmaW5lcyB0aGF0IHRoZSBYTUwgZG9jdW1lbnQgaXMgYW4gWFNMVCBzdHlsZSBzaGVldCBkb2N1bWVudC4KClRoZSBlbGVtZW50IGA8eHNsOnN0eWxlc2hlZXQ+YCBpcyBzeW5vbnltb3VzIHdpdGggYDx4c2w6dHJhbnNmb3JtPmAuCgpUaGUgKnhtbG5zOnhzbCogYXR0cmlidXRlIGRlZmluZXMgYSB1bmlxdWUgbmFtZXNwYWNlIGFuZCBtYXkgcG9pbnQgdG8gYW55IFVSTCwgYnV0IG1vc3QgY29tbW9ubHkgdGhlIG9uZSBzaG93bi4gVGhlIFhTTCBmcmFnbWVudCBpcyBzdGFuZGFyZCwgc28gc2ltcGx5IGNvcHkgdGhpcyBjb2RlLgoKIyMjIEtleSBSdWxlIE9wZXJhdG9ycwoKS2V5IGA8eHNsOm9wZXJhdG9ycz5gOgoKLSAgIGA8eHNsOnRlbXBsYXRlPmAKLSAgIGA8eHNsOnZhbHVlLW9mPmAKLSAgIGA8eHNsOmZvci1lYWNoPmAKLSAgIGA8eHNsOnNvcnQ+YAotICAgYDx4c2w6aWY+YAotICAgYDx4c2w6Y2hvb3NlPmAKLSAgIGA8eHNsOmtleT5gCi0gICBgPHhzbDptZXNzYWdlPmAKLSAgIGA8eHNsOmFwcGx5LXRlbXBsYXRlPmAKLSAgIGA8eHNsOmltcG9ydD5gCi0gICBgPHhzbDpvdXRwdXQ+YAotICAgYDx4c2w6dGV4dD5gCi0gICBgPHhzbDphdHRyaWJ1dGU+YAoKIyMjIGA8eHNsOnRlbXBsYXRlPmAKCkFuIFhTTCBzdHlsZSBzaGVldCBjb25zaXN0cyBvZiBvbmUgb3IgbW9yZSBzZXRzIG9mIHJ1bGVzIGNhbGxlZCB0ZW1wbGF0ZXMuIEVhY2ggYDx4c2w6dGVtcGxhdGU+YCBlbGVtZW50IGRlZmluZXMgYSBzZWN0aW9uIG9mIHRoZSB0YXJnZXQgZmlsZS4gVGhlIHZhbHVlIG9mIHRoZSAqbWF0Y2gqIGF0dHJpYnV0ZSBpcyBhbiBYUGF0aCBleHByZXNzaW9uLiBUaGUgcmVzdWx0IGRvY3VtZW50IGlzIGJ1aWx0IHRvcCB0byBib3R0b20uIEFueSB0ZXh0IHRoYXQgaXMgbm90IGA8eHNsOiDigKY+YCBpcyBwYXJ0IG9mIHRoZSB0YXJnZXQgZG9jdW1lbnQgKG91dHB1dCBvZiBYU0xUKS4KCioqRXhhbXBsZSBYU0wqKgoKYGBgIHhtbAo8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/PgoKPHhzbDpzdHlsZXNoZWV0CiAgICAgIHZlcnNpb249IjEuMCIKICAgICAgeG1sbnM6eHNsPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L1hTTC9UcmFuc2Zvcm0iPgogCiAgPHhzbDp0ZW1wbGF0ZSBtYXRjaD0iLyI+CiAgCiAgICA8cm9zdGVyPgogICAgICBCb3N0b24gQnJ1aW5zCiAgICA8L3Jvc3Rlcj4KICAgIAogIDwveHNsOnRlbXBsYXRlPgo8L3hzbDpzdHlsZXNoZWV0PgpgYGAKCioqUmVzdWx0IFhNTCoqCgpgYGAgeG1sCjw/eG1sIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IlVURi04Ij8+Cjxyb3N0ZXI+CiAgQm9zdG9uIEJydWlucwo8L3Jvc3Rlcj4KYGBgCgojIyMjIEZ1bGwgU3ludGF4OiBgPHhzbDp0ZW1wbGF0ZSBtYXRjaD0iPHhwYXRoLWV4cHI+Ij5gCgohW10oaW1hZ2VzL3RlbXBsYXRlLW1hdGNoLnBuZyl7d2lkdGg9IjcwJSJ9CgpgYGAgeG1sCjx4c2w6dGVtcGxhdGUgCiAgIG5hbWUgPSBRTmFtZSAKICAgbWF0Y2ggPSBQYXR0ZXJuIAogICBwcmlvcml0eSA9IG51bWJlciAKICAgbW9kZSA9IFFOYW1lID4KPC94c2w6dGVtcGxhdGU+CmBgYAoKLSAgICoqbmFtZSoqOiBOYW1lIG9mIHRoZSBlbGVtZW50IHRvIHdoaWNoIHRlbXBsYXRlIGlzIHRvIGJlIGFwcGxpZWQuCi0gICAqKm1hdGNoKio6IFhQYXRoIHBhdHRlcm4gd2hpY2ggbWF0Y2hlcyB0aGUgZWxlbWVudChzKSBvbiB3aGljaCB0ZW1wbGF0ZSBpcyB0byBiZSBhcHBsaWVkLgotICAgKipwcmlvcml0eSoqOiBQcmlvcml0eSBvZiBhIHRlbXBsYXRlIGluIGNhc2Ugb2YgY29uZmxpY3RpbmcgdGVtcGxhdGVzLgotICAgKiptb2RlKio6IEFsbG93cyBlbGVtZW50IHRvIGJlIHByb2Nlc3NlZCBtdWx0aXBsZSB0aW1lcyB0byBwcm9kdWNlIGEgZGlmZmVyZW50IHJlc3VsdCBlYWNoIHRpbWUuCgojIyMgYDx4c2w6Zm9yLWVhY2g+YAoKYDx4c2w6Zm9yLWVhY2ggc2VsZWN0PSI8eHBhdGgtZXhwcj4iPmAgcHJvY2Vzc2VzIGEgc2V0IG9mIFhNTCBlbGVtZW50cyB0aGF0IG1hdGNoIHRoZSBYUGF0aCBleHByZXNzaW9uLiBJdCBsb29wcyAoKmkuZS4qLCBpdGVyYXRlcykgdGhyb3VnaCBlYWNoIGVsZW1lbnQgaW4gdGhlIG1hdGNoZWQgc2V0IG9mIGVsZW1lbnRzIG9uZSBieSBvbmU6IGl0IGlzIGFuIGl0ZXJhdG9yLiBUaGUgY29udGV4dCBvZiB0aGUgWFBhdGggZXhwcmVzc2lvbiBpcyBzZXQgYnkgYW55IHBhcmVudCBgPHhzbDpvcGVyYXRpb24+YC4KCmBgYCB4bWwKPHhzbDpmb3ItZWFjaAogICBzZWxlY3QgPSBYUGF0aC1FeHByZXNzaW9uPgoKICBwcm9jZXNzIGVhY2ggbm9kZQo8L3hzbDpmb3ItZWFjaD4KYGBgCgpJbiB0aGUgZXhhbXBsZSBmcmFnbWVudCBiZWxvdywgdGhlIFhQYXRoIGV4cHJlc3Npb24gd2l0aGluICJ4c2w6Zm9yLWVhY2giIGJpbmRzIHRvIGVhY2ggKlw8cGxheWVyXD4qIGVsZW1lbnQgdW5kZXIgKlw8dGVhbVw+KiBhbmQgdGhlIHJ1bGUgaXMgYXBwbGllZCB0byBlYWNoIG9mIHRoZW0gaW4gdHVybi4KCioqRXhhbXBsZSBYU0wqKgoKYGBgIHhtbAouLi4KPHhzbDp0ZW1wbGF0ZSBtYXRjaD0iLy90ZWFtW0BuYW1lPSdCb3N0b24gQnJ1aW5zJ10iPgoKICA8cm9zdGVyPgogICAgPHhzbDpmb3ItZWFjaCBzZWxlY3Q9InBsYXllciI+CiAgICAgIDxyb3c+CiAgICAgICAgPGNlbGw+QnJhZCBNYXJjaGFuZDwvY2VsbD4KICAgICAgICA8Y2VsbD45PC9jZWxsPgogICAgICAgIDxjZWxsPjk8L2NlbGw+CiAgICAgIDwvcm93PgogICAgPC94c2w6Zm9yLWVhY2g+CiAgPC9yb3N0ZXI+CiAgCjwveHNsOnRlbXBsYXRlPgouLi4KYGBgCgpUaGUgcmVzdWx0aW5nIFhNTCB3b3VsZCBiZSBhcyBmb2xsb3dzOgoKYGBgIHhtbAo8P3htbCB2ZXJzaW9uPSIxLjAiIGVuY29kaW5nPSJVVEYtOCI/Pgo8cm9zdGVyPgogIDxyb3c+CiAgICA8Y2VsbD5CcmFkIE1hcmNoYW5kPC9jZWxsPgogICAgPGNlbGw+OTwvY2VsbD4KICAgIDxjZWxsPjk8L2NlbGw+CiAgPC9yb3c+CiAgPHJvdz4KICAgIDxjZWxsPkJyYWQgTWFyY2hhbmQ8L2NlbGw+CiAgICA8Y2VsbD45PC9jZWxsPgogICAgPGNlbGw+OTwvY2VsbD4KICA8L3Jvdz4KICAuLi4KYGBgCgojIyMgYDx4c2w6dmFsdWUtb2Y+YAoKYDx4c2w6dmFsdWUtb2Ygc2VsZWN0PSJ4cGF0aC1leHByIj5gIGV4dHJhY3RzIHRoZSB2YWx1ZSBvZiB0aGUgc2VsZWN0ZWQgbm9kZSBkZXRlcm1pbmVkIGJ5IHRoZSBYUGF0aCBleHByZXNzaW9uLCBhcyB0ZXh0LiBUaGUgY29udGV4dCBvZiB0aGUgWFBhdGggZXhwcmVzc2lvbiBpcyBzZXQgYnkgYW55IHBhcmVudCBgPHhzbDpvcGVyYXRpb24+YC4gVGhlIHZhbHVlIG9mIGEgbm9kZSBvciBlbGVtZW50IGlzIGV2ZXJ5dGhpbmcgYmV0d2VlbiB0aGUgZWxlbWVudCdzIG9wZW5pbmcgYW5kIGNsb3NpbmcgdGFncywgKmUuZy4qLCB0aGUgdmFsdWUgb2YgImdvYWxzIiBmb3IgYDxnb2Fscz45PC9nb2Fscz5gIGlzIDkgYW5kIHRoZSB2YWx1ZSBvZiAic3RhdHMiIGZvciBgPHN0YXRzPjxnb2Fscz45PC9nb2Fscz48L3N0YXRzPmAgaXMgIlw8Z29hbHNcPjlcPC9nb2Fsc1w+Ii4KClRoZSBmdWxsIHN5bnRheCBvZiBgPHhzbDp2YWx1ZS1vZj5gIGlzOgoKYGBgIHhtbAo8eHNsOnZhbHVlLW9mCiAgIHNlbGVjdCA9IEV4cHJlc3Npb24KICAgZGlzYWJsZS1vdXRwdXQtZXNjYXBpbmcgPSAieWVzIiB8ICJubyIvPgpgYGAKCioqRXhhbXBsZSBYU0wqKgoKYGBgIHhtbAo8eHNsOnRlbXBsYXRlIG1hdGNoPSIvL3RlYW1bQG5hbWU9J0Jvc3RvbiBCcnVpbnMnXSI+CiAgPHJvc3Rlcj4KICAgIDx4c2w6Zm9yLWVhY2ggc2VsZWN0PSJwbGF5ZXIiPgogICAgICAgPHJvdz4KICAgICAgICAgIDxuYW1lPgogICAgICAgICAgICA8eHNsOnZhbHVlLW9mIHNlbGVjdD0ibGFzdG5hbWUiIC8+CiAgICAgICAgICA8L25hbWU+CiAgICAgICAgICA8Z29hbHM+CiAgICAgICAgICAgIDx4c2w6dmFsdWUtb2Ygc2VsZWN0PSJwb2ludHMvZ29hbHMiIC8+CiAgICAgICAgICA8L2dvYWxzPgogICAgICAgICAgPGFzc2lzdHM+CiAgICAgICAgICAgIDx4c2w6dmFsdWUtb2Ygc2VsZWN0PSJwb2ludHMvYXNzaXN0cyIgLz4KICAgICAgICAgIDwvYXNzaXN0cz4KICAgICAgICA8L3Jvdz4gICAgICAKICAgIDwveHNsOmZvci1lYWNoPgogIDwvcm9zdGVyPgo8L3hzbDp0ZW1wbGF0ZT4KLi4uCmBgYAoKKipSZXN1bHQgWE1MKioKCmBgYCB4bWwKPD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHJvc3Rlcj4KICA8cm93PgogICAgPG5hbWU+QnJhZCBNYXJjaGFuZDwvbmFtZT4KICAgIDxnb2Fscz45PC9nb2Fscz4KICAgIDxhc3Npc3RzPjEwPC9hc3Npc3RzPgogIDwvcm93PgogIDxyb3c+CiAgICA8bmFtZT5QYXRyaWNlIEJlcmdlcm9uPC9uYW1lPgogICAgPGdvYWxzPjc8L2dvYWxzPgogICAgPGFzc2lzdHM+MTE8L2Fzc2lzdHM+CiAgPC9yb3c+CiAgLi4uCmBgYAoKIyMjIGA8eHNsOmlmPmAKCmA8eHNsOmlmIHRlc3Q9Ijx4cGF0aC1jb25kaXRpb24+Ij5gIGFsbG93cyBmb3IgY29uZGl0aW9uYWwgcHJvY2Vzc2luZyBvZiBub2RlcyBiYXNlZCBvbiB0aGUgcmVzdWx0IG9mIGFuIFhQYXRoIGNvbmRpdGlvbmFsIGV4cHJlc3Npb24uIFRoZSBjb250ZXh0IG9mIHRoZSBYUGF0aCBleHByZXNzaW9uIGlzIHNldCBieSBhbnkgcGFyZW50IGA8eHNsOm9wZXJhdGlvbj5gLgoKYGBgIHhtbAo8eHNsOmlmIAogIHRlc3QgPSBib29sZWFuLWV4cHJlc3Npb24gPiAKPC94c2w6aWY+IApgYGAKCkluIHRoZSBleGFtcGxlIGJlbG93LCB0aGUgcnVsZSBhZnRlciAieHNsOmlmIiBpcyBvbmx5IGFwcGxpZWQgaWYgdGhlIGNvbmRpdGlvbmFsIGV4cHJlc3Npb24sIHRoZSB0aGUgdmFsdWUgb2YgdGhlIHBsYXllcidzICpcPHBvc2l0aW9uXD4qIGluIHRoZSBzb3VyY2UgWE1MIGVsZW1lbnQgZG9lcyBub3QgaGF2ZSB0aGUgdmFsdWUgIkdvYWxpZSIsIGV2YWx1YXRlcyB0byAqdHJ1ZSouIFRoaXMgbWVhbnMgdGhhdCBvbmx5IHRob3NlIHBsYXllcnMgd2hvc2UgcG9zaXRpb24gaXMgbm90ICJHb2FsaWUiIHdvdWxkIGJlIGVtaXR0ZWQgdG8gdGhlIHJlc3VsdCBYTUwuCgpgYGAgeG1sCi4uLgo8eHNsOnRlbXBsYXRlIG1hdGNoPSIvL3RlYW1bQG5hbWU9J0Jvc3RvbiBCcnVpbnMnXSI+CiAgPHJvc3Rlcj4KICAgIDx4c2w6Zm9yLWVhY2ggc2VsZWN0PSJwbGF5ZXIiPgogICAgICA8eHNsOmlmIHRlc3QgPSAicG9zaXRpb24gIT0gJ0dvYWxpZSciPgogICAgICAgIDxyb3c+CiAgICAgICAgICA8bmFtZT4KICAgICAgICAgICAgPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9Imxhc3RuYW1lIiAvPgogICAgICAgICAgPC9uYW1lPgogICAgICAgICAgPGdvYWxzPgogICAgICAgICAgICA8eHNsOnZhbHVlLW9mIHNlbGVjdD0icG9pbnRzL2dvYWxzIiAvPgogICAgICAgICAgPC9nb2Fscz4KICAgICAgICAgIDxhc3Npc3RzPgogICAgICAgICAgICA8eHNsOnZhbHVlLW9mIHNlbGVjdD0icG9pbnRzL2Fzc2lzdHMiIC8+CiAgICAgICAgICA8L2Fzc2lzdHM+CiAgICAgICAgPC9yb3c+ICAgICAgICAKICAgICAgPC94c2w6aWY+ICAgICAgCiAgICA8L3hzbDpmb3ItZWFjaD4KICA8L3Jvc3Rlcj4KPC94c2w6dGVtcGxhdGU+Ci4uLgpgYGAKCiMjIyBgPHhzbDpjaG9vc2U+YAoKYDx4c2w6Y2hvb3NlPmAgYW5kIGA8eHNsOndoZW4gdGVzdD0ieHBhdGgtY29uZGl0aW9uIj5gIGFsbG93cyBmb3IgbXVsdGlwbGUgY29uZGl0aW9ucyBhbmQgImlmLXRoZW4tZWxzZSIgbGlrZSBzeW50YXguIEl0IGlzIGxpa2UgYSAic3dpdGNoLWNhc2UiIHN0YXRlbWVudCBpbiBzb21lIHByb2dyYW1taW5nIGxhbmd1YWdlcy4gRWFjaCBjb25kaXRpb24gaXMgcGFydCBvZiBhbiBgPHhzbDp3aGVuPmAgZWxlbWVudC4gSWYgbm9uZSBvZiB0aGUgdGVzdCBjb25kaXRpb25zIGFyZSB0cnVlLCB0aGVuIGFuIG9wdGlvbmFsIGA8eHNsOm90aGVyd2lzZT5gIGVsZW1lbnQgY2FuIGJlIHNwZWNpZmllZC4gVGhlIGNvbnRleHQgb2YgdGhlIFhQYXRoIG9wZXJhdGlvbiBpcyBzZXQgYnkgYW55IHBhcmVudCBgPHhzbDpvcGVyYXRpb24+YC4KCmBgYCB4bWwKPHhzbDpjaG9vc2U+CiAgPHhzbDp3aGVuIHRlc3QgPSAieHBhdGgtY29uZGl0aW9uMSI+CiAgICAuLi4KICA8L3hzbDp3aGVuPiAKICA8eHNsOndoZW4gdGVzdCA9ICJ4cGF0aC1jb25kaXRpb24yIj4KICAgIC4uLgogIDwveHNsOndoZW4+IAogIDx4c2w6b3RoZXJ3aXNlPgogICAgLi4uCiAgPC94c2w6b3RoZXJ3aXNlPgo8L3hzbDpjaG9vc2U+CmBgYAoKIyMjIGA8eHNsOmF0dHJpYnV0ZT5gCgpgPHhzbDphdHRyaWJ1dGUgbmFtZT0idGV4dCI+YCBwcm9kdWNlcyBhbiBhdHRyaWJ1dGUgZm9yIGFuIGVsZW1lbnQgaW4gYW4gb3V0cHV0IFhNTC4gVGhlIGNvbnRleHQgb2YgdGhlIFhQYXRoIGV4cHJlc3Npb24gaXMgc2V0IGJ5IGFueSBwYXJlbnQgYDx4c2w6b3BlcmF0aW9uPmAuIFRoZSBhdHRyaWJ1dGUgaXMgYWRkZWQgdG8gdGhlIHRhZyBmb3Igd2hpY2ggYDx4c2w6YXR0cmlidXRlPmAgaXMgYSBjaGlsZC4KCmBgYCB4bWwKPHNvbWVFbGVtZW50PgogIDx4c2w6YXR0cmlidXRlIAogICAgbmFtZSA9ICJzb21lTmFtZSIgPiAKICA8L3hzbDphdHRyaWJ1dGU+Cjwvc29tZUVsZW1lbnQ+IApgYGAKClRoZSBpbGx1c3RyYXRpb24gYmVsb3cgc2hvd3MgaG93IGF0dHJpYnV0ZXMgdG8gZWxlbWVudHMgZW1lcmdlLgoKIVtdKGltYWdlcy94c2wtYXR0cmlidXRlLnBuZyl7d2lkdGg9IjYwJSJ9CgojIyMgYDx4c2w6c29ydD5gCgpgPHhzbDpzb3J0IG5hbWU9InRleHQiPmAgb3JkZXJzIHRoZSBvdXRwdXQuIGA8eHNsOnNvcnQ+YCBpcyBhbHdheXMgd2l0aGluIGA8eHNsOmZvci1lYWNoPmAgb3IgYDx4c2w6YXBwbHktdGVtcGxhdGVzPmAuIFRoZSBmdWxsIHN5bnRheCBpczoKCmBgYCB4bWwKPHhzbDpzb3J0IAogIHNlbGVjdD0iZXhwcmVzc2lvbiIKICBsYW5nPSJsYW5ndWFnZS1jb2RlIgogIGRhdGEtdHlwZT0idGV4dHxudW1iZXJ8cW5hbWUiCiAgb3JkZXI9ImFzY2VuZGluZ3xkZXNjZW5kaW5nIgogIGNhc2Utb3JkZXI9InVwcGVyLWZpcnN0fGxvd2VyLWZpcnN0Ii8+CmBgYAoKVGhlIGZyYWdtZW50IGJlbG93IGRlbW9uc3RyYXRlcyB0aGUgdXNlLiBOb3RlIHRoYXQgInhzbDpzb3J0IiBpcyBhIHNlbGYtY2xvc2luZyB0YWcuCgpgYGAgeG1sCi4uLgo8eHNsOnRlbXBsYXRlIG1hdGNoPSIvL3RlYW1bQG5hbWU9J0Jvc3RvbiBCcnVpbnMnXSI+CiAgPHJvc3Rlcj4KICAgIDx4c2w6Zm9yLWVhY2ggc2VsZWN0PSJwbGF5ZXIiPgogICAgICA8eHNsOnNvcnQgc2VsZWN0PSJsYXN0bmFtZSIvPgogICAgICA8eHNsOmlmIHRlc3QgPSAicG9zaXRpb24gIT0gJ0dvYWxpZSciPgogICAgICAgIDxyb3c+CiAgICAgICAgICA8cGxheWVybmFtZT4KICAgICAgICAgICAgPHhzbDphdHRyaWJ1dGUgbmFtZT0ibnVtYmVyIj4KICAgICAgICAgICAgICA8eHNsOnZhbHVlLW9mIHNlbGVjdD0iLi9AbnVtIiAvPgogICAgICAgICAgICA8L3hzbDphdHRyaWJ1dGU+CiAgICAgICAgICAgIDx4c2w6dmFsdWUtb2Ygc2VsZWN0PSJsYXN0bmFtZSIgLz4KICAgICAgICAgIDwvcGxheWVybmFtZT4KICAgICAgICAgIDxnb2Fscz4KICAgICAgICAgICAgPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9InBvaW50cy9nb2FscyIgLz4KICAgICAgICAgIDwvZ29hbHM+CiAgICAgICAgICA8YXNzaXN0cz4KICAgICAgICAgICAgPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9InBvaW50cy9hc3Npc3RzIiAvPgogICAgICAgICAgPC9hc3Npc3RzPgogICAgICAgIDwvcm93PiAgICAgICAgCiAgICAgIDwveHNsOmlmPiAgICAgIAogICAgPC94c2w6Zm9yLWVhY2g+CiAgPC9yb3N0ZXI+CjwveHNsOnRlbXBsYXRlPgpgYGAKCiMjIyBgPHhzbDpvdXRwdXQ+YAoKYDx4c2w6b3V0cHV0PmAgZGVmaW5lcyB0aGUgb3V0cHV0IGZpbGUgZm9ybWF0IG9mIHRoZSB0YXJnZXQgZmlsZS4gVGhlIGRlZmF1bHQgaXMgWE1MIHdoaWNoIG1lYW5zIHRoYXQgWFNMVCB3aWxsIGluc2VydCB0aGUgc3RhbmRhcmQgWE1MIHByZWFtYmxlIGA8P3htbCB2ZXJzaW9uPSIxLjAiPz5gLiBJdCBtdXN0IGFwcGVhciByaWdodCBhZnRlciB0aGUgInhzbDp0cmFuc2Zvcm0iIG9yICJ4c2w6c3R5bGVzaGVldCIgZWxlbWVudCBhbmQgYmVmb3JlIGFueSAieHNsOnRlbXBsYXRlIiBvcGVyYXRvci4KClRoZSBmdWxsIHN5bnRheCBpczoKCmBgYCB4bWwKPHhzbDpvdXRwdXQKICBtZXRob2QgPSAieG1sfGh0bWx8dGV4dHxuYW1lIgogIHZlcnNpb24gPSAic3RyaW5nIgogIGVuY29kaW5nID0gInN0cmluZyIKICBvbWl0LXhtbC1kZWNsYXJhdGlvbiA9ICJ5ZXN8bm8iCiAgc3RhbmRhbG9uZSA9ICJ5ZXN8bm8iCiAgZG9jdHlwZS1wdWJsaWMgPSAic3RyaW5nIgogIGRvY3R5cGUtc3lzdGVtID0gInN0cmluZyIKICBjZGF0YS1zZWN0aW9uLWVsZW1lbnRzID0gIm5hbWVsaXN0IgogIGluZGVudCA9ICJ5ZXN8bm8iCiAgbWVkaWEtdHlwZSA9ICJzdHJpbmciLz4KYGBgCgpUaGUgZXhhbXBsZSBYU0wgZnJhZ21lbnQgYmVsb3cgZGVtb25zdHJhdGVzIGl0cyB1c2U6CgpgYGAgeG1sCjw/eG1sIHZlcnNpb249IjEuMCI/PgoKPHhzbDpzdHlsZXNoZWV0CiAgICB2ZXJzaW9uPSIxLjAiCiAgICB4bWxuczp4c2w9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvWFNML1RyYW5zZm9ybSI+CiAgICAKICA8eHNsOm91dHB1dCBtZXRob2Q9InRleHQiIAogICAgICAgICAgICAgIGVuY29kaW5nPSJVVEYtOCIgCiAgICAgICAgICAgICAgaW5kZW50PSJubyIKICAgICAgICAgICAgICBvbWl0LXhtbC1kZWNsYXJhdGlvbj0ieWVzIi8+CiAgICAKICA8eHNsOnRlbXBsYXRlIG1hdGNoPSIvL3BsYW50cyI+CiAgCiAgLi4uCmBgYAoKT21pdHRpbmcgdGhlIGRlY2xhcmF0aW9uIGFuZCBzdXBwcmVzc2luZyBpbmRlbnRhdGlvbiBtaWdodCBiZSBhcHByb3ByaWF0ZSB3aGVuIHByb2R1Y2luZyBhIEpTT04gb3IgQ1NWIGZpbGUgYXMgb3V0cHV0IHJhdGhlciB0aGFuIFhNTC4KCiMjIyBgPHhzbDp0ZXh0PmAKCmA8eHNsOnRleHQ+YCBpbnNlcnRzIHNwZWNpZmljIHVucGFyc2VkIHRleHQgaW50byB0aGUgb3V0cHV0IGZpbGUgdGhhdCBpcyBpZ25vcmVkIGJ5IFhTTFQuIFRoaXMgYWxsb3dzIGFueSB0ZXh0IHRvIGJlIHNlbnQgdG8gdGhlIG91dHB1dCBmaWxlLiBGb3IgZXhhbXBsZSwgdG8gc2VuZCBzcGVjaWFsIGNoYXJhY3RlcnMgc3VjaCBhcyBzcGFjZSBvciBuZXdsaW5lIHRvIHRoZSBvdXRwdXQgZmlsZSwgdXNlIFhNTCBlbnRpdGllcywgKmUuZy4qLCBgJiMxMDtgIGZvciBuZXdsaW5lIGFuZCBgJiMyMDtgIGZvciBzcGFjZS4gVGhpcyBjYW4gYWxzbyBiZSB1c2VkIHRvIGluY2x1ZGUgYW4gZW1iZWRkZWQgRFREIG9yIGEgbGluayB0byBhbiBleHRlcm5hbCBEVEQgaW50byB0aGUgb3V0cHV0IFhNTC4KClRoZSBmdWxsIHN5bnRheCBpczoKCmBgYCB4bWwKPHhzbDp0ZXh0IFt4bWw6d2hpdGVzcGFjZSA9ICJwcmVzZXJ2ZSJdPgogIGFueSB0ZXh0IGhlcmUKPC94c2w6dGV4dD4KYGBgCgojIyMgYDx4c2w6dmFyaWFibGU+YAoKYDx4c2w6dmFyaWFibGUgbmFtZSA9ICJ2YXIiIHNlbGVjdCA9ICJ4cGF0aC1leHByZXNzaW9uIj5gIGNyZWF0ZXMgYSB2YXJpYWJsZSB0aGF0IGNhbiBiZSBhY2Nlc3NlZCBpbiBYUGF0aCBleHByZXNzaW9ucy4gVXNlICpcJHZhck5hbWUqIHRvIGFjY2VzcyBhIHZhcmlhYmxlIHdpdGggdGhlIG5hbWUgKnZhck5hbWUqLgoKVGhlIGZ1bGwgc3ludGF4IGlzOgoKYGBgIHhtbAo8eHNsOnZhcmlhYmxlIG5hbWUgPSAidmFyaWFibGUiIAogICAgICAgICAgICAgIHNlbGVjdCA9ICJ4cGF0aC1leHByZXNzaW9uIiAvPgpgYGAKCkFuIFhTTCBmcmFnbWVudCBkZW1vbnN0cmF0aW5nIGl0cyB1c2UgaXMgYmVsb3cuIE5vdGUgdGhlIHVzZSBvZiBhbiBhZ2dyZWdhdGlvbiBmdW5jdGlvbiBpbiB0aGUgWFBhdGggZXhwcmVzc2lvbiB0byBnZXQgYSBzaW5nbGUgdmFsdWUgZm9yIHRoZSB2YWx1ZSBvZiB0aGUgdmFyaWFibGUgIm51bVJlY29yZHMiLgoKYGBgIHhtbAo8P3htbCB2ZXJzaW9uPSIxLjAiPz4KCjx4c2w6dHJhbnNmb3JtCiAgICB2ZXJzaW9uPSIxLjAiCiAgICB4bWxuczp4c2w9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvWFNML1RyYW5zZm9ybSI+CiAgICAKPHhzbDp0ZW1wbGF0ZSBtYXRjaD0iLyI+CiAgPHhzbDp2YXJpYWJsZSBuYW1lPSJudW1SZWNvcmRzIiAKICAgICAgICAgICAgICAgIHNlbGVjdD0iY291bnQocmVjb3Jkcy9yZWNvcmQpIi8+CiAgPHRyYW5zYWN0aW9ucz4KICA8eHNsOmF0dHJpYnV0ZSBuYW1lPSJudW1UeG5zIj4KICAgIDx4c2w6dmFsdWUtb2Ygc2VsZWN0PSIkbnVtUmVjb3JkcyIgLz4KICA8L3hzbDphdHRyaWJ1dGU+CgogIC4uLgpgYGAKCiMjIyBNYXRoIEV4dGVuc2lvbnMgdG8gWFNMVAoKVG8gcGVyZm9ybSBtYXRoZW1hdGljYWwgY2FsY3VsYXRpb25zIGluIFhTTFQsIHVzZSBlaXRoZXIgWFNMIDIuMCBvciBhIG1hdGggbGlicmFyeS4KCmBgYCB4bWwKPD94bWwgdmVyc2lvbj0iMS4wIj8+Cgo8eHNsOnN0eWxlc2hlZXQgdmVyc2lvbj0iMS4wIgogICAgICAgICAgICAgICAgeG1sbnM6eHNsPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L1hTTC9UcmFuc2Zvcm0iCiAgICAgICAgICAgICAgICB4bWxuczptYXRoPSJodHRwOi8vZXhzbHQub3JnL21hdGgiCiAgICAgICAgICAgICAgICBleHRlbnNpb24tZWxlbWVudC1wcmVmaXhlcz0ibWF0aCIgPiAgICAKICAgIAogPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9IihmbG9vcihtYXRoOnJhbmRvbSgpKjgpIG1vZCA4KSArIDEiIC8+CgogPHhzbDp2YXJpYWJsZSBuYW1lPSJzb21lUmVjb3JkIiAKICAgICAgICAgICAgICAgc2VsZWN0PSIoZmxvb3IobWF0aDpyYW5kb20oKSAqICRudW1SZWNvcmRzKSAKICAgICAgICAgICAgICAgICAgICAgICAgICBtb2QgJG51bVJlY29yZHMpICsgMSIvPgogICAgICAgICAgCiA8cHJvZHVjdD48eHNsOnZhbHVlLW9mIAogICAgICAgICAgIHNlbGVjdD0iLi4vcmVjb3JkWyRzb21lUmVjb3JkXS9wcm9kdWN0Ii8+PC9wcm9kdWN0PgoKLi4uCmBgYAoKIyMgVHV0b3JpYWwKClRoZSB0dXRvcmlhbCBiZWxvdyBwcm92aWRlcyBhbiBvdmVydmlldyBvZiB0aGUgY29uY2VwdHMgaW4gdGhpcyBsZXNzb24uIFRoZSBmaWxlcyBhbmQgc2xpZGUgZGVjayByZWZlcmVuY2VkIGluIHRoZSB0dXRvcmlhbCBhcmUgbGlua2VkIGJlbG93IGFuZCBzaG91bGQgYmUgZG93bmxvYWRlZCBwcmlvciB0byB3YXRjaGluZyB0aGUgdHV0b3JpYWwgc28gdGhhdCB5b3UgY2FuIGZvbGxvdyBhbG9uZy4KCmBgYHs9aHRtbH0KPGlmcmFtZSBzdHlsZT0iYm9yZGVyOiAxcHggc29saWQgIzQ2NDY0NjsiIHNyYz0iaHR0cHM6Ly9ub3J0aGVhc3Rlcm4uaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvRW1iZWQuYXNweD9pZD1kZGFiYTRlOC04MDNjLTRmN2QtYjc2MC1hY2Q2MDEzZTJiOWUmYW1wO2F1dG8lMjBsYXk9ZmFsc2UmYW1wO29mZmVydmlld2VyPWZhbHNlJmFtcDtzaG93dGl0bGU9ZmFsc2UmYW1wO3Nob3dicmFuZD1mYWxzZSZhbXA7c3RhcnQ9MCZhbXA7aW50ZXJhY3Rpdml0eT1hbGwiIHdpZHRoPSI0ODAiIGhlaWdodD0iMjcwIiBhbGxvd2Z1bGxzY3JlZW49ImFsbG93ZnVsbHNjcmVlbiIgYWxsb3c9ImF1dG9wbGF5IiBkYXRhLWV4dGVybmFsPSIxIj48L2lmcmFtZT4KYGBgCioqUmVzb3VyY2VzIGZvciBUdXRvcmlhbCoqCgotICAgKipTbGlkZSBEZWNrKio6IFtTbGlkZSBEZWNrOiBYTFNUXShzbGlkZWRlY2tzL3MtNi0zMTAtWFNMVC5wcHR4KQotICAgKipTb3VyY2UgWE1MKio6IFtUZWFtUm9zdGVycy54bWxdKFhNTC9UZWFtUm9zdGVycy54bWwpCi0gICAqKlhTTCBUcmFuc2Zvcm1hdGlvbioqOiBbUm9zdGVyczJTdGF0c1hTTC54c2xdKFhTTC9Sb3N0ZXJzMlN0YXRzWFNMLnhzbCkKLSAgICoqUiBDb2RlIEZyYWdtZW50Kio6IFtYU0xUX2luX1IuUm1kXShleGFtcGxlcy9YU0xUX2luX1IuUm1kKQoKUmlnaHQtY2xpY2sgKG9yIG9wZW4gdGhlIGNvbnRleHQgbWVudSkgYW5kIHNhdmUgdGhlIGZpbGVzIHJhdGhlciB0aGFuIGNsaWNraW5nIG9uIHRoZSBsaW5rcyB0byBvcGVuIHRoZSBmaWxlcyB3aXRoaW4geW91ciBicm93c2VyLgoKIyMgVHJhbnNmb3JtaW5nIFhNTCB0byBDU1YKClJhdGhlciB0aGFuIGdlbmVyYXRpbmcgWE1MIGFzIHRoZSByZXN1bHQgb2YgYSB0cmFuc2Zvcm1hdGlvbiwgd2UgY2FuIGdlbmVyYXRlIGFueSB0ZXh0IGZpbGUsIGluY2x1ZGluZyBTUUwgaW5zZXJ0IHN0YXRlbWVudCwgSlNPTiwgb3IgQ1NWLiBUaGUgWFNMIGJlbG93IGRlbW9uc3RyYXRlcyBob3cgdG8gZXh0cmFjdCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBYTUwgYW5kIHByb2R1Y2UgYSBDU1YuCgpgYGB7ciB0cmFuc2Zvcm1YTUwyQ1NWLCBlY2hvPUYsIGV2YWw9Rn0KbGlicmFyeSh4bWwyKQpsaWJyYXJ5KHhzbHQpCgp4bWxGbiA8LSAiWE1ML1RlYW1Sb3N0ZXJzLnhtbCIKeHNsRm4gPC0gIlhTTC9Sb3N0ZXJzMkNTVi1TdGF0c1hTTC54c2wiCnR4bkZuIDwtICJYTUwvUGxheWVyU3RhdHMuY3N2IgoKIyBTdGVwIDE6IGxvYWQgdGhlIHNvdXJjZSBYTUwKeG1sRG9jIDwtIHJlYWRfeG1sKHhtbEZuKQoKIyBTdGVwIDI6IGxvYWQgdGhlIFhTTCB0cmFuc2Zvcm1hdGlvbgp4c2xTdHlsZSA8LSByZWFkX3htbCh4c2xGbikKCiMgU3RlcCAzOiBhcHBseSB0aGUgWFNMIHRvIHRoZSBYTUwKY3N2IDwtIHhtbF94c2x0KHhtbERvYywgeHNsU3R5bGUpCgojIFN0ZXAgNDogd3JpdGUgdGhlIHJlc3VsdGluZyBDU1YgdG8gYSBmaWxlCnN0YXR1cyA8LSB3cml0ZUxpbmVzKGNzdiwgY29uID0gdHhuRm4pCmBgYAoKYGBgIHhtbAo8P3htbCB2ZXJzaW9uPSIxLjAiPz4KPHhzbDp0cmFuc2Zvcm0KICAgIHZlcnNpb249IjEuMCIKICAgIHhtbG5zOnhzbD0iaHR0cDovL3d3dy53My5vcmcvMTk5OS9YU0wvVHJhbnNmb3JtIj4KPHhzbDpvdXRwdXQgbWV0aG9kPSJ0ZXh0IiAKICAgICAgICBlbmNvZGluZz0iVVRGLTgiIAogICAgICAgIGluZGVudD0ibm8iCiAgICAgICAgb21pdC14bWwtZGVjbGFyYXRpb249InllcyIvPgo8eHNsOnRlbXBsYXRlIG1hdGNoPSIvIj4KcGxheWVyLW51bSxuYW1lLGdvYWxzLGFzc2lzdHMKPHhzbDpmb3ItZWFjaCBzZWxlY3Q9Ii8vcGxheWVyIj4KPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9Ii4vQG51bSIgLz4sPHhzbDp2YWx1ZS1vZiBzZWxlY3Q9Imxhc3RuYW1lIiAvPiw8eHNsOnZhbHVlLW9mIHNlbGVjdD0icG9pbnRzL2dvYWxzIiAvPiw8eHNsOnZhbHVlLW9mIHNlbGVjdD0icG9pbnRzL2Fzc2lzdHMiIC8+Cjx4c2w6dGV4dD4mIzEwPC94c2w6dGV4dD4KPC94c2w6Zm9yLWVhY2g+CjwveHNsOnRlbXBsYXRlPgo8L3hzbDp0cmFuc2Zvcm0+CmBgYAoKQSBmZXcgaXRlbXMgb2Ygbm90ZXdvcnRoaW5lc3MgaW4gdGhlIGFib3ZlIFhTTDoKCi0gICB0aGUgdXNlIG9mIGA8eHNsOm91dHB1dD5gIHRvIHN1cHByZXNzIHRoZSBlbWlzc2lvbiBvZiB0aGUgWE1MIHByZWFtYmxlIGFuZCBhdm9pZCBpbmRlbnRhdGlvbgotICAgdXNpbmcgYHdyaXRlTGluZXMoKWAgdG8gd3JpdGUgdGhlIENTViB0byBhIGZpbGUKLSAgIHRoZSBsYWNrIG9mIGluZGVudGF0aW9uIGluIHRoZSBYU0wgdG8gYXZvaWQgaW5kZW50YXRpb24gaW4gdGhlIHJlc3VsdCBmaWxlCi0gICB0aGUgdXNlIG9mICJ4c2w6dGV4dCIgdG8gZm9yY2UgbmV3bGluZXMgYWZ0ZXIgZWFjaCBsaW5lCgojIyBTdW1tYXJ5CgpYU0wgaXMgYSBzZXQgb2YgcnVsZXMgZXhwcmVzc2VkIGFzIFhNTCBlbGVtZW50cyB1c2VkIGZvciB0cmFuc2Zvcm1pbmcgYW5kIGZvcm1hdHRpbmcgWE1MIGRvY3VtZW50cy4gWFNMIGlzIGVzc2VudGlhbCBmb3IgdGFza3MgbGlrZSBjb252ZXJ0aW5nIFhNTCBkYXRhIGludG8gZGlmZmVyZW50IGZvcm1hdHMsIGV4dHJhY3Rpbmcgc3BlY2lmaWMgaW5mb3JtYXRpb24sIGNyZWF0aW5nIGRhdGEgZmlsZXMgKCplLmcuKiwgUERGcyksIGFuZCBtYWludGFpbmluZyBhIGNsZWFyIHNlcGFyYXRpb24gYmV0d2VlbiBjb250ZW50IGFuZCBwcmVzZW50YXRpb24uIEl0IGZvbGxvd3MgVzNDIHN0YW5kYXJkcyBhbmQgaXMgd2lkZWx5IHVzZWQgZm9yIHdvcmtpbmcgd2l0aCBYTUwgZGF0YSBpbiB2YXJpb3VzIGFwcGxpY2F0aW9ucy4KCiMjIEZpbGVzICYgUmVzb3VyY2VzCgpgYGB7ciB6aXBGaWxlcywgZWNobz1GQUxTRX0KemlwTmFtZSA9IHNwcmludGYoIkxlc3NvbkZpbGVzLSVzLSVzLnppcCIsIAogICAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwKICAgICAgICAgICAgICAgICBwYXJhbXMkbnVtYmVyKQoKdGV4dEFMaW5rID0gcGFzdGUwKCJBbGwgRmlsZXMgZm9yIExlc3NvbiAiLCAKICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LCIuIixwYXJhbXMkbnVtYmVyKQoKIyBkb3dubG9hZEZpbGVzTGluaygpIGlzIGluY2x1ZGVkIGZyb20gX2luc2VydDJEQi5SCmtuaXRyOjpyYXdfaHRtbChkb3dubG9hZEZpbGVzTGluaygiLiIsIHppcE5hbWUsIHRleHRBTGluaykpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBSZXNvdXJjZXMKCk5vbmUgeWV0LiBMZXQgdXMga25vdyBpZiB5b3UgaGF2ZSBmYXZvcml0ZXMuCgojIyBFcnJhdGEKCk5vbmUgY29sbGVjdGVkIHlldC4gTGV0IHVzIGtub3cuCgpbTGV0IHVzIGtub3ddKGh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS8yMTIxODcwNzI3ODQxNTcpe3RhcmdldD0iX2JsYW5rIn0uCg==