Introduction

Structured information objects have properties in the form of attributes. For example, instances of a Course have attributes such as title, length, credits, among others, depending on the use cases. Such information must often be externalized and stored in some structure where they can be shared and queried. A tabular organization is a common format for the externalization of structured information objects.

In this lesson, we will take a look at how tabular structures are queried using SQL. SQL has emerged as the most common way to find information objects stored in tables that meet specific criteria.

Tabular Structures

Information objects are stored in tabular form in:

  • CSV text data files
  • Excel (and other spreadsheet program) worksheets
  • Data frames in R and Python
  • Relational database tables
  • Two-Level XML documents

All tabular structures have the same format: rows with columns. Each row is an instance of an information object, while each column is an attribute value. Rows are generally numbered while columns are named.

CSV

The example below shows data in a CSV. Note the separation of columns by commas.

Product,CerealName,Manufacturer,Calories,Sodium,Fiber,Carbs,Sugars,Shelf,Year,,
1,100% Bran,Nabisco,70,130,10,5,6,3,1942,,
2,All-Bran,Kellogg,70,260,9,7,5,3,1916,,
3,All-Bran w/Extra Fiber,Kellogg,50,140,14,8,0,3,1916,,

A cell or a range of cells cannot be directly specified until the CSV is loaded into a program for processing.

Excel

The image below shows an example of a tabular structure in Excel. Columns are letters (A..Z, AA-ZZ, etc.) and rows are numbered; each intersection of a column and a row is a cell. In addition to data values, cells can also contain formulas which are algebraic expressions referencing cells or ranges of cells. A range of cells is similar to a vector, array, or a matrix in R.

Sample Tabular Structure in an Excel Worksheet
Sample Tabular Structure in an Excel Worksheet

A cell in Excel is referenced as a column/row pair, e.g., D4 references the Calories attribute of Product instance 3 and has the value 50. A range of cells can be specified by a starting and ending cell, e.g., D2:D8 is the vector of values \(\{70,70,50,110,110,110,130\}\).

Data Frames

Data frames are in-memory tabular structures common in several programming languages, including R and Python. While they can be programmatically generated, they are typically the result of loading a CSV file, Excel worksheet, or SQL query result into a data frame.

The R code fragment below loads a CSV file into a data frame, which is then shown.

df <- read.csv(file = "CerealDataCSV.csv", stringsAsFactors = F)
Table 1: Example of a tabular structure from a CSV.
Product CerealName Manufacturer Calories
1 100% Bran Nabisco 70
2 All-Bran Kellogg 70
3 All-Bran w/Extra Fiber Kellogg 50
4 Almond Delight Ralston Purina 110

Cells can be referenced in several ways: df[row-number,column-number], df[row-number,column-name]. The code block below demonstrates some ways to access specific cells and ranges.

# access a single cell
df[1,2]

# accessing a single column
head(df[,2])

# accessing several columns
df[,2:4]

# accessing rows 1 to 5 and columns 2 and 3
df[1:5,2:3]

# accessing all columns for some rows
df[1:5,]

# accessing a column by name
df[2:10,"Calories"]

# accessing entire column by name
df$Calories

# accessing the entire data frame
df[,]
# or, simply
df

Run the code and convince yourself that it works. In R, row and column specifications are ranges, or, to be precise, vectors – they do not need to be contiguous. The function c() concatenates several values into a vector.

df[c(2:4,8,10:14),c(1,3)]
##    Product   Manufacturer
## 2        2        Kellogg
## 3        3        Kellogg
## 4        4 Ralston Purina
## 8        8 Ralston Purina
## 10      10         Quaker
## 11      11  General Mills
## 12      12  General Mills
## 13      13  General Mills
## 14      14  General Mills

The above access expressions can be used on the left or right side, which means they can also be used to modify a data frame. An example is shown below:

# replace values in a column
df[,2] <- 0

For more information on working with data frames in R, see Lesson 6.103 Working with Vectors and Data Frames in R and for more information on reading files into data frames, see Lesson 6.106 Import Data into R from CSV, TSV, and Excel Files

XML

In some situations, XML documents where elements directly underneath the root represent rows and the child elements under the rows represents columns are used as an alternative to CSV. An example is shown below where each element is a row and the columns are , , and .

<?xml version="1.0"?>

<document>
  <row>
    <Girth>8.3</Girth>
    <Height>70</Height>
    <Volume>10.3</Volume>
  </row>
  <row>
    <Girth>8.6</Girth>
    <Height>65</Height>
    <Volume>10.3</Volume>
  </row>
  ...
</document>

Most commonly, such XML files are converted to a CSV or read into a programming language as a table. For example, in R, the function xmlToDataFrame() is often used to read such a specially-formatted XML into a data frame.

The code below demonstrates how to load a 2-level XML document and convert the values to the appropriate numeric data type:

library(XML)

fn <- "treedata2L.xml"

# load from local file
df <- xmlToDataFrame(fn, stringsAsFactors = F)

df$Girth <- as.numeric(df$Girth)
df$Height <- as.numeric(df$Height)
df$Volume <- as.numeric(df$Volume)

head(df, 3)
##   Girth Height Volume
## 1   8.3     70   10.3
## 2   8.6     65   10.3
## 3   8.8     63   10.2

Of course, this only works if the XML has this particular structure where each element directly under the root is a row and the child elements under each row are in the exact same order and represent columns. It does not work for more complex XML element structures. In that case, XPath or node traversal would be needed to read the data – and the data would not longer be a tabular structure but rather be a complex object structure.

For more information on using xmlToDataFrame(), see Lesson 6.323 Load Simple XML into Dataframe in R using xmlToDataFrame.

Relational Model of Data

The relational model is an approach to storing data that is tabular and that connects data in one table to data in another table. It is the foundation of relational databases, but the tables do not have to be in a database for the data to be “relational”.

Relations

The relational model is based on set theory and each table is more formally called a relation. A relation has attributes; the number of attributes that a relation has is called its degree. Fundamentally, a relation is a set of tuples. The cardinality of a relation is the number of tuples in the relation.

For example, a relational model representing a learning management system might have the relation courses which might have attributes course number, title, and credits, and it might the tuples shown below:

courses(
  {'IS2000','Principles of Information Science', 4},
  {'CS5200','Database Management Systems', 4},
  {'CS5380','Project Management with Scrum', 2},
  {'DA5021','Big Data', 1}
)

The relation is defined as follows:

\(courses(number,title,credits)\).

Let TEXT be the domain of all character strings. The attribute domains for the relation courses would then be

\(number,title \in TEXT\)

\(credits \in \mathbb{N}\).

The above relation has a degree of 3 and cardinality of 4. It is a set of four tuples. {‘CS5200’,‘Database Management Systems’, 4} is one such tuple.

In tabular form, the above is a table with three columns and four rows.

number title credits
IS2000 Principles of Information Science 4
CS5200 Database Management Systems 4
CS5380 Project Management with Scrum 2
DA5021 Big Data 1

Primary Key

Specific tuples are identified through a unique value of one attribute or a combination of several attributes. Tuples must be unique – this is a requirement of the relational model. The attribute (or combination of attributes) that uniquely identifies each tuple is referred to as the primary key. In the above example, course number would be the primary key as it is, by definition in the business domain, unique, i.e., no two different courses can have the same course number.

If there is no natural unique key or the key is too complex, then we generally “invent” one; a so-called artificial key. For example, for the courses relation we might wish to add a unique identifier column and assign a unique value for each course and use that as the primary key rather than the course number. Incidentally, this will make course number a so-called alternate key.

Here is what the new relation courses would look like:

\(courses(\underline{cid},number,title,credits)\).

courses(
  {4453,'IS2000','Principles of Information Science', 4},
  {6654,'CS5200','Database Management Systems', 4},
  {1009,'CS5380','Project Management with Scrum', 2},
  {4198,'DA5021','Big Data', 1}
)

The values for cid are “made up” – they could have been sequential numbers too, e.g., 1, 2, 3, and 4. The actual values are meaningless as long as they are unique. In a relation definition, primary key attributes are often underlined so they are easier to recognize.

Attributes

The set or range of values from which the value of an attribute can be drawn is called the attribute’s domain. In the above example, the domain for course number is TEXT and includes all possible course numbers that fit the pattern AANNNN, where AA is the course area, such as IS (Information Science) or CY (Cyber Security) and \(NNNN\) is a four digit course number. The domain for the credits attribute is all integers greater than 0 and no more than 5 – or at least that is the domain at Northeastern University.

More formally, we could express the domain for credits as a set using set generating notation:

\(courses \in \{x : x \gt 0 \land x \le 5\}\).

Naturally, different business areas will have different domains for attributes – they are a business rule.

Linked Relations

To show that relations are generally not by themselves but rather are linked, let’s say that we want to track the instructor who teaches a course. We might decide to extend the relation courses with an additional attribute for instructor:

\(courses(\underline{cid},number,title,credits,instructor)\).

An instance of that relation might now be:

courses(
  {4453, 'IS2000','Principles of Information Science', 4, 'Jose Roja'},
  {6654, 'CS5200','Database Management Systems', 4, 'Anneliese Frack'},
  {1009, 'CS5380','Project Management with Scrum', 2, 'Rahul Prahit'},
  {4198, 'DA5021','Big Data', 1, 'Xi Wang'}
)

Simple… but what if the same instructor teaches more than one course? Then we would have repetition. There would be even more information repeated if we tracked the instructor’s department, rank, and highest degree earned. While that is commonly done in a CSV or a data frame, it is not appropriate for a relational model where repetition is frowned upon as it violates so-called normal forms. A better approach would be to create another relation to track instructor-specific information: \(instructors(\underline{iid}, name,department,rank,degree)\). Note that we, once again, created an artificial key attribute – in this case there is no natural key as instructor name cannot be assumed to be unique.

instructors(
  {101, 'Jose Roja', 'Computer Science', 'Associate', 'PhD'},
  {102, 'Xi Wang', 'Computer Science', 'Full', 'PhD'},
  {901, 'Anneliese Frack', NULL, 'Adjunct', 'MSc'},
  {301, 'Rahul Prahit', 'Industrial Engineering', 'Research', 'DSc'}
  {601, 'Elaine Campbell', 'Education', 'Assistant', 'EdD'}
)

There is still some repetition when instructors are in the same department or have the same rank, but we will ignore that for now rather than adding a departments relation and a lookup relation for rank. Also note that we chose not to associate part-time adjunct instructors with a department. This is not a universal rule, but rather a rule that an analyst might have uncovered during information analysis.

To track which instructor teaches which course, we would first remove all instructor information from the courses relation and add a new attribute that has as a value the primary key of the instructor who teaches the course. Such as attribute in courses is referred to as a foreign key attribute.

The updated \(courses\) relation is

\(courses(\underline{cid},number,title,credits,\boldsymbol{iid})\).

Notice how the foreign key attribute is bolded (or sometimes italicized) to emphasize it.

An instance is shown below:

courses(
  {4453, 'IS2000','Principles of Information Science', 4, 101},
  {6654, 'CS5200','Database Management Systems', 4, 901},
  {1009, 'CS5380','Project Management with Scrum', 2, 301},
  {4198, 'DA5021','Big Data', 1, 102}
)

Now, if an instructor teaches more than one course, we would not need to repeat the instructor information.

courses(
  {4453, 'IS2000','Principles of Information Science', 4, 101},
  {6654, 'CS5200','Database Management Systems', 4, 901},
  {1009, 'CS5380','Project Management with Scrum', 2, 301},
  {4198, 'DA5021','Big Data', 1, 102},
  {8890, 'DA5029','Data Analytics Project', 1, 102},
)

Relational Operations

There are several important operations defined on a relational model:

  • selection
  • projection
  • rename
  • aggregation
  • grouping
  • equi-join

These are defined in a relational algebra. The full set of operations of the relational algebra are beyond the scope of this lesson. If you have an interest in learning more, consult Lesson 60.502 Relational Algebra.

Relational algebra is an important query abstraction mechanism and is often used to express queries without resorting to a specific query language such as SQL.

Relations as Sets

In the relational algebra, relations are treated as (mathematical) sets and thus cannot, by definition of a set, contain duplicates.

Selection

The selection operation is denoted with the letter \(\sigma\) and is used to choose a subset of tuples that satisfy a selection condition. The selection condition is expressed as a Boolean logical operation. It acts as a filter on the tuples of a relation. An alternate way of looking at selection is that it specifies conditions under which a tuple may belong to a relation. In more practical terms, we can look at it as selecting rows from a table that meet certain “search” criteria.

We will use the (simpler and single) relation

\(courses(number,title,credits)\)

with the instance shows below, for some of the upcoming examples:

courses(
  {'IS2000','Principles of Information Science', 4},
  {'CS5200','Database Management Systems', 4},
  {'CS5380','Project Management with Scrum', 2},
  {'DA5021','Big Data', 1}
)

Using the course relation defined above, to select all courses that are less than four credits, we would use the following operation:

\(\sigma_{credits \lt 4}(courses)\)

In general, the selection operation on any relation \(R\) is denoted by

\(\sigma_{<condition>}(R)\)

where is a Boolean logical expression of one or more clauses. An example with a compound Boolean condition is shown below:

\(\sigma_{(credits \geq 2) \land (credits \leq 4)}(courses)\)

The selection operator is unary – it applies to a single relation. The condition is applied to each tuple and can therefore only involve a single tuple. The degree of the resultant relation is the same as the relation to which selection is applied. The number of tuples of the resultant relation (its cardinality) is always less than or equal to the relation to which selection is applied. That is \(|\sigma_{C}(R)|\leq |R|\). The percentage of tuples selected by the selection operation is called the selection’s selectivity.

Selection operations can be cascaded since the result of a selection is a relation.

Selection is commutative, i.e.,

\(\sigma_{<C_1>}(\sigma_{<C_2>}(R)) = \sigma_{<C_2>}(\sigma_{<C_1>}(R))\)

A cascaded set of selections can always be transformed into a conjunction:

\(\sigma_{<C_1>}(\sigma_{<C_2>}(R)) = \sigma_{<C_1> \land <C_2>}(R)\)

Projection

The project operation is denoted with the Greek letter \(\pi\) and it selects specific attributes from a relation. It can be thought of as a vertical partitioning of a relation into two relations where one partition contains the attributes of interest and the other partition is discarded. The example below extracts the attributes number and title from the relation courses.

\(\pi_{number,title}(courses)\)

In general, the projection operation on any relation \(R\) is denoted by

\(\pi_{<attribute list>}(R)\)

The result of a projection is a relation having a degree equal to the number of attributes in the <attribute list> and is always less than or equal to the degree of \(R\). Similar to selection, the projection operator is also unary – it applies to a single relation. The number of tuples of the resultant relation (its cardinality) is always equal to the relation to which projection is applied.

The generalized form of projection allows functions on attributes to be included and is expressed as:

\(\pi_{F_1,F_2,...,F_j}(R)\),

where each \(F_k\) is a function over the attributes in the relation \(R\) and may include operations and scalar values.

For example,

\(\pi_{upper(title),credits/15}(courses)\),

where \(upper()\) should be interpreted a user defined function (in this example returning its argument in all upper case letters).

Rename

While relational algebra expressions can be cascaded (i.e., nested), this results is potentially long and unwieldy expressions. Alternatively, expressions can be decomposed into a sequence of individual expressions and intermediary resultant relations and attributes can be assigned new names using the rename operation. The rename operation is denoted with the Greek letter \(\rho\). Rename is a unary operation and takes the general form:

\(\rho_{S(B_1,B_2,\ldots,B_n)(R)}\)

This renames the relation \(R\) to \(S\) and renames each attribute \(A_i\) of \(R\) to \(B_i\).

The example below, selects the number and title of all courses with fewer than four credits and renames the resultant relation to shortCourses and the attributes to code and name.

\(\rho_{shortCourses(code,name)}(\pi_{credits,title}(\sigma_{credits \lt 4}(courses)))\)

Expressing the above as a series of relational algebra expression, we would get:

\(\rho_{E}(\sigma_{credits \lt 4}(courses))\)

\(\rho_{S}(\pi_{number,title}(E))\)

\(\rho_{shortCourses(code,name)}(S)\)

Both of the approaches are correct and the use is generally a personal preference. Often, using rename operations can make a complex expression simpler to follow.

Aggregation

An aggregate function operation1 is denoted with

\(\mathfrak{F}_{<functions>}(R)\).

The <functions> are one or more of

  • COUNT (number of occurrences),
  • SUM (summation),
  • AVG (average or mean),
  • MIN (minimum), and
  • MAX (maximum) or
  • any user-defined function definable over an attribute.

For example, the expression below defines a resultant relation that has two attributes (columns) that are the average and maximum credits for courses.

\(\mathfrak{F}_{<AVG(credits),MAX(credits)>}(courses)\)

The result of an aggregate function is, like every relational operation, a relation, albeit, in some cases, a relation of degree 1 and cardinality 1.

A function can also be an attribute in which case it is the “identify function” which is just the value of the attribute.

Grouping

Grouping is a common operation in which equal values of a column are “grouped” or “batched” and the resultant relation only contains the groups. A grouping operation is denoted with \({}_{<attribute-list>}\mathfrak{G}(R)\)

The result of an grouping function is always a relation, albeit, in some cases, a relation of degree 1 and cardinality of 1.

For example, the expression below defines a resultant relation that has one attribute (column) that are the different credits awarded to courses.

\({}_{<credits>}\mathfrak{G}(courses)\)

In another example, the expression below defines a resultant relation that has two attributes (columns) that are the credits and number of courses for each level of credits.

\({}_{<credits>}\mathfrak{G}_{<credits, COUNT>}(courses)\)

Equi-Join

The equi-join (also often called an inner join) operation is the most common form of a multi-relation operation and is often simply called a join. It is denoted by the symbol \(\bowtie\), is used to combine related tuples from two relations in a cross join (Cartesian product).

An equi-join has the general form:

\({R_1}{\bowtie}_{<join\;condition>}{R_2}\)

The \(<join\;condition>\) are the matching attributes in the two operand relations. For example, assume

\(R_1(pk_{R_1},a_1,a_2,fk)\), where \(fk\) is a foreign key referencing \(pk_{R_2}\), and \(R_2(pk_{R_2},a_1,a_2)\),

then an equi-join of the two relations is expressed as

\({R_1}{\bowtie}_{{R_1}.fk={R_2}.{pk_{R_2}}}{R_2}\)

The degree of a new relation resulting from an equi-join is \(degree(R_1) + degree(R_2)\) and the cardinality is at most \(|R_1| \cdot |R_2|\).

An equi-join matches tuples of the two relations based on equality of the attributes in the join condition.

In order to combine related tuples, one of the tables must have the primary key of the other as an attribute (i.e., as a foreign key). In a one-to-one relationship, it does not matter which relation has the foreign key; however, in a one-to-many relationship, the relation on the “side of the one” has the foreign key to the “side of the many”.

There are several other types of join, including natural, outer, and theta joins, but those are beyond the scope of this lesson.

To demonstrate this simple, but often confusing operation, let’s write a query that finds the name of a course and the name of the instructor who teaches the course. We will rely on the previous definitions for

\(courses(\underline{cid},number,title,credits,\boldsymbol{iid})\)

and

\(instructors(\underline{iid}, name,department,rank,degree)\).

The two relation instances are repeated below for convenience:

instructors(
  {101, 'Jose Roja', 'Computer Science', 'Associate', 'PhD'},
  {102, 'Xi Wang', 'Computer Science', 'Full', 'PhD'},
  {901, 'Anneliese Frack', NULL, 'Adjunct', 'MSc'},
  {301, 'Rahul Prahit', 'Industrial Engineering', 'Research', 'DSc'}
  {601, 'Elaine Campbell', 'Education', 'Assistant', 'EdD'}
)

courses(
  {4453, 'IS2000','Principles of Information Science', 4, 101},
  {6654, 'CS5200','Database Management Systems', 4, 901},
  {1009, 'CS5380','Project Management with Scrum', 2, 301},
  {4198, 'DA5021','Big Data', 1, 102},
  {8890, 'DA5029','Data Analytics Project', 1, 102},
)

The equi-join operation is:

\({courses}{\bowtie}_{courses.iid=instructors.iid}{instructors}\)

The foreign key in courses is iid and it “points to” or “references” the instructor who teaches the course in the instructors relation. So, that will need to be our join condition. It does not matter whether we put courses or instructors on the left or right side of the join operation. An equi-join is commutative.

We need to “scope” the names of the attributes as they are the same in both relations. We could have simplified the expression by first renaming the courses and instructors relations. This is left as an exercise for the reader – try it out.

Now the above gets as a relation that contains all attributes from courses and instructors. To get only the course name and instructor name, we need to add a projection. We will also take advantage a rename operation so we can split the expression.

\(\rho_{cni}({(courses)}{\bowtie}_{courses.iid=instructors.iid}{(instructors)})\)

\(\pi_{title,name}(cni)\)

There are several other ways that this query could have been written. Can you come up with one other way of writing this? Could you have projected attributes first before joining?

Let’s add one more wrinkle and say that we only want the four-credit courses and their instructors. Then we would need to add a selection before the projection.

\(\rho_{c}({(courses)}{\bowtie}_{courses.iid=instructors.iid}{(instructors)})\)

\(\rho_{s}(\sigma_{credits = 4}(c))\)

\(\pi_{title,name}(s)\)

Exercise: Try writing the above set of expressions as a single expression? Is is simpler? Easier or more difficult to “parse”?

Entity-Relationship Diagrams

Entity-Relationship Diagrams (ERD) are used to visualize relational data models. There are several notations that are in common use:

  • Information Engineering Notation (aka, Crow’s Foot Notation)
  • Chen Notation (named after Peter Chen, its creator)
  • IDEF1X (developed by the US Military)
  • UML Class Diagrams

We will take a closer look at the first one, as it is the most commonly used in practice, albeit being an older notation developed in the early 1980s. It is being slowly supplanted by the UML Class Diagram, though.

For more information on UML Class Diagrams, see Lesson 30.152 Domain Modeling with UML Class Diagrams.

IE/Crow’s Feet Notation

In the IE notation, entities (represented as relations in the model and tables in a database or data frames in R/Python and identical to classes in UML) are rounded-corner rectangles with two compartments: the entity name in the first compartment and the attributes in the second compartment. The second compartment is generally divided into three or four columns: modifier, name, data type (domain), and default value. The most common modifiers are PK for primary key and FK for foreign key. Primary keys are linked to foreign keys with a line – and the line emanates from the FK and ends at the PK row, unlike in UML where the line connects two classes. In an ERD it connects attributes.

The diagram below, created in LucidChart, shows a simple IE (Crow’s Feet) ERD with three entities and three relationships (ignore the relationships for now; they will be explain shortly):

ERD in Information Engineering (Crow’s Foot) Notation
ERD in Information Engineering (Crow’s Foot) Notation

As an aside, many models use singular nouns for entities while many relational databases use the entity name in the plural. It is a matter of preference. Relationships are easier to follow and explain if the entities are in the singular.

Whether an attribute name is bold or not, entities are shaded or filled, and whether the attributes are banded has no meaning; it is only intended to improve readability.

Some analysts like to underline primary key attributes and italicize foreign keys, which is consistent with relational schema definitions.

Relationships

Relationships connect entities. In a relational model, relationships can only be one-to-one or one-to-many, but not many-to-many as the latter type of relationships cannot be implemented using tables with fixed numbers of columns or atomic values for the attributes.

A relationship line can be augmented with an optional label to explain the relationship using “business terminology”. It can help explain the model and should be used.

The diagram below shows three entities and two relationships. The relationships have a label and the ends indicate the multiplicity (explained in next section).

ERD in Information Engineering (Crow’s Foot) Notation
ERD in Information Engineering (Crow’s Foot) Notation

Multiplicity

The multiplicity of a relationship is indicated with symbols at the end of the relationship line. The diagram below summarizes the symbols and their semantics.

ERD Multiplicity Symbols
ERD Multiplicity Symbols

In (I), there is no multiplicity defined; most likely because it is not yet known. in (II), we show a multiplicity of 1 on one side and many of the other size – the triangle, crow’s foot like shape indicates “many” with no particular bound. A circle indicates 0, or an optional relationship. So, (III) shows that it is 0 or more on one side and 0 or 1 on the other.

There is no mechanism to show any multiplicity bound other than 0, 1, and many. So, a UML Class Diagram showing a multiplicity of 5..10 would need to be shown as “many” in an IE ERD.

Reading the multiplicity is done by fixing one instance of one entity and mapping it to the other entity. So, in the diagram below, we visualize the facts:

  • “An (one) author writes many lessons, possibly none, i.e., there are some authors who do not write any lessons.”

  • “A (one) lesson is written by no more than one author, although some lessons do not have an author.”

If a relationship is 0..1 then it means that the foreign key can be NULL.

ERD Relationship Example I
ERD Relationship Example I

Exercise: Go back to the full diagram and state sentences about the relationship between Course and Lesson.

Junction Entities

If there is a many to many relationship then it must be resolved by adding a junction or association entity. For example, if we allow a lesson to be in more than one course, then we would have a many-to-many relationship between Course and Lesson and that would require the introduction of some kind of junction table. In the example below, we chose to name that table ContentMap.

Adding Foreign Keys

The foreign key is a “one-to-many” relationship is added to the table on the side of the “many” and it contains the value of the primary key is the side of the “one”. Sample tables can help clarify where they should be placed.

Implementing One-to-Many Relationships with FK/PK Links
Implementing One-to-Many Relationships with FK/PK Links

The quick bite tutorial below illustrates how to resolve one-to-many relationships with FK/PK links.

SQL

As we saw, the relational model has a mechanism in which to express “queries” on relations called relational algebra, but that is not useful in practice. Instead, data is extracted from tables in a relational model (i.e., tables) using the industry-standard query language SQL. This section reviews the key operations of SQL for retrieving data from one or more tables. In the context of SQL, we generally prefer the term table over relation.

One noteworthy item is that tables in relational databases are not, in fact, relations and therefore several useful theorems about the relational algebra do not hold in the SQL counterpart. The reason is that the SQL table model is a bag (multiset), rather than a set which cannot contain duplicates and all tuples in the set must be unique.

Databases

Tabular data can be stored in in-memory data frames created from reading CSV, Excel, or XML files. In addition, tables can be stored in a database which is preferable when there are many tables and the tables are larger than would fit into memory or when multiple applications need access to the same data.

There are many relational database in use today, including SQLite, MySQL, MariaDB, Oracle, Microsoft SQL Server, Informix, DB2, JavaDB, among many others. The aforementioned are all relational databases storing data in tables. Organizations also make use of non-relational databases that store data in a different structure: hierarchical, key/value, columnar, or as documents. These types of databases include CouchDB, MongoDB, Neo4J, among many others, and are also often called NoSQL databases as the primary query language is not SQL.

SQL in R

In R, there are two ways to use SQL for retrieving data from tables depending on where the tables are: tables in memory versus tables in a database. Fortunately, it does not matter where the tables are, we can use SQL to retrieve data from them. However, we need to know where the tables are stored so that we use the correct set of functions from the right package.

  • sqldf: A package containing functions to retrieve data using SQL from in-memory data frames. This package is actually built on top of the RSQLite package.
  • RSQLite: A package containing functions to retrieve data using SQL from tables in a SQLite database.

Of course, if we were to use a different database such as MySQL, we would need to use a different package (e.g., RMySQL) but could still use the same SQL queries, making SQL a more or less universal query language.

Before we dive into details, note that all table names and also all data frames used with sqldf cannot contain a period (.) or, if they do, they must be escaped by enclosing the table or data frame name in backticks, e.g.,, `df.table` and not simply df.table as the . is interpreted as a scope operator, i.e., df.table means to SQL the table column in the table df.

sqldf

sqldf is a package that allows data frames to be queried with SQL as if they were tables in a database. It allows a programmer or data analyst to use SQL to access, process, search, and aggregate data in data frames.

Many queries, while generally doable in Base R, are often simpler with a SQL query – albeit a bit slower, but that reduction in performance is often not perceptible. Underneath the hood, the sqldf package actually loads the data frame into an in-memory SQLite database.

sqldf is primarily used to:

  • summarize of data in data frames
  • harmonize data access via SQL for all tabular data
  • import parts of a CSV

It is also useful for learning SQL without being concerned about setting up a separate relational database.

Lesson 6.330 Querying Data Frames in R with sqldf explains in more detail how to use **sqldf* to query data frames in R.

Example Relational Model

In the explanation below, we will use a set of tables from the package nycflights13 created by Hadley Wickham of RStudio. The tables in that package are actually tibbles. Tibbles are a type of data frame created by Wickham as part of his tidyverse package. However, we will treat the tibbles as data frames and ignore the fact that they are “tibbles”.

library(nycflights13)

The diagram below illustrates the data model for the tables in that package, expressed as an Entity Relational Diagram (ERD) in the Information Engineering (aka “Crow’s Foot”) notation. This notation is common for relational data models, although a UML Class Diagram could also have been used. Irrelevant attributes (columns) have been omitted from the diagram for the sake of readability.

Explore the data model. Inspect each of the data frames.

Surrogate Artificial Primary Keys

One item of interesting is that some of the data frames have composite primary keys (i.e., primary keys that consist of several columns). For example, the primary key to uniquely identify a row in flights is the combination of the carrier, flight [number], year, month, and day. This can be very inconvenient when we want to combine data from multiple data frames with joins. So, we generally add a new column, an identifier, that is a single number that is unique and use that as the primary key instead. Such “invented or artificial” primary keys are also often called a surrogate key. They are quite common.

A simple way to do this is to add a column that is a sequence number. The code below does this – note the use of a starting number so that it is a bit easier to distinguish between keys of different data frames; not necessary but convenient.

# add artificial key to "flights"
flights$fid <- seq(1000:(1000+nrow(flights)-1))

# add artificial key to "weather"
weather$wid <- seq(1:(nrow(weather)))

We now have two possible primary keys for the data frames weather and flights: the combination of the columns and the new surrogate ID. We now need to designate one the primary key, making the other an alternate key. Each would be called a “candidate key”. One of the candidate keys, the compound key, is a natural key as it comes from data in the business, while the other is an artificial key.

Getting Started with SQL

Note that SQL is not case sensitive, so SELECT and select are equally valid. By convention, SQL keywords are written in upper case. The semicolon is generally required by some ad hoc query tools or when used in a script. We generally include it for compatibility and due to convention.

The result of every SQL statement is a table which means that a query can be “queried”. In other words, a query can be used wherever a table is required and so we can formulate subqueries.

Before we can use sqldf, remember to load the library – and install if not yet installed.

In the sqldf::sqldf() function, tables are presumed to be the identifiers for data frames that have been loaded or created. So, in the SQL statement that is passed sqldf::sqldf() below, “airlines” refers to the data frame (well, tibble, to be more precise, but that doesn’t matter) airlines.

The SQL statement can be enclosed either in single ’ or double quotes “. Using one or the other is useful if we want to the the other within the query.

library(sqldf)

Retrieving Rows: SELECT

The SELECT statement is used to retrieve rows that meet certain conditions. It has the general form:

SELECT {columns | * | expression | literal} 
  FROM {tables}
 [WHERE {conditions}]
 [GROUP BY {column}]
 [HAVING {group criteria}]
 [ORDER BY {columns}]
 [LIMIT number];

Retrieve All Rows

The simplest SELECT query retrieves all rows from a single table. The ‘*’ specifies all columns in a table. In the example below, we get the first five rows from the table airlines by using the LIMIT keyword; if we do not specify a limit then we would get all rows which might be very large.

library(sqldf)

sqldf::sqldf("SELECT * FROM airlines LIMIT 5")
##   carrier                   name
## 1      9E      Endeavor Air Inc.
## 2      AA American Airlines Inc.
## 3      AS   Alaska Airlines Inc.
## 4      B6        JetBlue Airways
## 5      DL   Delta Air Lines Inc.

If we need to process the result further, then we would assign the return value of sqldf::sqldf() to a variable.

rs <- sqldf::sqldf("SELECT * FROM airlines LIMIT 5")

Since the result is a data frame, we can then apply R functions to that data frame.

WARNING. If your data frame identifier name contains a period (common way to name data frames in R as period is a legal identifier character in R), then you must escape the data frame when used in sqldf with backticks. Let’s say the data frame was df.airlines and not airlines. Not enclosing the data frame in backticks changes its meaning for SQL to: access column airlines in the table df and since that does not exist, it’ll throw a SQL error.

The code fragment below illustrates the correct way to do this.

rs <- sqldf::sqldf("SELECT * FROM `df.airlines` LIMIT 5")

The same approach of using backticks also applies to column names containing periods.

Retrieve Specific Columns

Rather than using ‘*’ to get all columns, we can specify a subset of the columns by name.

rs <- sqldf::sqldf("SELECT carrier FROM airlines LIMIT 5")
print(rs)
##   carrier
## 1      9E
## 2      AA
## 3      AS
## 4      B6
## 5      DL

Ordering Results: ORDER BY

The rows are returned in some order – and likely not the order in which they were inserted or might have appeared in another query. So, never rely on the order unless you specifically specify an ordering with ORDER BY. An optional sorting direction can be added; DESC or descending and ASC for ascending.

rs <- sqldf::sqldf("SELECT name FROM airlines ORDER BY name DESC LIMIT 5")
print(rs)
##                     name
## 1         Virgin America
## 2  United Air Lines Inc.
## 3        US Airways Inc.
## 4 Southwest Airlines Co.
## 5  SkyWest Airlines Inc.

Rename Columns

By default, the names of the columns in the result table are the same as the source table, but they can be renamed using AS.

rs <- sqldf::sqldf("SELECT name AS airline FROM airlines ORDER BY name LIMIT 5")
print(rs)
##                       airline
## 1 AirTran Airways Corporation
## 2        Alaska Airlines Inc.
## 3      American Airlines Inc.
## 4        Delta Air Lines Inc.
## 5           Endeavor Air Inc.

Expressions

The SELECT statement can include expressions as a “column”. The expression is often renamed with an AS. In the example below, we create a new column “hr_delay” as the departure delay expressed in hours rather than minutes, rounding to two significant digits – not particularly useful but instructive.

We are also using the paste0() function to break up the SQL statement into multiple lines so it is easier to read. R does not allow line breaks in strings like other languages.

sql <- paste0(
  "SELECT carrier, flight, round(dep_delay / 60, 2) AS hr_delay",
  "  FROM flights",
  " LIMIT 5;")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier flight hr_delay
## 1      UA   1545     0.03
## 2      UA   1714     0.07
## 3      AA   1141     0.03
## 4      B6    725    -0.02
## 5      DL    461    -0.10

Specific Rows with WHERE

Adding a WHERE clause to a query retrieves specific rows that meet the conditions of the WHERE clause. It is equivalent to a relational selection operation.

The clause is a Boolean expression and can contain AND, OR, and NOT plus logical operations include equality =, less than <, less than or equal <=, greater than >, greater than or equal >=, and not equal <>.

Simple Conditions

sql <- paste0(
  "SELECT carrier, name",
  "  FROM airlines",
  " WHERE carrier = 'UA'")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier                  name
## 1      UA United Air Lines Inc.

Boolean Clauses

Complex selection filters can be created using Boolean expressions containing AND, OR, and NOT. Using parenthesis helps enforce precedence.

sql <- paste0(
  "SELECT carrier, name",
  "  FROM airlines",
  " WHERE carrier = 'UA'",
  "    OR carrier = 'AA'")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier                   name
## 1      AA American Airlines Inc.
## 2      UA  United Air Lines Inc.

Testing for NULL

To test if the value is NULL requires using IS NULL or IS NOT NULL. Using = or <> results in an error as NULL is not a value. NULL indicates the absence of a value and is used for “missing” values – it is the equivalent of NA in R and reading a table into a data frame would change all NULL values to NA.

sql <- paste0(
  "SELECT origin, year, day, month",
  "  FROM weather",
  " WHERE wind_gust IS NULL",
  " LIMIT 10")
rs <- sqldf::sqldf(sql)
print(rs)
##    origin year day month
## 1     EWR 2013   1     1
## 2     EWR 2013   1     1
## 3     EWR 2013   1     1
## 4     EWR 2013   1     1
## 5     EWR 2013   1     1
## 6     EWR 2013   1     1
## 7     EWR 2013   1     1
## 8     EWR 2013   1     1
## 9     EWR 2013   1     1
## 10    EWR 2013   1     1

Expressions

To use an expression in the WHERE clause requires that the expression be aliased. In the example below, we want to find all flights that have more than a 15 hour delay. We add a column that is an alias for an expression that converts the departure delay from minutes to hours and then use that alias in the WHERE clause. And, yes, we do realize that we could have just tested for /> 900 but then we couldn’t have demonstrated how to use expressions in WHERE clauses…

The SQL statement below also adds an ORDER BY clause to sort the results by departure delay.

sql <- paste0(
  "SELECT carrier, flight, ",
  "       round((dep_delay/60.0),1) as depHr",
  "  FROM flights",
  " WHERE depHr > 15",
  " ORDER BY depHr DESC")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier flight depHr
## 1      HA     51  21.7
## 2      MQ   3535  19.0
## 3      MQ   3695  18.8
## 4      AA    177  16.9
## 5      MQ   3075  16.8
## 6      DL   2391  16.0
## 7      DL   2119  15.2

Set Membership

The set membership operator IN tests if a value is one of several values in a set. It is often simpler than using multiple = with OR.

sql <- paste0(
  "SELECT carrier, name",
  "  FROM airlines",
  " WHERE carrier IN ('UA','AA','LH','VX')")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier                   name
## 1      AA American Airlines Inc.
## 2      UA  United Air Lines Inc.
## 3      VX         Virgin America

Rather than testing if a value is in a set, it is often necessary to test if it is not in the set. So, in the query below, we would get all airlines that are not in the specified set, i.e., everything else.

sql <- paste0(
  "SELECT carrier, name",
  "  FROM airlines",
  " WHERE carrier NOT IN ('UA','AA','LH','VX')")
rs <- sqldf::sqldf(sql)
print(rs)
##    carrier                        name
## 1       9E           Endeavor Air Inc.
## 2       AS        Alaska Airlines Inc.
## 3       B6             JetBlue Airways
## 4       DL        Delta Air Lines Inc.
## 5       EV    ExpressJet Airlines Inc.
## 6       F9      Frontier Airlines Inc.
## 7       FL AirTran Airways Corporation
## 8       HA      Hawaiian Airlines Inc.
## 9       MQ                   Envoy Air
## 10      OO       SkyWest Airlines Inc.
## 11      US             US Airways Inc.
## 12      WN      Southwest Airlines Co.
## 13      YV          Mesa Airlines Inc.

Set membership is often combined with subqueries where the subquery is the set of values.

Dates

Dates are unique data types in SQL. The two most common date related data types are DATE and DATETIME. The data type DATE are values in the form “YYYY-MM-DD” while DATETIME are values in the form of “YYYY-MM-DD HH:MI:SS”.

To check if a date column is a particular date, you can use = and to check if it is within a date range you can use BETWEEN.

Our data does not have any date data types but if we had a column flightdate in flights and want all flights in a date range, we could use the query below.

sql <- paste0(
  "SELECT carrier, flight, flightdate",
  "  FROM flights",
  " WHERE flightdate BETWEEN '2013-01-01' AND '2013-01-31'")
rs <- sqldf::sqldf(sql)
print(rs)

Unique Rows

It is possible that a query results in duplicate rows. The keyword DISTINCT eliminates duplicates in the result set.

sql <- paste0(
  "SELECT DISTINCT carrier, flight",
  "  FROM flights LIMIT 5")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier flight
## 1      UA   1545
## 2      UA   1714
## 3      AA   1141
## 4      B6    725
## 5      DL    461

Try running the above query without DISTINCT and observe the difference. How many rows does the result have when you remove DISTINCT?

Aggregation

sql <- paste0(
  "SELECT avg(dep_delay) AS avg_delay, max(dep_delay) AS max_delay",
  "  FROM flights")
rs <- sqldf::sqldf(sql)
print(rs)
##   avg_delay max_delay
## 1  12.63907      1301

Counting Rows

Aggregation is also often used to count rows in the result set. For example, this query finds the number of flights.

sql <- paste0(
  "SELECT count(*)",
  "  FROM flights")
rs <- sqldf::sqldf(sql)
print(rs)
##   count(*)
## 1   336776

The DISTINCT keyword removes duplicates and therefore only counts unique rows.

sql <- paste0(
  "SELECT count(DISTINCT carrier)",
  "  FROM flights")
rs <- sqldf::sqldf(sql)
print(rs)
##   count(DISTINCT carrier)
## 1                      16

Also, note that the COUNT function does not require a column name like other aggregation functions because it is counting rows and that is the same regardless of which column one specifies. However, if we use the DISTINCT keyword, then a column name is required and it counts the distinct or unique values for that column.

GROUP BY

sql <- paste0(
  "SELECT carrier, round(avg(dep_delay),0) AS avg_delay_mins",
  "  FROM flights",
  " GROUP BY carrier",
  " LIMIT 6;")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier avg_delay_mins
## 1      9E             17
## 2      AA              9
## 3      AS              6
## 4      B6             13
## 5      DL              9
## 6      EV             20

Can you modify the above SQL so that it finds the average and maximum delay (rounded to the nearest minute) for each carrier? What about per airport? Or per airport per carrier? These are common analytical queries and are often supported by fact tables in data warehouses.

The groups can be selected using the HAVING clause. The HAVING clause applies to groups, while the WHERE clause applies to the rows that will be grouped. In other words, the conditions of the WHERE clause and applied first, then GROUP BY and then HAVING; and finally, ORDER BY.

INNER JOIN

An inner join (or simply a join) is an equi-join that selects all rows where the foreign key value of one data frame (table) matches a primary key value of the linked data frame (table).

As an example, let’s find carrier, flight number, and name of the origin airport. Recall that the data frame flights has a foreign key column origin that is the airport code of the origin, i.e., a link to the primary key in the airports table.

To build a join, we list both tables in the FROM clause and then add the keyword JOIN (or INNER JOIN if one wants to be more specific as to which join is being applied) between them and in parenthesis the join condition after the keyword ON.

sql <- paste0(
  "SELECT carrier, flight, name",
  "  FROM flights JOIN airports ON (origin = faa)",
  " LIMIT 5;")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier flight                name
## 1      UA   1545 Newark Liberty Intl
## 2      UA   1714          La Guardia
## 3      AA   1141 John F Kennedy Intl
## 4      B6    725 John F Kennedy Intl
## 5      DL    461          La Guardia

The above SQL statement is equivalent to the relational algebra expression below:

\(\pi_{carrier,flight,name}((flights)\bowtie_{origin=faa}(airports))\)

A point of clarification: the above SQL works as the same attributes names do not appear in the two tables. If we had an attribute att in both tables, then we would have to explicitly scope which table we mean and we would commonly alias the table names. This is also done even if there’s no confusion in order to make the query easier to understand. The updated SQL below illustrates this:

sql <- paste0(
  "SELECT f.carrier, f.flight, a.name",
  "  FROM flights AS f JOIN airports AS a ON (f.origin = a.faa)",
  " LIMIT 5;")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier flight                name
## 1      UA   1545 Newark Liberty Intl
## 2      UA   1714          La Guardia
## 3      AA   1141 John F Kennedy Intl
## 4      B6    725 John F Kennedy Intl
## 5      DL    461          La Guardia

Of course, we can combine several tables. Let’s say we need to know the carrier name rather than the carrier’s abbreviation code for the above query, i.e., “American Airlines” rather than “AA”. To do that, we need to combine three tables: flights, airlines, and airports.

sql <- paste0(
  "SELECT r.name, f.flight, a.name",
  "  FROM flights AS f ",
  "       JOIN airports AS a ON (f.origin = a.faa)",
  "       JOIN airlines AS r ON (f.carrier = r.carrier)",
  " LIMIT 5;")
rs <- sqldf::sqldf(sql)
print(rs)
##                     name flight                name
## 1  United Air Lines Inc.   1545 Newark Liberty Intl
## 2  United Air Lines Inc.   1714          La Guardia
## 3 American Airlines Inc.   1141 John F Kennedy Intl
## 4        JetBlue Airways    725 John F Kennedy Intl
## 5   Delta Air Lines Inc.    461          La Guardia

SQL supports other joins as well, including outer join.. See Lesson 70.112 Retrieving Data from Multiple Tables Using Various Joins if you want to learn more.

Subqueries

Since the result of a SELECT statement is a table, we can use a SELECT statement wherever a table is required, _e.g., in the FROM clause. We can use a SELECT statement that return one row and one column (a single value) wherever a single value is expected, such as in a condition in a WHERE clause.

The query below finds all flights that have a below average departure delay.

sql <- paste0(
  "SELECT f.flight, f.dep_delay",
  "  FROM flights AS f",
  " WHERE f.arr_delay < (SELECT avg(arr_delay)",
  "                        FROM flights)",
  " LIMIT 5;")
rs <- sqldf::sqldf(sql)
print(rs)
##   flight dep_delay
## 1    725        -1
## 2    461        -6
## 3   5708        -3
## 4     79        -3
## 5     49        -2

In this query, we use a subquery to generate a list of values which we then use in a set operation, such as IN.

SQL on Tabular Data in Files

Using SQL on CSV Files

To query data in a CSV file using SQL can be done in two ways:

  1. load the CSV into a data frame and use sqldf::sqldf()
  2. run the SQL query directly against the CSV using sqldf::read.csv.sql()

Note that in (2), underneath R will still load the data from the CSV into memory and then copy the data frame into an in-memory SQLite database on which the SQL query is actually executed. But that all happens underneath the hood and while no more efficient it is simpler to use.

Let’s query the data in the CSV CerealDataCSV.csv using both of these approaches. You can decide which you prefer.

Approach I: Load CSV into data frame and use sqldf()

library(sqldf)

# load the CSV into a data frame
cereals <- read.csv("CerealDataCSV.csv")

sqlStmt <- paste0(
  "SELECT CerealName,Calories,Sodium,Fiber,Carbs ",
  "  FROM cereals ",
  " WHERE Sodium > 250")

rs <- sqldf::sqldf(sqlStmt)

print(rs)

Approach II: Run SQL directly using read.csv.sql()

The function reads a CSV filtered by a SQL statement, resulting in a subset of the data contained in the CSV being imported into a data frame. Since the table is a file it has no name, so in the SQL statement we refer to the “table” as “file”.

Note how we build the SQL “string” using paste0() so that we can split it over multiple lines, embed the value of R variables, and make it more readable.

library(sqldf)

sqlStmt <- paste0(
  "SELECT CerealName,Calories,Sodium,Fiber,Carbs ",
  "  FROM file ",
  " WHERE Sodium > 250")

rs <- sqldf::read.csv.sql(file = "CerealDataCSV.csv", 
                          sql = sqlStmt)

print(rs)

Using SQL on XML Files

To use SQL against an XML document, the XML elements must be read into memory, split into tables, and only then can be queried. Lesson 6.328 Parsing an XML Document and Saving to SQLite Database in R explains this process in detail should you be interested.

Relational Databases

A relational database is a persistent collection of tables where each table holds information about one entity, e.g., a table of customers or a table of sales. Each table is like a CSV file or a data frame; it has rows and columns, but unlike data frames, the rows are not numbered and cannot be accessed by a row number.

Each table holds information about one entity, e.g., a table of customers or a table of sales. Each table is like a CSV file or a data frame; it has rows and columns, but unlike data frames, the rows are not numbered and cannot be accessed by a row number.

A relational database is generally only necessary when the data does not fit into memory, sharing of CSV or XML files is inconvenient, or multiple applications need access to the same data at the same time. Other times, tabular files are often sufficient.

SQLite

One of the most popular relational databases is SQLite. It is a server-less, file-based database that is fast and small so it runs on most common devices and operating systems, including Windows, MacOS, Linux, Android, and iOS.

This is not a complete tutorial on SQLite databases, rather this section seeks to show how to create and new SQLite database and how to connect to a existing SQLite database from R. If you want to know more about SQLite, read Lesson 70.801 The SQLite Database.

To connect to or access a SQLite database from R requires two steps:

  1. load the RSQLite package2
  2. call the function dbConnect()

For more information on using SQLite with R, consult Lesson 6.300 SQLite with R: A Primer.

Connect to SQLite Database

The code below demonstrates connecting to a SQLite database located in the file “flightsDB.db” which is located within the current project folder or current working directory. If it is not, then the full path to the file must be specified. If the database file does not already exist, a new database is created.

library(RSQLite)
dbcon <- dbConnect(RSQLite::SQLite(), "flightsDB.db")

The database is a replica of the tables in the nycflights13 package that we have been using.

Run SQL Queries in SQLite

Once the connection to the database has been made, you can work with the database from R using functions from the RSQLite package, such as dbExecute(), dbGetQuery(), and dbSendStatement(). Alternatively, in R Notebooks, SQL code blocks can be inserted that are then “knitted” into the aforementioned R functions. Note that this only works in R Notebooks and not R Scripts (programs).

dbGetQuery()

The code below demonstrates how to run a SQL query (SELECT) on the tables in the connected “flightsDB.db” SQLite database that contains the same tables as we have been using from the package nycflights13.

The function dbgetQuery() can only be used for SELECT statements and it always returns a data frame containing the result set. The function dbExecute(), and dbSendStatement() can be used for SELECT queries and also for other SQL statements, such as INSERT or DROP TABLE. This is beyond the scope of this tutorial. Consult Lesson 6.301 ┆ Working with Databases in R if you want to learn more.

SQL Code Chunks

If we are building R Notebook, then we can also use SQL code chunks. When the R Notebook is knitted, the SQL chunk is converted to a call to an R function, so it is no more or less efficient; it is merely a simpler and more convenient way to run SQL statements.

A SQL code chunk is in a code fence that starts with {sql connection=dbcon} where dbcon is a database connection obtained from a call to dbConnect().

The code chunk as it would be typed into an R Markdown document is shown in the image below:

SQL Code Fence
SQL Code Fence
Table 2: 5 records
airline code flight origin
United Air Lines Inc. UA 1545 Newark Liberty Intl
United Air Lines Inc. UA 1714 La Guardia
American Airlines Inc. AA 1141 John F Kennedy Intl
JetBlue Airways B6 725 John F Kennedy Intl
Delta Air Lines Inc. DL 461 La Guardia

Running a SQL query in a code chunk displays the result set. However, if we need to further process the result set in R or want to run analytics, then we need to capture the result set in a data frame. When we used dbGetQuery() this was the return value, but in a code chunk we need to use a parameter: output.var

So, the code chunk we added to the R Notebook looked like this:

SQL Code Fence with Captured Reset Set
SQL Code Fence with Captured Reset Set

Once captured in a {sql} code chunk, we can use it in an R code chunk.

head(df.flights)
##                  airline code flight              origin
## 1  United Air Lines Inc.   UA   1545 Newark Liberty Intl
## 2  United Air Lines Inc.   UA   1714          La Guardia
## 3 American Airlines Inc.   AA   1141 John F Kennedy Intl
## 4        JetBlue Airways   B6    725 John F Kennedy Intl
## 5   Delta Air Lines Inc.   DL    461          La Guardia

Of course, the R code above is not very useful but it illustrates that the result set is now in a data frame and can be processed accordingly.

Creating a Relational Database

A new database can be created in two ways:

  1. creating tables and then inserting data
  2. writing existing data from a CSV or data frames to tables

CREATE TABLE

Building a database means creating tables with attributes. This is done using the CREATE TABLE SQL statement. SQL is not case sensitive, so it does not matter whether we say CREATE TABLE or create table. Creating a table that already exists results in a error.

Once a table is created, it can be removed or modified (to some extent and with some restrictions). Removing a table is done with the DROP TABLE statement, while modifying a table is done with ALTER TABLE.

Before we can add tables and data to a database, we need to connect to the database. recall that connecting to a SQLite database that does not exist will create it (of course, without any tables in it).

library(RSQLite)

lessonDB <- dbConnect(RSQLite::SQLite(), "lessonDB.db")

In the following code blocks, we will create a new table lessons. But, in case it already exists, we will first remove it. Note that the {sql} code fence has to set connection=lessonDB.

create table lessons (
  lname text not null,
  llength integer not null
);

Inserting Data

Once we have a new table, we can add data using the SQL INSERT statement. Let’s add a few rows of data to the table so we can see how SQLite assigns a row id as a primary key.

insert into lessons values
  ('Intro C++', 90),
  ('Intro Java', 90),
  ('SQL Joins', 60)

Rather than using a {sql} code chunk we can also use R functions such as dbSendStatement(). This is an option for R Notebooks but required if we write an R Script where code fences cannot be used.

Now, finally, let’s run a query that returns some data so we can be sure it worked.

select * from lessons;
Table 3: 3 records
lname llength
Intro C++ 90
Intro Java 90
SQL Joins 60

So, now you have seen how to create, connect to, and work with a SQLite database in R.

Writing Data Frames

As an alternative to inserting data one (or multiple) rows at a time, R has a function that writes an entire data frame to a table in the database3.

The code below creates the “flightsDB.db” database from the data frames in the nycflights13 package that we have been using.

To verify that the creation of the database was successful, we will retrieve some data from multiple tables using a join. For convenience, we’ll use a SQL code chunk rather than using an R function.

select l.name as 'airline', 
       f.carrier as 'code', 
       f.flight, 
       a.name as 'origin'
  from flights f join airports a on (f.origin = a.faa)
                 join airlines l on (f.carrier = l.carrier)
 limit 5;
Table 4: 5 records
airline code flight origin
United Air Lines Inc. UA 1545 Newark Liberty Intl
United Air Lines Inc. UA 1714 La Guardia
American Airlines Inc. AA 1141 John F Kennedy Intl
JetBlue Airways B6 725 John F Kennedy Intl
Delta Air Lines Inc. DL 461 La Guardia

So, now we have seen how to write entire data frames to a relational database.

SQL vs R

In many cases, SQL is simpler and more “universal” but R is often faster. Some queries, particularly across multiple data frames and involving grouping, is simplest in SQL. Others, can be done either with SQL or with R functions. In many situations, if the data frame is in memory, then R functions are much faster.

Let’s look at a few queries and carry them out with SQL and with Base R.

Worked Examples

The worked examples below use the tables defined in the nycflights13 database and the data frames from the nycflight13 package. We will switch between the two but the data model is the same and shown below again for convenience.

Before we can make any queries in a database, we will need to connect to the database.

To follow along and try the examples for yourself, follow these steps:

  1. Create a new R Project
  2. Download the SQLite database: flightsDB.db4
  3. Copy the downloaded “flightsDB.db” into your project folder
  4. Create a new R Notebook
  5. Connect to the database

The code to connect to the database is repeated below. It also loads the data frames from the nycflights13 package to demonstrate sqldf.

library(nycflights13)
library(RSQLite)

dbcon <- dbConnect(RSQLite::SQLite(), "flightsDB.db")

As you can see, the connection object for {sql} chunks and R database functions is dbcon.

Example 1

Question

List the carrier and flight number for all flights with an airtime of more than 6 hours. Limit the result to the first 10 rows.

Solution

select carrier, flight, air_time 
  from flights 
 where air_time > (6*60)
 limit 10;
Table 5: Displaying records 1 - 10
carrier flight air_time
UA 1124 361
UA 303 366
UA 1665 366
UA 1496 380
DL 1865 362
VX 399 361
B6 671 381
AA 59 378
UA 1668 373
UA 223 369

Example 2

Question

How many flights have an airtime of less than 1 hour.

Solution I: SQL

select count(*) as 'NumFlightsLTOneHr' 
  from flights
 where air_time < 60;
Table 6: 1 records
NumFlightsLTOneHr
52433

Solution II: R

Assuming that we have the data in memory and assuming that the data fits into memory, then we can also craft a solution that uses R functions and does not use SQL.

n <- length(which(flights$air_time < 60))

print(n)
## [1] 52433

Solution III: Relational Algebra

\(\pi\)

Example 3

Question

Use the in-memory data frames and create a query that finds the number of flights per airline.

Solution

The solution uses SQL with sqldf. Using Base R would be very difficult – if you do not agree, then build a solution using R and not using SQL.

sql <- paste0(
  "SELECT carrier, count(*) AS numFlights",
  "  FROM flights",
  " GROUP BY carrier")
rs <- sqldf::sqldf(sql)
print(rs)
##    carrier numFlights
## 1       9E      18460
## 2       AA      32729
## 3       AS        714
## 4       B6      54635
## 5       DL      48110
## 6       EV      54173
## 7       F9        685
## 8       FL       3260
## 9       HA        342
## 10      MQ      26397
## 11      OO         32
## 12      UA      58665
## 13      US      20536
## 14      VX       5162
## 15      WN      12275
## 16      YV        601

Example 4

Question

Use the in-memory data frames and create a query using sqldf that finds the carriers with fewer than 1000 flights sorted alphabetically.

Solution

sql <- paste0(
  "SELECT carrier, count(*) AS numFlights",
  "  FROM flights",
  " GROUP BY carrier ",
  "HAVING numFlights < 1000",
  " ORDER BY carrier")
rs <- sqldf::sqldf(sql)
print(rs)
##   carrier numFlights
## 1      AS        714
## 2      F9        685
## 3      HA        342
## 4      OO         32
## 5      YV        601

Example 5

Question

How many unique carriers are there?

Solution I: SQL

SELECT count(DISTINCT carrier) AS numCarriers FROM flights
Table 7: 1 records
numCarriers
16

Example 6

Question

Which flights had a below average departure delay? List the carrier and flight number and their departure delay.

Solution I: SQL

We need to find the average departure delay and then use this value as an argument for the where clause.

SELECT f.carrier, f.flight, f.dep_delay
  FROM flights AS f
 WHERE f.arr_delay < (SELECT avg(arr_delay)
                        FROM flights)
 LIMIT 5;
Table 8: 5 records
carrier flight dep_delay
B6 725 -1
DL 461 -6
EV 5708 -3
B6 79 -3
B6 49 -2

Solution II: R

# calculate the average departure delay ignoring missing values (NA)
m <- mean(flights$dep_delay, na.rm = T)

# select rows where departure delay is below average
rs <- flights[which(flights$dep_delay < m),c("carrier","flight","dep_delay")]

head(rs, 5)
## # A tibble: 5 × 3
##   carrier flight dep_delay
##   <chr>    <int>     <dbl>
## 1 UA        1545         2
## 2 UA        1714         4
## 3 AA        1141         2
## 4 B6         725        -1
## 5 DL         461        -6

Example 7

Question

List the names of all airlines that have at least one flight in alphabetical order. Remove all duplicates.

Solution: SQL

There may be airlines in the airlines tables that are not linked to from the flights table, i.e., there is not flight for them. So, to list only the airlines that have at least one flight we need to join airlines and flights.

In the solution we are introducing aliases for the two tables and we are using them in the join clause. They are required as the carrier attribute is in two tables and is therefore ambiguous. The keyword as could have been omitted.

select distinct name 
  from flights as f join airlines as a on (f.carrier = a.carrier)
 order by name;
Table 9: Displaying records 1 - 10
name
AirTran Airways Corporation
Alaska Airlines Inc.
American Airlines Inc.
Delta Air Lines Inc.
Endeavor Air Inc.
Envoy Air
ExpressJet Airlines Inc.
Frontier Airlines Inc.
Hawaiian Airlines Inc.
JetBlue Airways

Example 8

Question

What is the total delay (in hours) for each airline? Display the carrier (e.g., UA or AA) and the total delay (sum).

Solution: SQL

select carrier, sum(dep_delay)
  from flights
 group by carrier
Table 10: Displaying records 1 - 10
carrier sum(dep_delay)
9E 291296
AA 275551
AS 4133
B6 705417
DL 442482
EV 1024829
F9 13787
FL 59680
HA 1676
MQ 265521

Example 9

Question

How many flights departed from each airport? List the name of the airport and number of flights.

Solution: SQL

select name, count(*)
  from flights f join airports a on (f.origin = a.faa)
 group by origin
 limit 10
Table 11: 3 records
name count(*)
Newark Liberty Intl 120835
John F Kennedy Intl 111279
La Guardia 104662

Example 10

Question

List the name of the airport and number of flights during the winter months (Dec, Jan, Feb, Mar) for all airports that had at least 10000 flights, sorted from highest to lowest.

Solution: SQL

select name, count(*) as 'numDepartures'
  from flights f join airports a on (f.origin = a.faa)
  where month in (12,1,2,3)
  group by origin
 having numDepartures > 10000
  order by numDepartures DESC
Table 12: 3 records
name numDepartures
Newark Liberty Intl 39342
John F Kennedy Intl 36425
La Guardia 33157

Example 11

Question

List the airline name, carrier code, and the total number of flights each had.

Solution: SQL

select name, count(*)
  from flights f join airports a on (f.origin = a.faa)
 group by origin
 limit 10
Table 13: 3 records
name count(*)
Newark Liberty Intl 120835
John F Kennedy Intl 111279
La Guardia 104662

Example 12

Question

Find all airlines that contain the word “America”. List the airline carrier code and airline name.

Solution: SQL

select carrier, name 
  from airlines
  where name like '%America%'
Table 14: 2 records
carrier name
AA American Airlines Inc.
VX Virgin America

Example 13

Question

How many flights were delayed during the summer months (June to Sept) due to wind_gusts?

Solution: SQL

select count(*) as 'delGusts' 
  from flights as f join weather as w on 
         (f.year = w.year and
          f.month = w.month and
          f.day = w.day and
          f.hour = w.hour)
 where f.month in (6,7,8,9) and
       w.wind_gust not null
Table 15: 1 records
delGusts
60370

Example 14

Question

Find the flight number and airline name plus the date of departure and the name of the departure airport that had the longest arrival delay. Display the date in the format ‘M/D/YYYY’ and the delay in hours rounded to one digit of precision.

Solution: SQL

select name, carrier, flight, round(dep_delay/60,1) as 'delay', 
       month || '/' || day || '/' || year as 'date'
  from flights join airports on (origin = faa)
 where dep_delay = (select max(dep_delay) from flights)
Table 16: 1 records
name carrier flight delay date
John F Kennedy Intl HA 51 21.7 1/9/2013

Example 15

Question

Find the flight number and airline name plus the date of departure and the name of the departure airport that had the second longest arrival delay. Display the date in the format ‘M/D/YYYY’ and the delay in hours rounded to one digit of precision.

Solution: SQL

select name, carrier, flight, round(dep_delay/60,1) as 'delay', 
       month || '/' || day || '/' || year as 'date'
  from flights join airports on (origin = faa)
 where dep_delay = (select min(dep_delay)
                      from (select dep_delay 
                              from flights 
                             where dep_delay > 0 
                             order by dep_delay DESC 
                             limit 2))
 
Table 17: 1 records
name carrier flight delay date
John F Kennedy Intl MQ 3535 19 6/15/2013

Lectures and Tutorials

The (hour-long) video of a lecture on these topics by Dr. Martin Schedlbauer of Khoury Online is provided below. We recommend watching it to get additional insights, see the connections between ERDs, UML, SQL, Relational Databases, SQLite, and R. The lecture also provided an introduction to tools for building ERD diagrams, where he demonstrates how to build a relational model as a Crow’s Foot ERD using LucidChart and implement that model in SQLite in an R Notebook using {sql} chunks.

In this video tutorial below, Khoury Boston’s Prof. Schedlbauer explains how to map various associations, aggregations, multi-valued attributes, and generalizations to a relational schema. Generalization defines a specialization hierarchy of classes in a conceptual model. While some databases support generalization or type hierarchies, relational database do not. Nevertheless, there are ways to implement generalization in a relational model.

Summary

This lesson showed that SQL is a universal language for querying information in relationally organized tabular structures. In addition, the lesson explained key operations of relational algebra which forms the basis of SQL.

Finally, the lesson demonstrated, through examples, how to execute SQL queries on in-memory data frames using sqldf and on tables in SQLite databases using {sql} code chunks and R functions.


Files & Resources

All Files for Lesson 50.951

Errata

None collected yet. Let us know.


  1. While aggregation functions are not part of basic relational algebra does not support grouping or mathematical aggregate functions, they are important for expressing queries and are part of extended relational algebra.↩︎

  2. Naturally, you must first install the RSQLite package if is not yet installed on your installation of R.↩︎

  3. This requires support from the database for “bulk insertion”. SQLite supports this by default, while MySQL and other databases must be configured to allow that. Some cloud database services such as db4free.net do not allow the use of dbWriteTable.↩︎

  4. Be sure to right-click on the link and choose Save As… or Save Link As… rather than clicking on the link which will not work as the browser would try to display the database file as a document.↩︎

LS0tCnRpdGxlOiAiVGFidWxhciBTdHJ1Y3R1cmVzLCBSZWxhdGlvbmFsIE1vZGVscywgYW5kIFNRTCIKcGFyYW1zOgogIGNhdGVnb3J5OiA1MAogIG51bWJlcjogOTUxCiAgdGltZTogOTAKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAic3FsaXRlLHJlbGF0aW9uYWwgZGF0YWJhc2UsZGF0YWJhc2UsUixTUUwsdGFidWxhcixzcWxkZixleGNlbCIKICBkZXNjcmlwdGlvbjogIlN0cnVjdHVyZWQgaW5mb3JtYXRpb24gb2JqZWN0cyBhcmUgb2Z0ZW4gb3JnYW5pemVkIGluIHRhYnVsYXIKICAgICAgICAgICAgICAgIGZvciBzdWNoIGFzIGRhdGEgZnJhbWVzLCB0YWJsZXMsIG9yIHdvcmtzaGVldHMuIFRoaXMgbGVzc29uCiAgICAgICAgICAgICAgICBleHBsYWlucyBob3cgU1FMIGlzIHVzZWQgdG8gcmV0cmlldmUgaW5mb3JtYXRpb24gZnJvbSB0YWJ1bGFyCiAgICAgICAgICAgICAgICBzdHJ1Y3R1cmVzIGFuZCBob3cgdG8gd29yayB3aXRoIHRhYnVsYXIgaW5mb3JtYXRpb24gc3RydWN0dXJlcwogICAgICAgICAgICAgICAgY29udGFpbmVkIGluIFNRTGl0ZSwgUiwgRXhjZWwsIGFtb25nIG90aGVycy4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCmBgYHtyIGluc3RhbGxSZXFQa2dzLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFLCBldmFsPVRSVUV9CiMgcGFja2FnZXMgcmVxdWlyZWQgZm9yIHRoaXMgbGVzc29uCnBhY2thZ2VzIDwtIGMoIm55Y2ZsaWdodHMxMyIsIlJTUUxpdGUiLCJzcWxkZiIpCgojIGluc3RhbGwgcGFja2FnZXMgbm90IHlldCBpbnN0YWxsZWQKaW5zdGFsbGVkX3BhY2thZ2VzIDwtIHBhY2thZ2VzICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpCmlmIChhbnkoaW5zdGFsbGVkX3BhY2thZ2VzID09IEZBTFNFKSkgewogIHN1cHByZXNzV2FybmluZ3Moc3VwcHJlc3NNZXNzYWdlcyhpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2VzWyFpbnN0YWxsZWRfcGFja2FnZXNdKSkpCn0KYGBgCgojIyBJbnRyb2R1Y3Rpb24KClN0cnVjdHVyZWQgaW5mb3JtYXRpb24gb2JqZWN0cyBoYXZlIHByb3BlcnRpZXMgaW4gdGhlIGZvcm0gb2YgYXR0cmlidXRlcy4gRm9yIGV4YW1wbGUsIGluc3RhbmNlcyBvZiBhICpDb3Vyc2UqIGhhdmUgYXR0cmlidXRlcyBzdWNoIGFzICp0aXRsZSosICpsZW5ndGgqLCAqY3JlZGl0cyosIGFtb25nIG90aGVycywgZGVwZW5kaW5nIG9uIHRoZSB1c2UgY2FzZXMuIFN1Y2ggaW5mb3JtYXRpb24gbXVzdCBvZnRlbiBiZSBleHRlcm5hbGl6ZWQgYW5kIHN0b3JlZCBpbiBzb21lIHN0cnVjdHVyZSB3aGVyZSB0aGV5IGNhbiBiZSBzaGFyZWQgYW5kIHF1ZXJpZWQuIEEgdGFidWxhciBvcmdhbml6YXRpb24gaXMgYSBjb21tb24gZm9ybWF0IGZvciB0aGUgZXh0ZXJuYWxpemF0aW9uIG9mIHN0cnVjdHVyZWQgaW5mb3JtYXRpb24gb2JqZWN0cy4KCkluIHRoaXMgbGVzc29uLCB3ZSB3aWxsIHRha2UgYSBsb29rIGF0IGhvdyB0YWJ1bGFyIHN0cnVjdHVyZXMgYXJlIHF1ZXJpZWQgdXNpbmcgU1FMLiBTUUwgaGFzIGVtZXJnZWQgYXMgdGhlIG1vc3QgY29tbW9uIHdheSB0byBmaW5kIGluZm9ybWF0aW9uIG9iamVjdHMgc3RvcmVkIGluIHRhYmxlcyB0aGF0IG1lZXQgc3BlY2lmaWMgY3JpdGVyaWEuCgojIyBUYWJ1bGFyIFN0cnVjdHVyZXMKCkluZm9ybWF0aW9uIG9iamVjdHMgYXJlIHN0b3JlZCBpbiB0YWJ1bGFyIGZvcm0gaW46CgotICAgQ1NWIHRleHQgZGF0YSBmaWxlcwotICAgRXhjZWwgKGFuZCBvdGhlciBzcHJlYWRzaGVldCBwcm9ncmFtKSB3b3Jrc2hlZXRzCi0gICBEYXRhIGZyYW1lcyBpbiBSIGFuZCBQeXRob24KLSAgIFJlbGF0aW9uYWwgZGF0YWJhc2UgdGFibGVzCi0gICBUd28tTGV2ZWwgWE1MIGRvY3VtZW50cwoKQWxsIHRhYnVsYXIgc3RydWN0dXJlcyBoYXZlIHRoZSBzYW1lIGZvcm1hdDogcm93cyB3aXRoIGNvbHVtbnMuIEVhY2ggcm93IGlzIGFuIGluc3RhbmNlIG9mIGFuIGluZm9ybWF0aW9uIG9iamVjdCwgd2hpbGUgZWFjaCBjb2x1bW4gaXMgYW4gYXR0cmlidXRlIHZhbHVlLiBSb3dzIGFyZSBnZW5lcmFsbHkgbnVtYmVyZWQgd2hpbGUgY29sdW1ucyBhcmUgbmFtZWQuCgojIyMgQ1NWCgpUaGUgZXhhbXBsZSBiZWxvdyBzaG93cyBkYXRhIGluIGEgQ1NWLiBOb3RlIHRoZSBzZXBhcmF0aW9uIG9mIGNvbHVtbnMgYnkgY29tbWFzLgoKYGBgICAgICAgICAgClByb2R1Y3QsQ2VyZWFsTmFtZSxNYW51ZmFjdHVyZXIsQ2Fsb3JpZXMsU29kaXVtLEZpYmVyLENhcmJzLFN1Z2FycyxTaGVsZixZZWFyLCwKMSwxMDAlIEJyYW4sTmFiaXNjbyw3MCwxMzAsMTAsNSw2LDMsMTk0MiwsCjIsQWxsLUJyYW4sS2VsbG9nZyw3MCwyNjAsOSw3LDUsMywxOTE2LCwKMyxBbGwtQnJhbiB3L0V4dHJhIEZpYmVyLEtlbGxvZ2csNTAsMTQwLDE0LDgsMCwzLDE5MTYsLApgYGAKCkEgY2VsbCBvciBhIHJhbmdlIG9mIGNlbGxzIGNhbm5vdCBiZSBkaXJlY3RseSBzcGVjaWZpZWQgdW50aWwgdGhlIENTViBpcyBsb2FkZWQgaW50byBhIHByb2dyYW0gZm9yIHByb2Nlc3NpbmcuCgojIyMgRXhjZWwKClRoZSBpbWFnZSBiZWxvdyBzaG93cyBhbiBleGFtcGxlIG9mIGEgdGFidWxhciBzdHJ1Y3R1cmUgaW4gRXhjZWwuIENvbHVtbnMgYXJlIGxldHRlcnMgKEEuLlosIEFBLVpaLCBldGMuKSBhbmQgcm93cyBhcmUgbnVtYmVyZWQ7IGVhY2ggaW50ZXJzZWN0aW9uIG9mIGEgY29sdW1uIGFuZCBhIHJvdyBpcyBhICpjZWxsKi4gSW4gYWRkaXRpb24gdG8gZGF0YSB2YWx1ZXMsIGNlbGxzIGNhbiBhbHNvIGNvbnRhaW4gZm9ybXVsYXMgd2hpY2ggYXJlIGFsZ2VicmFpYyBleHByZXNzaW9ucyByZWZlcmVuY2luZyBjZWxscyBvciByYW5nZXMgb2YgY2VsbHMuIEEgcmFuZ2Ugb2YgY2VsbHMgaXMgc2ltaWxhciB0byBhIHZlY3RvciwgYXJyYXksIG9yIGEgbWF0cml4IGluIFIuCgohW1NhbXBsZSBUYWJ1bGFyIFN0cnVjdHVyZSBpbiBhbiBFeGNlbCBXb3Jrc2hlZXRdKGltYWdlcy9TYW1wbGVFeGNlbC5qcGcpe3dpZHRoPSI1MCUifQoKQSBjZWxsIGluIEV4Y2VsIGlzIHJlZmVyZW5jZWQgYXMgYSBjb2x1bW4vcm93IHBhaXIsICplLmcuKiwgKkQ0KiByZWZlcmVuY2VzIHRoZSAqQ2Fsb3JpZXMqIGF0dHJpYnV0ZSBvZiAqUHJvZHVjdCogaW5zdGFuY2UgMyBhbmQgaGFzIHRoZSB2YWx1ZSAqNTAqLiBBIHJhbmdlIG9mIGNlbGxzIGNhbiBiZSBzcGVjaWZpZWQgYnkgYSBzdGFydGluZyBhbmQgZW5kaW5nIGNlbGwsICplLmcuKiwgKkQyOkQ4KiBpcyB0aGUgdmVjdG9yIG9mIHZhbHVlcyAkXHs3MCw3MCw1MCwxMTAsMTEwLDExMCwxMzBcfSQuCgojIyMgRGF0YSBGcmFtZXMKCkRhdGEgZnJhbWVzIGFyZSBpbi1tZW1vcnkgdGFidWxhciBzdHJ1Y3R1cmVzIGNvbW1vbiBpbiBzZXZlcmFsIHByb2dyYW1taW5nIGxhbmd1YWdlcywgaW5jbHVkaW5nIFIgYW5kIFB5dGhvbi4gV2hpbGUgdGhleSBjYW4gYmUgcHJvZ3JhbW1hdGljYWxseSBnZW5lcmF0ZWQsIHRoZXkgYXJlIHR5cGljYWxseSB0aGUgcmVzdWx0IG9mIGxvYWRpbmcgYSBDU1YgZmlsZSwgRXhjZWwgd29ya3NoZWV0LCBvciBTUUwgcXVlcnkgcmVzdWx0IGludG8gYSBkYXRhIGZyYW1lLgoKVGhlIFIgY29kZSBmcmFnbWVudCBiZWxvdyBsb2FkcyBhIENTViBmaWxlIGludG8gYSBkYXRhIGZyYW1lLCB3aGljaCBpcyB0aGVuIHNob3duLgoKYGBge3IgbG9hZENTViwgZWNobz1UfQpkZiA8LSByZWFkLmNzdihmaWxlID0gIkNlcmVhbERhdGFDU1YuY3N2Iiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCmBgYAoKYGBge3IgZWNobz1GfQprbml0cjo6a2FibGUoaGVhZChkZlssIDE6NF0sNCksICJwaXBlIiwgCiAgICAgICAgICAgICBhbGlnbiA9ICJjbGxjIiwKICAgICAgICAgICAgIGNhcHRpb24gPSAiRXhhbXBsZSBvZiBhIHRhYnVsYXIgc3RydWN0dXJlIGZyb20gYSBDU1YuIikKYGBgCgpDZWxscyBjYW4gYmUgcmVmZXJlbmNlZCBpbiBzZXZlcmFsIHdheXM6IGBkZltyb3ctbnVtYmVyLGNvbHVtbi1udW1iZXJdYCwgYGRmW3Jvdy1udW1iZXIsY29sdW1uLW5hbWVdYC4gVGhlIGNvZGUgYmxvY2sgYmVsb3cgZGVtb25zdHJhdGVzIHNvbWUgd2F5cyB0byBhY2Nlc3Mgc3BlY2lmaWMgY2VsbHMgYW5kIHJhbmdlcy4KCmBgYHtyIGV2YWw9Rn0KIyBhY2Nlc3MgYSBzaW5nbGUgY2VsbApkZlsxLDJdCgojIGFjY2Vzc2luZyBhIHNpbmdsZSBjb2x1bW4KaGVhZChkZlssMl0pCgojIGFjY2Vzc2luZyBzZXZlcmFsIGNvbHVtbnMKZGZbLDI6NF0KCiMgYWNjZXNzaW5nIHJvd3MgMSB0byA1IGFuZCBjb2x1bW5zIDIgYW5kIDMKZGZbMTo1LDI6M10KCiMgYWNjZXNzaW5nIGFsbCBjb2x1bW5zIGZvciBzb21lIHJvd3MKZGZbMTo1LF0KCiMgYWNjZXNzaW5nIGEgY29sdW1uIGJ5IG5hbWUKZGZbMjoxMCwiQ2Fsb3JpZXMiXQoKIyBhY2Nlc3NpbmcgZW50aXJlIGNvbHVtbiBieSBuYW1lCmRmJENhbG9yaWVzCgojIGFjY2Vzc2luZyB0aGUgZW50aXJlIGRhdGEgZnJhbWUKZGZbLF0KIyBvciwgc2ltcGx5CmRmCmBgYAoKUnVuIHRoZSBjb2RlIGFuZCBjb252aW5jZSB5b3Vyc2VsZiB0aGF0IGl0IHdvcmtzLiBJbiBSLCByb3cgYW5kIGNvbHVtbiBzcGVjaWZpY2F0aW9ucyBhcmUgcmFuZ2VzLCBvciwgdG8gYmUgcHJlY2lzZSwgdmVjdG9ycyAtLSB0aGV5IGRvIG5vdCBuZWVkIHRvIGJlIGNvbnRpZ3VvdXMuIFRoZSBmdW5jdGlvbiBgYygpYCBjb25jYXRlbmF0ZXMgc2V2ZXJhbCB2YWx1ZXMgaW50byBhIHZlY3Rvci4KCmBgYHtyfQpkZltjKDI6NCw4LDEwOjE0KSxjKDEsMyldCmBgYAoKVGhlIGFib3ZlIGFjY2VzcyBleHByZXNzaW9ucyBjYW4gYmUgdXNlZCBvbiB0aGUgbGVmdCBvciByaWdodCBzaWRlLCB3aGljaCBtZWFucyB0aGV5IGNhbiBhbHNvIGJlIHVzZWQgdG8gbW9kaWZ5IGEgZGF0YSBmcmFtZS4gQW4gZXhhbXBsZSBpcyBzaG93biBiZWxvdzoKCmBgYHtyfQojIHJlcGxhY2UgdmFsdWVzIGluIGEgY29sdW1uCmRmWywyXSA8LSAwCmBgYAoKRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gd29ya2luZyB3aXRoIGRhdGEgZnJhbWVzIGluIFIsIHNlZSBbTGVzc29uIDYuMTAzIFdvcmtpbmcgd2l0aCBWZWN0b3JzIGFuZCBEYXRhIEZyYW1lcyBpbiBSXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTEwMy12ZWNzLWFuZC1kZnMvbC02LTEwMy5odG1sKSBhbmQgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gcmVhZGluZyBmaWxlcyBpbnRvIGRhdGEgZnJhbWVzLCBzZWUgW0xlc3NvbiA2LjEwNiBJbXBvcnQgRGF0YSBpbnRvIFIgZnJvbSBDU1YsIFRTViwgYW5kIEV4Y2VsIEZpbGVzXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTEwNi1sb2FkLWNzdi10c3YtZXhjZWwtZmlsZXMvbC02LTEwNi5odG1sKQoKIyMjIFhNTAoKSW4gc29tZSBzaXR1YXRpb25zLCBYTUwgZG9jdW1lbnRzIHdoZXJlIGVsZW1lbnRzIGRpcmVjdGx5IHVuZGVybmVhdGggdGhlIHJvb3QgcmVwcmVzZW50IHJvd3MgYW5kIHRoZSBjaGlsZCBlbGVtZW50cyB1bmRlciB0aGUgcm93cyByZXByZXNlbnRzIGNvbHVtbnMgYXJlIHVzZWQgYXMgYW4gYWx0ZXJuYXRpdmUgdG8gQ1NWLiBBbiBleGFtcGxlIGlzIHNob3duIGJlbG93IHdoZXJlIGVhY2ggPHJvdz4gZWxlbWVudCBpcyBhIHJvdyBhbmQgdGhlIGNvbHVtbnMgYXJlIDxHaXJ0aD4sIDxIZWlnaHQ+LCBhbmQgPFZvbHVtZT4uCgpgYGAgeG1sCjw/eG1sIHZlcnNpb249IjEuMCI/PgoKPGRvY3VtZW50PgogIDxyb3c+CiAgICA8R2lydGg+OC4zPC9HaXJ0aD4KICAgIDxIZWlnaHQ+NzA8L0hlaWdodD4KICAgIDxWb2x1bWU+MTAuMzwvVm9sdW1lPgogIDwvcm93PgogIDxyb3c+CiAgICA8R2lydGg+OC42PC9HaXJ0aD4KICAgIDxIZWlnaHQ+NjU8L0hlaWdodD4KICAgIDxWb2x1bWU+MTAuMzwvVm9sdW1lPgogIDwvcm93PgogIC4uLgo8L2RvY3VtZW50PgpgYGAKCk1vc3QgY29tbW9ubHksIHN1Y2ggWE1MIGZpbGVzIGFyZSBjb252ZXJ0ZWQgdG8gYSBDU1Ygb3IgcmVhZCBpbnRvIGEgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UgYXMgYSB0YWJsZS4gRm9yIGV4YW1wbGUsIGluIFIsIHRoZSBmdW5jdGlvbiBgeG1sVG9EYXRhRnJhbWUoKWAgaXMgb2Z0ZW4gdXNlZCB0byByZWFkIHN1Y2ggYSBzcGVjaWFsbHktZm9ybWF0dGVkIFhNTCBpbnRvIGEgZGF0YSBmcmFtZS4KClRoZSBjb2RlIGJlbG93IGRlbW9uc3RyYXRlcyBob3cgdG8gbG9hZCBhIDItbGV2ZWwgWE1MIGRvY3VtZW50IGFuZCBjb252ZXJ0IHRoZSB2YWx1ZXMgdG8gdGhlIGFwcHJvcHJpYXRlIG51bWVyaWMgZGF0YSB0eXBlOgoKYGBge3J9CmxpYnJhcnkoWE1MKQoKZm4gPC0gInRyZWVkYXRhMkwueG1sIgoKIyBsb2FkIGZyb20gbG9jYWwgZmlsZQpkZiA8LSB4bWxUb0RhdGFGcmFtZShmbiwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgpkZiRHaXJ0aCA8LSBhcy5udW1lcmljKGRmJEdpcnRoKQpkZiRIZWlnaHQgPC0gYXMubnVtZXJpYyhkZiRIZWlnaHQpCmRmJFZvbHVtZSA8LSBhcy5udW1lcmljKGRmJFZvbHVtZSkKCmhlYWQoZGYsIDMpCmBgYAoKT2YgY291cnNlLCB0aGlzIG9ubHkgd29ya3MgaWYgdGhlIFhNTCBoYXMgdGhpcyBwYXJ0aWN1bGFyIHN0cnVjdHVyZSB3aGVyZSBlYWNoIGVsZW1lbnQgZGlyZWN0bHkgdW5kZXIgdGhlIHJvb3QgaXMgYSByb3cgYW5kIHRoZSBjaGlsZCBlbGVtZW50cyB1bmRlciBlYWNoIHJvdyBhcmUgaW4gdGhlIGV4YWN0IHNhbWUgb3JkZXIgYW5kIHJlcHJlc2VudCBjb2x1bW5zLiBJdCBkb2VzIG5vdCB3b3JrIGZvciBtb3JlIGNvbXBsZXggWE1MIGVsZW1lbnQgc3RydWN0dXJlcy4gSW4gdGhhdCBjYXNlLCBYUGF0aCBvciBub2RlIHRyYXZlcnNhbCB3b3VsZCBiZSBuZWVkZWQgdG8gcmVhZCB0aGUgZGF0YSAtLSBhbmQgdGhlIGRhdGEgd291bGQgbm90IGxvbmdlciBiZSBhIHRhYnVsYXIgc3RydWN0dXJlIGJ1dCByYXRoZXIgYmUgYSBjb21wbGV4IG9iamVjdCBzdHJ1Y3R1cmUuCgpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB1c2luZyBgeG1sVG9EYXRhRnJhbWUoKWAsIHNlZSBbTGVzc29uIDYuMzIzIExvYWQgU2ltcGxlIFhNTCBpbnRvIERhdGFmcmFtZSBpbiBSIHVzaW5nIHhtbFRvRGF0YUZyYW1lXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTMyMy1sb2FkLXhtbC14bWxUb0RhdGFGcmFtZS9sLTYtMzIzLmh0bWwpLgoKIyMgUmVsYXRpb25hbCBNb2RlbCBvZiBEYXRhCgpUaGUgcmVsYXRpb25hbCBtb2RlbCBpcyBhbiBhcHByb2FjaCB0byBzdG9yaW5nIGRhdGEgdGhhdCBpcyB0YWJ1bGFyIGFuZCB0aGF0IGNvbm5lY3RzIGRhdGEgaW4gb25lIHRhYmxlIHRvIGRhdGEgaW4gYW5vdGhlciB0YWJsZS4gSXQgaXMgdGhlIGZvdW5kYXRpb24gb2YgcmVsYXRpb25hbCBkYXRhYmFzZXMsIGJ1dCB0aGUgdGFibGVzIGRvIG5vdCBoYXZlIHRvIGJlIGluIGEgZGF0YWJhc2UgZm9yIHRoZSBkYXRhIHRvIGJlICJyZWxhdGlvbmFsIi4KCiMjIyBSZWxhdGlvbnMKClRoZSByZWxhdGlvbmFsIG1vZGVsIGlzIGJhc2VkIG9uIHNldCB0aGVvcnkgYW5kIGVhY2ggdGFibGUgaXMgbW9yZSBmb3JtYWxseSBjYWxsZWQgYSAqcmVsYXRpb24qLiBBIHJlbGF0aW9uIGhhcyBhdHRyaWJ1dGVzOyB0aGUgbnVtYmVyIG9mIGF0dHJpYnV0ZXMgdGhhdCBhIHJlbGF0aW9uIGhhcyBpcyBjYWxsZWQgaXRzICpkZWdyZWUqLiBGdW5kYW1lbnRhbGx5LCBhIHJlbGF0aW9uIGlzIGEgc2V0IG9mICp0dXBsZXMqLiBUaGUgY2FyZGluYWxpdHkgb2YgYSByZWxhdGlvbiBpcyB0aGUgbnVtYmVyIG9mIHR1cGxlcyBpbiB0aGUgcmVsYXRpb24uCgpGb3IgZXhhbXBsZSwgYSByZWxhdGlvbmFsIG1vZGVsIHJlcHJlc2VudGluZyBhIGxlYXJuaW5nIG1hbmFnZW1lbnQgc3lzdGVtIG1pZ2h0IGhhdmUgdGhlIHJlbGF0aW9uICpjb3Vyc2VzKiB3aGljaCBtaWdodCBoYXZlIGF0dHJpYnV0ZXMgKmNvdXJzZSBudW1iZXIqLCAqdGl0bGUqLCBhbmQgKmNyZWRpdHMqLCBhbmQgaXQgbWlnaHQgdGhlIHR1cGxlcyBzaG93biBiZWxvdzoKCmBgYCAgICAgICAgIApjb3Vyc2VzKAogIHsnSVMyMDAwJywnUHJpbmNpcGxlcyBvZiBJbmZvcm1hdGlvbiBTY2llbmNlJywgNH0sCiAgeydDUzUyMDAnLCdEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbXMnLCA0fSwKICB7J0NTNTM4MCcsJ1Byb2plY3QgTWFuYWdlbWVudCB3aXRoIFNjcnVtJywgMn0sCiAgeydEQTUwMjEnLCdCaWcgRGF0YScsIDF9CikKYGBgCgpUaGUgcmVsYXRpb24gaXMgZGVmaW5lZCBhcyBmb2xsb3dzOgoKJGNvdXJzZXMobnVtYmVyLHRpdGxlLGNyZWRpdHMpJC4KCkxldCAqVEVYVCogYmUgdGhlIGRvbWFpbiBvZiBhbGwgY2hhcmFjdGVyIHN0cmluZ3MuIFRoZSBhdHRyaWJ1dGUgZG9tYWlucyBmb3IgdGhlIHJlbGF0aW9uICpjb3Vyc2VzKiB3b3VsZCB0aGVuIGJlCgokbnVtYmVyLHRpdGxlIFxpbiBURVhUJAoKJGNyZWRpdHMgXGluIFxtYXRoYmJ7Tn0kLgoKVGhlIGFib3ZlIHJlbGF0aW9uIGhhcyBhIGRlZ3JlZSBvZiAzIGFuZCBjYXJkaW5hbGl0eSBvZiA0LiBJdCBpcyBhIHNldCBvZiBmb3VyIHR1cGxlcy4geydDUzUyMDAnLCdEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbXMnLCA0fSBpcyBvbmUgc3VjaCB0dXBsZS4KCkluIHRhYnVsYXIgZm9ybSwgdGhlIGFib3ZlIGlzIGEgdGFibGUgd2l0aCB0aHJlZSBjb2x1bW5zIGFuZCBmb3VyIHJvd3MuCgpgYGB7ciBkZWZDb3Vyc2VERiwgaW5jbHVkZT1GfQpjb3Vyc2VzIDwtIGRhdGEuZnJhbWUoCiAgbnVtYmVyID0gY2hhcmFjdGVyKDQpLAogIHRpdGxlID0gY2hhcmFjdGVyKDQpLAogIGNyZWRpdHMgPSBpbnRlZ2VyKDQpKQoKY291cnNlcyRudW1iZXIgPC0gYygnSVMyMDAwJywnQ1M1MjAwJywnQ1M1MzgwJywnREE1MDIxJykKY291cnNlcyR0aXRsZSA8LSBjKCdQcmluY2lwbGVzIG9mIEluZm9ybWF0aW9uIFNjaWVuY2UnLAogICAgICAgICAgICAgICAgICAnRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW1zJywKICAgICAgICAgICAgICAgICAgJ1Byb2plY3QgTWFuYWdlbWVudCB3aXRoIFNjcnVtJywKICAgICAgICAgICAgICAgICAgJ0JpZyBEYXRhJykKY291cnNlcyRjcmVkaXRzIDwtIGMoNCw0LDIsMSkKYGBgCgpgYGB7ciBkaXNwQ291cnNlc0RGLCBlY2hvPUZ9CmtuaXRyOjprYWJsZShjb3Vyc2VzKQpgYGAKCiMjIyBQcmltYXJ5IEtleQoKU3BlY2lmaWMgdHVwbGVzIGFyZSBpZGVudGlmaWVkIHRocm91Z2ggYSB1bmlxdWUgdmFsdWUgb2Ygb25lIGF0dHJpYnV0ZSBvciBhIGNvbWJpbmF0aW9uIG9mIHNldmVyYWwgYXR0cmlidXRlcy4gVHVwbGVzIG11c3QgYmUgdW5pcXVlIC0tIHRoaXMgaXMgYSByZXF1aXJlbWVudCBvZiB0aGUgcmVsYXRpb25hbCBtb2RlbC4gVGhlIGF0dHJpYnV0ZSAob3IgY29tYmluYXRpb24gb2YgYXR0cmlidXRlcykgdGhhdCB1bmlxdWVseSBpZGVudGlmaWVzIGVhY2ggdHVwbGUgaXMgcmVmZXJyZWQgdG8gYXMgdGhlICpwcmltYXJ5IGtleSouIEluIHRoZSBhYm92ZSBleGFtcGxlLCAqY291cnNlIG51bWJlciogd291bGQgYmUgdGhlIHByaW1hcnkga2V5IGFzIGl0IGlzLCBieSBkZWZpbml0aW9uIGluIHRoZSBidXNpbmVzcyBkb21haW4sIHVuaXF1ZSwgKmkuZS4qLCBubyB0d28gZGlmZmVyZW50IGNvdXJzZXMgY2FuIGhhdmUgdGhlIHNhbWUgY291cnNlIG51bWJlci4KCklmIHRoZXJlIGlzIG5vIG5hdHVyYWwgdW5pcXVlIGtleSBvciB0aGUga2V5IGlzIHRvbyBjb21wbGV4LCB0aGVuIHdlIGdlbmVyYWxseSAiaW52ZW50IiBvbmU7IGEgc28tY2FsbGVkICphcnRpZmljaWFsIGtleSouIEZvciBleGFtcGxlLCBmb3IgdGhlICpjb3Vyc2VzKiByZWxhdGlvbiB3ZSBtaWdodCB3aXNoIHRvIGFkZCBhIHVuaXF1ZSBpZGVudGlmaWVyIGNvbHVtbiBhbmQgYXNzaWduIGEgdW5pcXVlIHZhbHVlIGZvciBlYWNoIGNvdXJzZSBhbmQgdXNlIHRoYXQgYXMgdGhlIHByaW1hcnkga2V5IHJhdGhlciB0aGFuIHRoZSBjb3Vyc2UgbnVtYmVyLiBJbmNpZGVudGFsbHksIHRoaXMgd2lsbCBtYWtlIGNvdXJzZSBudW1iZXIgYSBzby1jYWxsZWQgYWx0ZXJuYXRlIGtleS4KCkhlcmUgaXMgd2hhdCB0aGUgbmV3IHJlbGF0aW9uICpjb3Vyc2VzKiB3b3VsZCBsb29rIGxpa2U6CgokY291cnNlcyhcdW5kZXJsaW5le2NpZH0sbnVtYmVyLHRpdGxlLGNyZWRpdHMpJC4KCmBgYCAgICAgICAgIApjb3Vyc2VzKAogIHs0NDUzLCdJUzIwMDAnLCdQcmluY2lwbGVzIG9mIEluZm9ybWF0aW9uIFNjaWVuY2UnLCA0fSwKICB7NjY1NCwnQ1M1MjAwJywnRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW1zJywgNH0sCiAgezEwMDksJ0NTNTM4MCcsJ1Byb2plY3QgTWFuYWdlbWVudCB3aXRoIFNjcnVtJywgMn0sCiAgezQxOTgsJ0RBNTAyMScsJ0JpZyBEYXRhJywgMX0KKQpgYGAKClRoZSB2YWx1ZXMgZm9yICpjaWQqIGFyZSAibWFkZSB1cCIgLS0gdGhleSBjb3VsZCBoYXZlIGJlZW4gc2VxdWVudGlhbCBudW1iZXJzIHRvbywgKmUuZy4qLCAxLCAyLCAzLCBhbmQgNC4gVGhlIGFjdHVhbCB2YWx1ZXMgYXJlIG1lYW5pbmdsZXNzIGFzIGxvbmcgYXMgdGhleSBhcmUgdW5pcXVlLiBJbiBhIHJlbGF0aW9uIGRlZmluaXRpb24sIHByaW1hcnkga2V5IGF0dHJpYnV0ZXMgYXJlIG9mdGVuIHVuZGVybGluZWQgc28gdGhleSBhcmUgZWFzaWVyIHRvIHJlY29nbml6ZS4KCiMjIyBBdHRyaWJ1dGVzCgpUaGUgc2V0IG9yIHJhbmdlIG9mIHZhbHVlcyBmcm9tIHdoaWNoIHRoZSB2YWx1ZSBvZiBhbiBhdHRyaWJ1dGUgY2FuIGJlIGRyYXduIGlzIGNhbGxlZCB0aGUgYXR0cmlidXRlJ3MgKmRvbWFpbiouIEluIHRoZSBhYm92ZSBleGFtcGxlLCB0aGUgZG9tYWluIGZvciAqY291cnNlIG51bWJlciogaXMgKlRFWFQqIGFuZCBpbmNsdWRlcyBhbGwgcG9zc2libGUgY291cnNlIG51bWJlcnMgdGhhdCBmaXQgdGhlIHBhdHRlcm4gKkFBTk5OTiosIHdoZXJlICpBQSogaXMgdGhlIGNvdXJzZSBhcmVhLCBzdWNoIGFzICpJUyogKEluZm9ybWF0aW9uIFNjaWVuY2UpIG9yICpDWSogKEN5YmVyIFNlY3VyaXR5KSBhbmQgJE5OTk4kIGlzIGEgZm91ciBkaWdpdCBjb3Vyc2UgbnVtYmVyLiBUaGUgZG9tYWluIGZvciB0aGUgKmNyZWRpdHMqIGF0dHJpYnV0ZSBpcyBhbGwgaW50ZWdlcnMgZ3JlYXRlciB0aGFuIDAgYW5kIG5vIG1vcmUgdGhhbiA1IC0tIG9yIGF0IGxlYXN0IHRoYXQgaXMgdGhlIGRvbWFpbiBhdCBOb3J0aGVhc3Rlcm4gVW5pdmVyc2l0eS4KCk1vcmUgZm9ybWFsbHksIHdlIGNvdWxkIGV4cHJlc3MgdGhlIGRvbWFpbiBmb3IgKmNyZWRpdHMqIGFzIGEgc2V0IHVzaW5nIHNldCBnZW5lcmF0aW5nIG5vdGF0aW9uOgoKJGNvdXJzZXMgXGluIFx7eCA6IHggXGd0IDAgXGxhbmQgeCBcbGUgNVx9JC4KCk5hdHVyYWxseSwgZGlmZmVyZW50IGJ1c2luZXNzIGFyZWFzIHdpbGwgaGF2ZSBkaWZmZXJlbnQgZG9tYWlucyBmb3IgYXR0cmlidXRlcyAtLSB0aGV5IGFyZSBhIGJ1c2luZXNzIHJ1bGUuCgojIyMgTGlua2VkIFJlbGF0aW9ucwoKVG8gc2hvdyB0aGF0IHJlbGF0aW9ucyBhcmUgZ2VuZXJhbGx5IG5vdCBieSB0aGVtc2VsdmVzIGJ1dCByYXRoZXIgYXJlIGxpbmtlZCwgbGV0J3Mgc2F5IHRoYXQgd2Ugd2FudCB0byB0cmFjayB0aGUgaW5zdHJ1Y3RvciB3aG8gdGVhY2hlcyBhIGNvdXJzZS4gV2UgbWlnaHQgZGVjaWRlIHRvIGV4dGVuZCB0aGUgcmVsYXRpb24gKmNvdXJzZXMqIHdpdGggYW4gYWRkaXRpb25hbCBhdHRyaWJ1dGUgZm9yIGluc3RydWN0b3I6CgokY291cnNlcyhcdW5kZXJsaW5le2NpZH0sbnVtYmVyLHRpdGxlLGNyZWRpdHMsaW5zdHJ1Y3RvcikkLgoKQW4gaW5zdGFuY2Ugb2YgdGhhdCByZWxhdGlvbiBtaWdodCBub3cgYmU6CgpgYGAgICAgICAgICAKY291cnNlcygKICB7NDQ1MywgJ0lTMjAwMCcsJ1ByaW5jaXBsZXMgb2YgSW5mb3JtYXRpb24gU2NpZW5jZScsIDQsICdKb3NlIFJvamEnfSwKICB7NjY1NCwgJ0NTNTIwMCcsJ0RhdGFiYXNlIE1hbmFnZW1lbnQgU3lzdGVtcycsIDQsICdBbm5lbGllc2UgRnJhY2snfSwKICB7MTAwOSwgJ0NTNTM4MCcsJ1Byb2plY3QgTWFuYWdlbWVudCB3aXRoIFNjcnVtJywgMiwgJ1JhaHVsIFByYWhpdCd9LAogIHs0MTk4LCAnREE1MDIxJywnQmlnIERhdGEnLCAxLCAnWGkgV2FuZyd9CikKYGBgCgpTaW1wbGUuLi4gYnV0IHdoYXQgaWYgdGhlIHNhbWUgaW5zdHJ1Y3RvciB0ZWFjaGVzIG1vcmUgdGhhbiBvbmUgY291cnNlPyBUaGVuIHdlIHdvdWxkIGhhdmUgcmVwZXRpdGlvbi4gVGhlcmUgd291bGQgYmUgZXZlbiBtb3JlIGluZm9ybWF0aW9uIHJlcGVhdGVkIGlmIHdlIHRyYWNrZWQgdGhlIGluc3RydWN0b3IncyBkZXBhcnRtZW50LCByYW5rLCBhbmQgaGlnaGVzdCBkZWdyZWUgZWFybmVkLiBXaGlsZSB0aGF0IGlzIGNvbW1vbmx5IGRvbmUgaW4gYSBDU1Ygb3IgYSBkYXRhIGZyYW1lLCBpdCBpcyBub3QgYXBwcm9wcmlhdGUgZm9yIGEgcmVsYXRpb25hbCBtb2RlbCB3aGVyZSByZXBldGl0aW9uIGlzIGZyb3duZWQgdXBvbiBhcyBpdCB2aW9sYXRlcyBzby1jYWxsZWQgKm5vcm1hbCBmb3JtcyouIEEgYmV0dGVyIGFwcHJvYWNoIHdvdWxkIGJlIHRvIGNyZWF0ZSBhbm90aGVyIHJlbGF0aW9uIHRvIHRyYWNrIGluc3RydWN0b3Itc3BlY2lmaWMgaW5mb3JtYXRpb246ICRpbnN0cnVjdG9ycyhcdW5kZXJsaW5le2lpZH0sIG5hbWUsZGVwYXJ0bWVudCxyYW5rLGRlZ3JlZSkkLiBOb3RlIHRoYXQgd2UsIG9uY2UgYWdhaW4sIGNyZWF0ZWQgYW4gYXJ0aWZpY2lhbCBrZXkgYXR0cmlidXRlIC0tIGluIHRoaXMgY2FzZSB0aGVyZSBpcyBubyBuYXR1cmFsIGtleSBhcyBpbnN0cnVjdG9yIG5hbWUgY2Fubm90IGJlIGFzc3VtZWQgdG8gYmUgdW5pcXVlLgoKYGBgICAgICAgICAgCmluc3RydWN0b3JzKAogIHsxMDEsICdKb3NlIFJvamEnLCAnQ29tcHV0ZXIgU2NpZW5jZScsICdBc3NvY2lhdGUnLCAnUGhEJ30sCiAgezEwMiwgJ1hpIFdhbmcnLCAnQ29tcHV0ZXIgU2NpZW5jZScsICdGdWxsJywgJ1BoRCd9LAogIHs5MDEsICdBbm5lbGllc2UgRnJhY2snLCBOVUxMLCAnQWRqdW5jdCcsICdNU2MnfSwKICB7MzAxLCAnUmFodWwgUHJhaGl0JywgJ0luZHVzdHJpYWwgRW5naW5lZXJpbmcnLCAnUmVzZWFyY2gnLCAnRFNjJ30KICB7NjAxLCAnRWxhaW5lIENhbXBiZWxsJywgJ0VkdWNhdGlvbicsICdBc3Npc3RhbnQnLCAnRWREJ30KKQpgYGAKClRoZXJlIGlzIHN0aWxsIHNvbWUgcmVwZXRpdGlvbiB3aGVuIGluc3RydWN0b3JzIGFyZSBpbiB0aGUgc2FtZSBkZXBhcnRtZW50IG9yIGhhdmUgdGhlIHNhbWUgcmFuaywgYnV0IHdlIHdpbGwgaWdub3JlIHRoYXQgZm9yIG5vdyByYXRoZXIgdGhhbiBhZGRpbmcgYSAqZGVwYXJ0bWVudHMqIHJlbGF0aW9uIGFuZCBhIGxvb2t1cCByZWxhdGlvbiBmb3IgcmFuay4gQWxzbyBub3RlIHRoYXQgd2UgY2hvc2Ugbm90IHRvIGFzc29jaWF0ZSBwYXJ0LXRpbWUgYWRqdW5jdCBpbnN0cnVjdG9ycyB3aXRoIGEgZGVwYXJ0bWVudC4gVGhpcyBpcyBub3QgYSB1bml2ZXJzYWwgcnVsZSwgYnV0IHJhdGhlciBhIHJ1bGUgdGhhdCBhbiBhbmFseXN0IG1pZ2h0IGhhdmUgdW5jb3ZlcmVkIGR1cmluZyBpbmZvcm1hdGlvbiBhbmFseXNpcy4KClRvIHRyYWNrIHdoaWNoIGluc3RydWN0b3IgdGVhY2hlcyB3aGljaCBjb3Vyc2UsIHdlIHdvdWxkIGZpcnN0IHJlbW92ZSBhbGwgaW5zdHJ1Y3RvciBpbmZvcm1hdGlvbiBmcm9tIHRoZSAqY291cnNlcyogcmVsYXRpb24gYW5kIGFkZCBhIG5ldyBhdHRyaWJ1dGUgdGhhdCBoYXMgYXMgYSB2YWx1ZSB0aGUgcHJpbWFyeSBrZXkgb2YgdGhlIGluc3RydWN0b3Igd2hvIHRlYWNoZXMgdGhlIGNvdXJzZS4gU3VjaCBhcyBhdHRyaWJ1dGUgaW4gKmNvdXJzZXMqIGlzIHJlZmVycmVkIHRvIGFzIGEgKmZvcmVpZ24ga2V5KiBhdHRyaWJ1dGUuCgpUaGUgdXBkYXRlZCAkY291cnNlcyQgcmVsYXRpb24gaXMKCiRjb3Vyc2VzKFx1bmRlcmxpbmV7Y2lkfSxudW1iZXIsdGl0bGUsY3JlZGl0cyxcYm9sZHN5bWJvbHtpaWR9KSQuCgpOb3RpY2UgaG93IHRoZSBmb3JlaWduIGtleSBhdHRyaWJ1dGUgaXMgYm9sZGVkIChvciBzb21ldGltZXMgaXRhbGljaXplZCkgdG8gZW1waGFzaXplIGl0LgoKQW4gaW5zdGFuY2UgaXMgc2hvd24gYmVsb3c6CgpgYGAgICAgICAgICAKY291cnNlcygKICB7NDQ1MywgJ0lTMjAwMCcsJ1ByaW5jaXBsZXMgb2YgSW5mb3JtYXRpb24gU2NpZW5jZScsIDQsIDEwMX0sCiAgezY2NTQsICdDUzUyMDAnLCdEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbXMnLCA0LCA5MDF9LAogIHsxMDA5LCAnQ1M1MzgwJywnUHJvamVjdCBNYW5hZ2VtZW50IHdpdGggU2NydW0nLCAyLCAzMDF9LAogIHs0MTk4LCAnREE1MDIxJywnQmlnIERhdGEnLCAxLCAxMDJ9CikKYGBgCgpOb3csIGlmIGFuIGluc3RydWN0b3IgdGVhY2hlcyBtb3JlIHRoYW4gb25lIGNvdXJzZSwgd2Ugd291bGQgbm90IG5lZWQgdG8gcmVwZWF0IHRoZSBpbnN0cnVjdG9yIGluZm9ybWF0aW9uLgoKYGBgICAgICAgICAgCmNvdXJzZXMoCiAgezQ0NTMsICdJUzIwMDAnLCdQcmluY2lwbGVzIG9mIEluZm9ybWF0aW9uIFNjaWVuY2UnLCA0LCAxMDF9LAogIHs2NjU0LCAnQ1M1MjAwJywnRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW1zJywgNCwgOTAxfSwKICB7MTAwOSwgJ0NTNTM4MCcsJ1Byb2plY3QgTWFuYWdlbWVudCB3aXRoIFNjcnVtJywgMiwgMzAxfSwKICB7NDE5OCwgJ0RBNTAyMScsJ0JpZyBEYXRhJywgMSwgMTAyfSwKICB7ODg5MCwgJ0RBNTAyOScsJ0RhdGEgQW5hbHl0aWNzIFByb2plY3QnLCAxLCAxMDJ9LAopCmBgYAoKIyMjIFJlbGF0aW9uYWwgT3BlcmF0aW9ucwoKVGhlcmUgYXJlIHNldmVyYWwgaW1wb3J0YW50IG9wZXJhdGlvbnMgZGVmaW5lZCBvbiBhIHJlbGF0aW9uYWwgbW9kZWw6CgotICAgc2VsZWN0aW9uCi0gICBwcm9qZWN0aW9uCi0gICByZW5hbWUKLSAgIGFnZ3JlZ2F0aW9uCi0gICBncm91cGluZwotICAgZXF1aS1qb2luCgpUaGVzZSBhcmUgZGVmaW5lZCBpbiBhICpyZWxhdGlvbmFsIGFsZ2VicmEqLiBUaGUgZnVsbCBzZXQgb2Ygb3BlcmF0aW9ucyBvZiB0aGUgcmVsYXRpb25hbCBhbGdlYnJhIGFyZSBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgbGVzc29uLiBJZiB5b3UgaGF2ZSBhbiBpbnRlcmVzdCBpbiBsZWFybmluZyBtb3JlLCBjb25zdWx0IFtMZXNzb24gNjAuNTAyIFJlbGF0aW9uYWwgQWxnZWJyYV0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy82MC5kYmRlc2lnbi9sLTYwLTUwMi1yZWwtYWxnZWJyYS9sLTYwLTUwMi5odG1sKS4KClJlbGF0aW9uYWwgYWxnZWJyYSBpcyBhbiBpbXBvcnRhbnQgcXVlcnkgYWJzdHJhY3Rpb24gbWVjaGFuaXNtIGFuZCBpcyBvZnRlbiB1c2VkIHRvIGV4cHJlc3MgcXVlcmllcyB3aXRob3V0IHJlc29ydGluZyB0byBhIHNwZWNpZmljIHF1ZXJ5IGxhbmd1YWdlIHN1Y2ggYXMgU1FMLgoKIyMjIyBSZWxhdGlvbnMgYXMgU2V0cwoKSW4gdGhlIHJlbGF0aW9uYWwgYWxnZWJyYSwgcmVsYXRpb25zIGFyZSB0cmVhdGVkIGFzIChtYXRoZW1hdGljYWwpIHNldHMgYW5kIHRodXMgY2Fubm90LCBieSBkZWZpbml0aW9uIG9mIGEgc2V0LCBjb250YWluIGR1cGxpY2F0ZXMuCgojIyMjIFNlbGVjdGlvbgoKVGhlIHNlbGVjdGlvbiBvcGVyYXRpb24gaXMgZGVub3RlZCB3aXRoIHRoZSBsZXR0ZXIgJFxzaWdtYSQgYW5kIGlzIHVzZWQgdG8gY2hvb3NlIGEgc3Vic2V0IG9mIHR1cGxlcyB0aGF0IHNhdGlzZnkgYSBzZWxlY3Rpb24gY29uZGl0aW9uLiBUaGUgc2VsZWN0aW9uIGNvbmRpdGlvbiBpcyBleHByZXNzZWQgYXMgYSBCb29sZWFuIGxvZ2ljYWwgb3BlcmF0aW9uLiBJdCBhY3RzIGFzIGEgKmZpbHRlciogb24gdGhlIHR1cGxlcyBvZiBhIHJlbGF0aW9uLiBBbiBhbHRlcm5hdGUgd2F5IG9mIGxvb2tpbmcgYXQgc2VsZWN0aW9uIGlzIHRoYXQgaXQgc3BlY2lmaWVzIGNvbmRpdGlvbnMgdW5kZXIgd2hpY2ggYSB0dXBsZSBtYXkgYmVsb25nIHRvIGEgcmVsYXRpb24uIEluIG1vcmUgcHJhY3RpY2FsIHRlcm1zLCB3ZSBjYW4gbG9vayBhdCBpdCBhcyBzZWxlY3Rpbmcgcm93cyBmcm9tIGEgdGFibGUgdGhhdCBtZWV0IGNlcnRhaW4gInNlYXJjaCIgY3JpdGVyaWEuCgpXZSB3aWxsIHVzZSB0aGUgKHNpbXBsZXIgYW5kIHNpbmdsZSkgcmVsYXRpb24KCiRjb3Vyc2VzKG51bWJlcix0aXRsZSxjcmVkaXRzKSQKCndpdGggdGhlIGluc3RhbmNlIHNob3dzIGJlbG93LCBmb3Igc29tZSBvZiB0aGUgdXBjb21pbmcgZXhhbXBsZXM6CgpgYGAgICAgICAgICAKY291cnNlcygKICB7J0lTMjAwMCcsJ1ByaW5jaXBsZXMgb2YgSW5mb3JtYXRpb24gU2NpZW5jZScsIDR9LAogIHsnQ1M1MjAwJywnRGF0YWJhc2UgTWFuYWdlbWVudCBTeXN0ZW1zJywgNH0sCiAgeydDUzUzODAnLCdQcm9qZWN0IE1hbmFnZW1lbnQgd2l0aCBTY3J1bScsIDJ9LAogIHsnREE1MDIxJywnQmlnIERhdGEnLCAxfQopCmBgYAoKVXNpbmcgdGhlICpjb3Vyc2UqIHJlbGF0aW9uIGRlZmluZWQgYWJvdmUsIHRvIHNlbGVjdCBhbGwgY291cnNlcyB0aGF0IGFyZSBsZXNzIHRoYW4gZm91ciBjcmVkaXRzLCB3ZSB3b3VsZCB1c2UgdGhlIGZvbGxvd2luZyBvcGVyYXRpb246CgokXHNpZ21hX3tjcmVkaXRzIFxsdCA0fShjb3Vyc2VzKSQKCkluIGdlbmVyYWwsIHRoZSBzZWxlY3Rpb24gb3BlcmF0aW9uIG9uIGFueSByZWxhdGlvbiAkUiQgaXMgZGVub3RlZCBieQoKJFxzaWdtYV97PGNvbmRpdGlvbj59KFIpJAoKd2hlcmUgPGNvbmRpdGlvbj4gaXMgYSBCb29sZWFuIGxvZ2ljYWwgZXhwcmVzc2lvbiBvZiBvbmUgb3IgbW9yZSBjbGF1c2VzLiBBbiBleGFtcGxlIHdpdGggYSBjb21wb3VuZCBCb29sZWFuIGNvbmRpdGlvbiBpcyBzaG93biBiZWxvdzoKCiRcc2lnbWFfeyhjcmVkaXRzIFxnZXEgMikgXGxhbmQgKGNyZWRpdHMgXGxlcSA0KX0oY291cnNlcykkCgpUaGUgc2VsZWN0aW9uIG9wZXJhdG9yIGlzICp1bmFyeSogLS0gaXQgYXBwbGllcyB0byBhIHNpbmdsZSByZWxhdGlvbi4gVGhlIGNvbmRpdGlvbiBpcyBhcHBsaWVkIHRvIGVhY2ggdHVwbGUgYW5kIGNhbiB0aGVyZWZvcmUgb25seSBpbnZvbHZlIGEgc2luZ2xlIHR1cGxlLiBUaGUgZGVncmVlIG9mIHRoZSByZXN1bHRhbnQgcmVsYXRpb24gaXMgdGhlIHNhbWUgYXMgdGhlIHJlbGF0aW9uIHRvIHdoaWNoIHNlbGVjdGlvbiBpcyBhcHBsaWVkLiBUaGUgbnVtYmVyIG9mIHR1cGxlcyBvZiB0aGUgcmVzdWx0YW50IHJlbGF0aW9uIChpdHMgKmNhcmRpbmFsaXR5KikgaXMgYWx3YXlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byB0aGUgcmVsYXRpb24gdG8gd2hpY2ggc2VsZWN0aW9uIGlzIGFwcGxpZWQuIFRoYXQgaXMgJHxcc2lnbWFfe0N9KFIpfFxsZXEgfFJ8JC4gVGhlIHBlcmNlbnRhZ2Ugb2YgdHVwbGVzIHNlbGVjdGVkIGJ5IHRoZSBzZWxlY3Rpb24gb3BlcmF0aW9uIGlzIGNhbGxlZCB0aGUgc2VsZWN0aW9uJ3MgKnNlbGVjdGl2aXR5Ki4KClNlbGVjdGlvbiBvcGVyYXRpb25zIGNhbiBiZSBjYXNjYWRlZCBzaW5jZSB0aGUgcmVzdWx0IG9mIGEgc2VsZWN0aW9uIGlzIGEgcmVsYXRpb24uCgpTZWxlY3Rpb24gaXMgKmNvbW11dGF0aXZlKiwgKmkuZS4qLAoKJFxzaWdtYV97PENfMT59KFxzaWdtYV97PENfMj59KFIpKSA9IFxzaWdtYV97PENfMj59KFxzaWdtYV97PENfMT59KFIpKSQKCkEgY2FzY2FkZWQgc2V0IG9mIHNlbGVjdGlvbnMgY2FuIGFsd2F5cyBiZSB0cmFuc2Zvcm1lZCBpbnRvIGEgY29uanVuY3Rpb246CgokXHNpZ21hX3s8Q18xPn0oXHNpZ21hX3s8Q18yPn0oUikpID0gXHNpZ21hX3s8Q18xPiBcbGFuZCA8Q18yPn0oUikkCgojIyMjIFByb2plY3Rpb24KClRoZSBwcm9qZWN0IG9wZXJhdGlvbiBpcyBkZW5vdGVkIHdpdGggdGhlIEdyZWVrIGxldHRlciAkXHBpJCBhbmQgaXQgc2VsZWN0cyBzcGVjaWZpYyBhdHRyaWJ1dGVzIGZyb20gYSByZWxhdGlvbi4gSXQgY2FuIGJlIHRob3VnaHQgb2YgYXMgYSB2ZXJ0aWNhbCBwYXJ0aXRpb25pbmcgb2YgYSByZWxhdGlvbiBpbnRvIHR3byByZWxhdGlvbnMgd2hlcmUgb25lIHBhcnRpdGlvbiBjb250YWlucyB0aGUgYXR0cmlidXRlcyBvZiBpbnRlcmVzdCBhbmQgdGhlIG90aGVyIHBhcnRpdGlvbiBpcyBkaXNjYXJkZWQuIFRoZSBleGFtcGxlIGJlbG93IGV4dHJhY3RzIHRoZSBhdHRyaWJ1dGVzICpudW1iZXIqIGFuZCAqdGl0bGUqIGZyb20gdGhlIHJlbGF0aW9uICpjb3Vyc2VzLioKCiRccGlfe251bWJlcix0aXRsZX0oY291cnNlcykkCgpJbiBnZW5lcmFsLCB0aGUgcHJvamVjdGlvbiBvcGVyYXRpb24gb24gYW55IHJlbGF0aW9uICRSJCBpcyBkZW5vdGVkIGJ5CgokXHBpX3s8YXR0cmlidXRlIGxpc3Q+fShSKSQKClRoZSByZXN1bHQgb2YgYSBwcm9qZWN0aW9uIGlzIGEgcmVsYXRpb24gaGF2aW5nIGEgZGVncmVlIGVxdWFsIHRvIHRoZSBudW1iZXIgb2YgYXR0cmlidXRlcyBpbiB0aGUgKlw8YXR0cmlidXRlIGxpc3RcPiogYW5kIGlzIGFsd2F5cyBsZXNzIHRoYW4gb3IgZXF1YWwgdG8gdGhlIGRlZ3JlZSBvZiAkUiQuIFNpbWlsYXIgdG8gc2VsZWN0aW9uLCB0aGUgcHJvamVjdGlvbiBvcGVyYXRvciBpcyBhbHNvICp1bmFyeSogLS0gaXQgYXBwbGllcyB0byBhIHNpbmdsZSByZWxhdGlvbi4gVGhlIG51bWJlciBvZiB0dXBsZXMgb2YgdGhlIHJlc3VsdGFudCByZWxhdGlvbiAoaXRzICpjYXJkaW5hbGl0eSopIGlzIGFsd2F5cyBlcXVhbCB0byB0aGUgcmVsYXRpb24gdG8gd2hpY2ggcHJvamVjdGlvbiBpcyBhcHBsaWVkLgoKVGhlIGdlbmVyYWxpemVkIGZvcm0gb2YgcHJvamVjdGlvbiBhbGxvd3MgZnVuY3Rpb25zIG9uIGF0dHJpYnV0ZXMgdG8gYmUgaW5jbHVkZWQgYW5kIGlzIGV4cHJlc3NlZCBhczoKCiRccGlfe0ZfMSxGXzIsLi4uLEZfan0oUikkLAoKd2hlcmUgZWFjaCAkRl9rJCBpcyBhIGZ1bmN0aW9uIG92ZXIgdGhlIGF0dHJpYnV0ZXMgaW4gdGhlIHJlbGF0aW9uICRSJCBhbmQgbWF5IGluY2x1ZGUgb3BlcmF0aW9ucyBhbmQgc2NhbGFyIHZhbHVlcy4KCkZvciBleGFtcGxlLAoKJFxwaV97dXBwZXIodGl0bGUpLGNyZWRpdHMvMTV9KGNvdXJzZXMpJCwKCndoZXJlICR1cHBlcigpJCBzaG91bGQgYmUgaW50ZXJwcmV0ZWQgYSB1c2VyIGRlZmluZWQgZnVuY3Rpb24gKGluIHRoaXMgZXhhbXBsZSByZXR1cm5pbmcgaXRzIGFyZ3VtZW50IGluIGFsbCB1cHBlciBjYXNlIGxldHRlcnMpLgoKIyMjIyBSZW5hbWUKCldoaWxlIHJlbGF0aW9uYWwgYWxnZWJyYSBleHByZXNzaW9ucyBjYW4gYmUgY2FzY2FkZWQgKCppLmUuKiwgbmVzdGVkKSwgdGhpcyByZXN1bHRzIGlzIHBvdGVudGlhbGx5IGxvbmcgYW5kIHVud2llbGR5IGV4cHJlc3Npb25zLiBBbHRlcm5hdGl2ZWx5LCBleHByZXNzaW9ucyBjYW4gYmUgZGVjb21wb3NlZCBpbnRvIGEgc2VxdWVuY2Ugb2YgaW5kaXZpZHVhbCBleHByZXNzaW9ucyBhbmQgaW50ZXJtZWRpYXJ5IHJlc3VsdGFudCByZWxhdGlvbnMgYW5kIGF0dHJpYnV0ZXMgY2FuIGJlIGFzc2lnbmVkIG5ldyBuYW1lcyB1c2luZyB0aGUgKnJlbmFtZSogb3BlcmF0aW9uLiBUaGUgcmVuYW1lIG9wZXJhdGlvbiBpcyBkZW5vdGVkIHdpdGggdGhlIEdyZWVrIGxldHRlciAkXHJobyQuIFJlbmFtZSBpcyBhIHVuYXJ5IG9wZXJhdGlvbiBhbmQgdGFrZXMgdGhlIGdlbmVyYWwgZm9ybToKCiRccmhvX3tTKEJfMSxCXzIsXGxkb3RzLEJfbikoUil9JAoKVGhpcyByZW5hbWVzIHRoZSByZWxhdGlvbiAkUiQgdG8gJFMkIGFuZCByZW5hbWVzIGVhY2ggYXR0cmlidXRlICRBX2kkIG9mICRSJCB0byAkQl9pJC4KClRoZSBleGFtcGxlIGJlbG93LCBzZWxlY3RzIHRoZSBudW1iZXIgYW5kIHRpdGxlIG9mIGFsbCBjb3Vyc2VzIHdpdGggZmV3ZXIgdGhhbiBmb3VyIGNyZWRpdHMgYW5kIHJlbmFtZXMgdGhlIHJlc3VsdGFudCByZWxhdGlvbiB0byAqc2hvcnRDb3Vyc2VzKiBhbmQgdGhlIGF0dHJpYnV0ZXMgdG8gKmNvZGUqIGFuZCAqbmFtZS4qCgokXHJob197c2hvcnRDb3Vyc2VzKGNvZGUsbmFtZSl9KFxwaV97Y3JlZGl0cyx0aXRsZX0oXHNpZ21hX3tjcmVkaXRzIFxsdCA0fShjb3Vyc2VzKSkpJAoKRXhwcmVzc2luZyB0aGUgYWJvdmUgYXMgYSBzZXJpZXMgb2YgcmVsYXRpb25hbCBhbGdlYnJhIGV4cHJlc3Npb24sIHdlIHdvdWxkIGdldDoKCiRccmhvX3tFfShcc2lnbWFfe2NyZWRpdHMgXGx0IDR9KGNvdXJzZXMpKSQKCiRccmhvX3tTfShccGlfe251bWJlcix0aXRsZX0oRSkpJAoKJFxyaG9fe3Nob3J0Q291cnNlcyhjb2RlLG5hbWUpfShTKSQKCkJvdGggb2YgdGhlIGFwcHJvYWNoZXMgYXJlIGNvcnJlY3QgYW5kIHRoZSB1c2UgaXMgZ2VuZXJhbGx5IGEgcGVyc29uYWwgcHJlZmVyZW5jZS4gT2Z0ZW4sIHVzaW5nIHJlbmFtZSBvcGVyYXRpb25zIGNhbiBtYWtlIGEgY29tcGxleCBleHByZXNzaW9uIHNpbXBsZXIgdG8gZm9sbG93LgoKIyMjIyBBZ2dyZWdhdGlvbgoKQW4gKmFnZ3JlZ2F0ZSBmdW5jdGlvbiogb3BlcmF0aW9uW14xXSBpcyBkZW5vdGVkIHdpdGgKClteMV06IFdoaWxlIGFnZ3JlZ2F0aW9uIGZ1bmN0aW9ucyBhcmUgbm90IHBhcnQgb2YgYmFzaWMgcmVsYXRpb25hbCBhbGdlYnJhIGRvZXMgbm90IHN1cHBvcnQgZ3JvdXBpbmcgb3IgbWF0aGVtYXRpY2FsIGFnZ3JlZ2F0ZSBmdW5jdGlvbnMsIHRoZXkgYXJlIGltcG9ydGFudCBmb3IgZXhwcmVzc2luZyBxdWVyaWVzIGFuZCBhcmUgcGFydCBvZiBleHRlbmRlZCByZWxhdGlvbmFsIGFsZ2VicmEuCgokXG1hdGhmcmFre0Z9X3s8ZnVuY3Rpb25zPn0oUikkLgoKVGhlIFw8ZnVuY3Rpb25zXD4gYXJlIG9uZSBvciBtb3JlIG9mCgotICAgKkNPVU5UKiAobnVtYmVyIG9mIG9jY3VycmVuY2VzKSwKLSAgICpTVU0qIChzdW1tYXRpb24pLAotICAgKkFWRyogKGF2ZXJhZ2Ugb3IgbWVhbiksCi0gICAqTUlOKiAobWluaW11bSksIGFuZAotICAgKk1BWCogKG1heGltdW0pIG9yCi0gICBhbnkgdXNlci1kZWZpbmVkIGZ1bmN0aW9uIGRlZmluYWJsZSBvdmVyIGFuIGF0dHJpYnV0ZS4KCkZvciBleGFtcGxlLCB0aGUgZXhwcmVzc2lvbiBiZWxvdyBkZWZpbmVzIGEgcmVzdWx0YW50IHJlbGF0aW9uIHRoYXQgaGFzIHR3byBhdHRyaWJ1dGVzIChjb2x1bW5zKSB0aGF0IGFyZSB0aGUgYXZlcmFnZSBhbmQgbWF4aW11bSBjcmVkaXRzIGZvciBjb3Vyc2VzLgoKJFxtYXRoZnJha3tGfV97PEFWRyhjcmVkaXRzKSxNQVgoY3JlZGl0cyk+fShjb3Vyc2VzKSQKClRoZSByZXN1bHQgb2YgYW4gYWdncmVnYXRlIGZ1bmN0aW9uIGlzLCBsaWtlIGV2ZXJ5IHJlbGF0aW9uYWwgb3BlcmF0aW9uLCBhIHJlbGF0aW9uLCBhbGJlaXQsIGluIHNvbWUgY2FzZXMsIGEgcmVsYXRpb24gb2YgZGVncmVlIDEgYW5kIGNhcmRpbmFsaXR5IDEuCgpBIGZ1bmN0aW9uIGNhbiBhbHNvIGJlIGFuIGF0dHJpYnV0ZSBpbiB3aGljaCBjYXNlIGl0IGlzIHRoZSAiaWRlbnRpZnkgZnVuY3Rpb24iIHdoaWNoIGlzIGp1c3QgdGhlIHZhbHVlIG9mIHRoZSBhdHRyaWJ1dGUuCgojIyMjIEdyb3VwaW5nCgpHcm91cGluZyBpcyBhIGNvbW1vbiBvcGVyYXRpb24gaW4gd2hpY2ggZXF1YWwgdmFsdWVzIG9mIGEgY29sdW1uIGFyZSAiZ3JvdXBlZCIgb3IgImJhdGNoZWQiIGFuZCB0aGUgcmVzdWx0YW50IHJlbGF0aW9uIG9ubHkgY29udGFpbnMgdGhlIGdyb3Vwcy4gQSAqZ3JvdXBpbmcqIG9wZXJhdGlvbiBpcyBkZW5vdGVkIHdpdGggJHt9X3s8YXR0cmlidXRlLWxpc3Q+fVxtYXRoZnJha3tHfShSKSQKClRoZSByZXN1bHQgb2YgYW4gZ3JvdXBpbmcgZnVuY3Rpb24gaXMgYWx3YXlzIGEgcmVsYXRpb24sIGFsYmVpdCwgaW4gc29tZSBjYXNlcywgYSByZWxhdGlvbiBvZiBkZWdyZWUgMSBhbmQgY2FyZGluYWxpdHkgb2YgMS4KCkZvciBleGFtcGxlLCB0aGUgZXhwcmVzc2lvbiBiZWxvdyBkZWZpbmVzIGEgcmVzdWx0YW50IHJlbGF0aW9uIHRoYXQgaGFzIG9uZSBhdHRyaWJ1dGUgKGNvbHVtbikgdGhhdCBhcmUgdGhlIGRpZmZlcmVudCBjcmVkaXRzIGF3YXJkZWQgdG8gY291cnNlcy4KCiR7fV97PGNyZWRpdHM+fVxtYXRoZnJha3tHfShjb3Vyc2VzKSQKCkluIGFub3RoZXIgZXhhbXBsZSwgdGhlIGV4cHJlc3Npb24gYmVsb3cgZGVmaW5lcyBhIHJlc3VsdGFudCByZWxhdGlvbiB0aGF0IGhhcyB0d28gYXR0cmlidXRlcyAoY29sdW1ucykgdGhhdCBhcmUgdGhlIGNyZWRpdHMgYW5kIG51bWJlciBvZiBjb3Vyc2VzIGZvciBlYWNoIGxldmVsIG9mIGNyZWRpdHMuCgoke31fezxjcmVkaXRzPn1cbWF0aGZyYWt7R31fezxjcmVkaXRzLCBDT1VOVD59KGNvdXJzZXMpJAoKIyMjIyBFcXVpLUpvaW4KClRoZSBlcXVpLWpvaW4gKGFsc28gb2Z0ZW4gY2FsbGVkIGFuIGlubmVyIGpvaW4pIG9wZXJhdGlvbiBpcyB0aGUgbW9zdCBjb21tb24gZm9ybSBvZiBhIG11bHRpLXJlbGF0aW9uIG9wZXJhdGlvbiBhbmQgaXMgb2Z0ZW4gc2ltcGx5IGNhbGxlZCBhIGpvaW4uIEl0IGlzIGRlbm90ZWQgYnkgdGhlIHN5bWJvbCAkXGJvd3RpZSQsIGlzIHVzZWQgdG8gY29tYmluZSAqcmVsYXRlZCB0dXBsZXMqIGZyb20gdHdvIHJlbGF0aW9ucyBpbiBhIGNyb3NzIGpvaW4gKENhcnRlc2lhbiBwcm9kdWN0KS4KCkFuIGVxdWktam9pbiBoYXMgdGhlIGdlbmVyYWwgZm9ybToKCiR7Ul8xfXtcYm93dGllfV97PGpvaW5cO2NvbmRpdGlvbj59e1JfMn0kCgpUaGUgJDxqb2luXDtjb25kaXRpb24+JCBhcmUgdGhlIG1hdGNoaW5nIGF0dHJpYnV0ZXMgaW4gdGhlIHR3byBvcGVyYW5kIHJlbGF0aW9ucy4gRm9yIGV4YW1wbGUsIGFzc3VtZQoKJFJfMShwa197Ul8xfSxhXzEsYV8yLGZrKSQsIHdoZXJlICRmayQgaXMgYSBmb3JlaWduIGtleSByZWZlcmVuY2luZyAkcGtfe1JfMn0kLCBhbmQgJFJfMihwa197Ul8yfSxhXzEsYV8yKSQsCgp0aGVuIGFuIGVxdWktam9pbiBvZiB0aGUgdHdvIHJlbGF0aW9ucyBpcyBleHByZXNzZWQgYXMKCiR7Ul8xfXtcYm93dGllfV97e1JfMX0uZms9e1JfMn0ue3BrX3tSXzJ9fX17Ul8yfSQKClRoZSBkZWdyZWUgb2YgYSBuZXcgcmVsYXRpb24gcmVzdWx0aW5nIGZyb20gYW4gZXF1aS1qb2luIGlzICRkZWdyZWUoUl8xKSArIGRlZ3JlZShSXzIpJCBhbmQgdGhlIGNhcmRpbmFsaXR5IGlzIGF0IG1vc3QgJHxSXzF8IFxjZG90IHxSXzJ8JC4KCkFuIGVxdWktam9pbiBtYXRjaGVzIHR1cGxlcyBvZiB0aGUgdHdvIHJlbGF0aW9ucyBiYXNlZCBvbiBlcXVhbGl0eSBvZiB0aGUgYXR0cmlidXRlcyBpbiB0aGUgam9pbiBjb25kaXRpb24uCgpJbiBvcmRlciB0byBjb21iaW5lIHJlbGF0ZWQgdHVwbGVzLCBvbmUgb2YgdGhlIHRhYmxlcyBtdXN0IGhhdmUgdGhlIHByaW1hcnkga2V5IG9mIHRoZSBvdGhlciBhcyBhbiBhdHRyaWJ1dGUgKCppLmUuKiwgYXMgYSAqZm9yZWlnbiBrZXkqKS4gSW4gYSBvbmUtdG8tb25lIHJlbGF0aW9uc2hpcCwgaXQgZG9lcyBub3QgbWF0dGVyIHdoaWNoIHJlbGF0aW9uIGhhcyB0aGUgZm9yZWlnbiBrZXk7IGhvd2V2ZXIsIGluIGEgb25lLXRvLW1hbnkgcmVsYXRpb25zaGlwLCB0aGUgcmVsYXRpb24gb24gdGhlICJzaWRlIG9mIHRoZSBvbmUiIGhhcyB0aGUgZm9yZWlnbiBrZXkgdG8gdGhlICJzaWRlIG9mIHRoZSBtYW55Ii4KClRoZXJlIGFyZSBzZXZlcmFsIG90aGVyIHR5cGVzIG9mIGpvaW4sIGluY2x1ZGluZyBuYXR1cmFsLCBvdXRlciwgYW5kIHRoZXRhIGpvaW5zLCBidXQgdGhvc2UgYXJlIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyBsZXNzb24uCgpUbyBkZW1vbnN0cmF0ZSB0aGlzIHNpbXBsZSwgYnV0IG9mdGVuIGNvbmZ1c2luZyBvcGVyYXRpb24sIGxldCdzIHdyaXRlIGEgcXVlcnkgdGhhdCBmaW5kcyB0aGUgbmFtZSBvZiBhIGNvdXJzZSBhbmQgdGhlIG5hbWUgb2YgdGhlIGluc3RydWN0b3Igd2hvIHRlYWNoZXMgdGhlIGNvdXJzZS4gV2Ugd2lsbCByZWx5IG9uIHRoZSBwcmV2aW91cyBkZWZpbml0aW9ucyBmb3IKCiRjb3Vyc2VzKFx1bmRlcmxpbmV7Y2lkfSxudW1iZXIsdGl0bGUsY3JlZGl0cyxcYm9sZHN5bWJvbHtpaWR9KSQKCmFuZAoKJGluc3RydWN0b3JzKFx1bmRlcmxpbmV7aWlkfSwgbmFtZSxkZXBhcnRtZW50LHJhbmssZGVncmVlKSQuCgpUaGUgdHdvIHJlbGF0aW9uIGluc3RhbmNlcyBhcmUgcmVwZWF0ZWQgYmVsb3cgZm9yIGNvbnZlbmllbmNlOgoKYGBgICAgICAgICAgCmluc3RydWN0b3JzKAogIHsxMDEsICdKb3NlIFJvamEnLCAnQ29tcHV0ZXIgU2NpZW5jZScsICdBc3NvY2lhdGUnLCAnUGhEJ30sCiAgezEwMiwgJ1hpIFdhbmcnLCAnQ29tcHV0ZXIgU2NpZW5jZScsICdGdWxsJywgJ1BoRCd9LAogIHs5MDEsICdBbm5lbGllc2UgRnJhY2snLCBOVUxMLCAnQWRqdW5jdCcsICdNU2MnfSwKICB7MzAxLCAnUmFodWwgUHJhaGl0JywgJ0luZHVzdHJpYWwgRW5naW5lZXJpbmcnLCAnUmVzZWFyY2gnLCAnRFNjJ30KICB7NjAxLCAnRWxhaW5lIENhbXBiZWxsJywgJ0VkdWNhdGlvbicsICdBc3Npc3RhbnQnLCAnRWREJ30KKQoKY291cnNlcygKICB7NDQ1MywgJ0lTMjAwMCcsJ1ByaW5jaXBsZXMgb2YgSW5mb3JtYXRpb24gU2NpZW5jZScsIDQsIDEwMX0sCiAgezY2NTQsICdDUzUyMDAnLCdEYXRhYmFzZSBNYW5hZ2VtZW50IFN5c3RlbXMnLCA0LCA5MDF9LAogIHsxMDA5LCAnQ1M1MzgwJywnUHJvamVjdCBNYW5hZ2VtZW50IHdpdGggU2NydW0nLCAyLCAzMDF9LAogIHs0MTk4LCAnREE1MDIxJywnQmlnIERhdGEnLCAxLCAxMDJ9LAogIHs4ODkwLCAnREE1MDI5JywnRGF0YSBBbmFseXRpY3MgUHJvamVjdCcsIDEsIDEwMn0sCikKYGBgCgpUaGUgZXF1aS1qb2luIG9wZXJhdGlvbiBpczoKCiR7Y291cnNlc317XGJvd3RpZX1fe2NvdXJzZXMuaWlkPWluc3RydWN0b3JzLmlpZH17aW5zdHJ1Y3RvcnN9JAoKVGhlIGZvcmVpZ24ga2V5IGluICpjb3Vyc2VzKiBpcyAqaWlkKiBhbmQgaXQgInBvaW50cyB0byIgb3IgInJlZmVyZW5jZXMiIHRoZSBpbnN0cnVjdG9yIHdobyB0ZWFjaGVzIHRoZSBjb3Vyc2UgaW4gdGhlICppbnN0cnVjdG9ycyogcmVsYXRpb24uIFNvLCB0aGF0IHdpbGwgbmVlZCB0byBiZSBvdXIgam9pbiBjb25kaXRpb24uIEl0IGRvZXMgbm90IG1hdHRlciB3aGV0aGVyIHdlIHB1dCAqY291cnNlcyogb3IgKmluc3RydWN0b3JzKiBvbiB0aGUgbGVmdCBvciByaWdodCBzaWRlIG9mIHRoZSBqb2luIG9wZXJhdGlvbi4gQW4gZXF1aS1qb2luIGlzIGNvbW11dGF0aXZlLgoKV2UgbmVlZCB0byAic2NvcGUiIHRoZSBuYW1lcyBvZiB0aGUgYXR0cmlidXRlcyBhcyB0aGV5IGFyZSB0aGUgc2FtZSBpbiBib3RoIHJlbGF0aW9ucy4gV2UgY291bGQgaGF2ZSBzaW1wbGlmaWVkIHRoZSBleHByZXNzaW9uIGJ5IGZpcnN0IHJlbmFtaW5nIHRoZSAqY291cnNlcyogYW5kICppbnN0cnVjdG9ycyogcmVsYXRpb25zLiBUaGlzIGlzIGxlZnQgYXMgYW4gZXhlcmNpc2UgZm9yIHRoZSByZWFkZXIgLS0gdHJ5IGl0IG91dC4KCk5vdyB0aGUgYWJvdmUgZ2V0cyBhcyBhIHJlbGF0aW9uIHRoYXQgY29udGFpbnMgYWxsIGF0dHJpYnV0ZXMgZnJvbSAqY291cnNlcyogYW5kICppbnN0cnVjdG9ycy4qIFRvIGdldCBvbmx5IHRoZSBjb3Vyc2UgbmFtZSBhbmQgaW5zdHJ1Y3RvciBuYW1lLCB3ZSBuZWVkIHRvIGFkZCBhIHByb2plY3Rpb24uIFdlIHdpbGwgYWxzbyB0YWtlIGFkdmFudGFnZSBhIHJlbmFtZSBvcGVyYXRpb24gc28gd2UgY2FuIHNwbGl0IHRoZSBleHByZXNzaW9uLgoKJFxyaG9fe2NuaX0oeyhjb3Vyc2VzKX17XGJvd3RpZX1fe2NvdXJzZXMuaWlkPWluc3RydWN0b3JzLmlpZH17KGluc3RydWN0b3JzKX0pJAoKJFxwaV97dGl0bGUsbmFtZX0oY25pKSQKClRoZXJlIGFyZSBzZXZlcmFsIG90aGVyIHdheXMgdGhhdCB0aGlzIHF1ZXJ5IGNvdWxkIGhhdmUgYmVlbiB3cml0dGVuLiBDYW4geW91IGNvbWUgdXAgd2l0aCBvbmUgb3RoZXIgd2F5IG9mIHdyaXRpbmcgdGhpcz8gQ291bGQgeW91IGhhdmUgcHJvamVjdGVkIGF0dHJpYnV0ZXMgZmlyc3QgYmVmb3JlIGpvaW5pbmc/CgpMZXQncyBhZGQgb25lIG1vcmUgd3JpbmtsZSBhbmQgc2F5IHRoYXQgd2Ugb25seSB3YW50IHRoZSBmb3VyLWNyZWRpdCBjb3Vyc2VzIGFuZCB0aGVpciBpbnN0cnVjdG9ycy4gVGhlbiB3ZSB3b3VsZCBuZWVkIHRvIGFkZCBhIHNlbGVjdGlvbiBiZWZvcmUgdGhlIHByb2plY3Rpb24uCgokXHJob197Y30oeyhjb3Vyc2VzKX17XGJvd3RpZX1fe2NvdXJzZXMuaWlkPWluc3RydWN0b3JzLmlpZH17KGluc3RydWN0b3JzKX0pJAoKJFxyaG9fe3N9KFxzaWdtYV97Y3JlZGl0cyA9IDR9KGMpKSQKCiRccGlfe3RpdGxlLG5hbWV9KHMpJAoKKipFeGVyY2lzZSoqOiBUcnkgd3JpdGluZyB0aGUgYWJvdmUgc2V0IG9mIGV4cHJlc3Npb25zIGFzIGEgc2luZ2xlIGV4cHJlc3Npb24/IElzIGlzIHNpbXBsZXI/IEVhc2llciBvciBtb3JlIGRpZmZpY3VsdCB0byAicGFyc2UiPwoKIyMgRW50aXR5LVJlbGF0aW9uc2hpcCBEaWFncmFtcwoKRW50aXR5LVJlbGF0aW9uc2hpcCBEaWFncmFtcyAoRVJEKSBhcmUgdXNlZCB0byB2aXN1YWxpemUgcmVsYXRpb25hbCBkYXRhIG1vZGVscy4gVGhlcmUgYXJlIHNldmVyYWwgbm90YXRpb25zIHRoYXQgYXJlIGluIGNvbW1vbiB1c2U6CgotICAgSW5mb3JtYXRpb24gRW5naW5lZXJpbmcgTm90YXRpb24gKCpha2EqLCBDcm93J3MgRm9vdCBOb3RhdGlvbikKLSAgIENoZW4gTm90YXRpb24gKG5hbWVkIGFmdGVyIFBldGVyIENoZW4sIGl0cyBjcmVhdG9yKQotICAgSURFRjFYIChkZXZlbG9wZWQgYnkgdGhlIFVTIE1pbGl0YXJ5KQotICAgVU1MIENsYXNzIERpYWdyYW1zCgpXZSB3aWxsIHRha2UgYSBjbG9zZXIgbG9vayBhdCB0aGUgZmlyc3Qgb25lLCBhcyBpdCBpcyB0aGUgbW9zdCBjb21tb25seSB1c2VkIGluIHByYWN0aWNlLCBhbGJlaXQgYmVpbmcgYW4gb2xkZXIgbm90YXRpb24gZGV2ZWxvcGVkIGluIHRoZSBlYXJseSAxOTgwcy4gSXQgaXMgYmVpbmcgc2xvd2x5IHN1cHBsYW50ZWQgYnkgdGhlIFVNTCBDbGFzcyBEaWFncmFtLCB0aG91Z2guCgpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBVTUwgQ2xhc3MgRGlhZ3JhbXMsIHNlZSBbTGVzc29uIDMwLjE1MiBEb21haW4gTW9kZWxpbmcgd2l0aCBVTUwgQ2xhc3MgRGlhZ3JhbXNdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMzAudmlzbW9kZWxpbmcvbC0zMC0xNTItdW1sLWNsYXNzLWRpYWdzL2wtMzAtMTUyLmh0bWwpLgoKIyMjIElFL0Nyb3cncyBGZWV0IE5vdGF0aW9uCgpJbiB0aGUgSUUgbm90YXRpb24sIGVudGl0aWVzIChyZXByZXNlbnRlZCBhcyByZWxhdGlvbnMgaW4gdGhlIG1vZGVsIGFuZCB0YWJsZXMgaW4gYSBkYXRhYmFzZSBvciBkYXRhIGZyYW1lcyBpbiBSL1B5dGhvbiBhbmQgaWRlbnRpY2FsIHRvIGNsYXNzZXMgaW4gVU1MKSBhcmUgcm91bmRlZC1jb3JuZXIgcmVjdGFuZ2xlcyB3aXRoIHR3byBjb21wYXJ0bWVudHM6IHRoZSBlbnRpdHkgbmFtZSBpbiB0aGUgZmlyc3QgY29tcGFydG1lbnQgYW5kIHRoZSBhdHRyaWJ1dGVzIGluIHRoZSBzZWNvbmQgY29tcGFydG1lbnQuIFRoZSBzZWNvbmQgY29tcGFydG1lbnQgaXMgZ2VuZXJhbGx5IGRpdmlkZWQgaW50byB0aHJlZSBvciBmb3VyIGNvbHVtbnM6IG1vZGlmaWVyLCBuYW1lLCBkYXRhIHR5cGUgKGRvbWFpbiksIGFuZCBkZWZhdWx0IHZhbHVlLiBUaGUgbW9zdCBjb21tb24gbW9kaWZpZXJzIGFyZSAqUEsqIGZvciBwcmltYXJ5IGtleSBhbmQgKkZLKiBmb3IgZm9yZWlnbiBrZXkuIFByaW1hcnkga2V5cyBhcmUgbGlua2VkIHRvIGZvcmVpZ24ga2V5cyB3aXRoIGEgbGluZSAtLSBhbmQgdGhlIGxpbmUgZW1hbmF0ZXMgZnJvbSB0aGUgRksgYW5kIGVuZHMgYXQgdGhlIFBLIHJvdywgdW5saWtlIGluIFVNTCB3aGVyZSB0aGUgbGluZSBjb25uZWN0cyB0d28gY2xhc3Nlcy4gSW4gYW4gRVJEIGl0IGNvbm5lY3RzIGF0dHJpYnV0ZXMuCgpUaGUgZGlhZ3JhbSBiZWxvdywgY3JlYXRlZCBpbiBbTHVjaWRDaGFydF0oaHR0cDovL2x1Y2lkLmFwcCksIHNob3dzIGEgc2ltcGxlIElFIChDcm93J3MgRmVldCkgRVJEIHdpdGggdGhyZWUgZW50aXRpZXMgYW5kIHRocmVlIHJlbGF0aW9uc2hpcHMgKGlnbm9yZSB0aGUgcmVsYXRpb25zaGlwcyBmb3Igbm93OyB0aGV5IHdpbGwgYmUgZXhwbGFpbiBzaG9ydGx5KToKCiFbRVJEIGluIEluZm9ybWF0aW9uIEVuZ2luZWVyaW5nIChDcm93J3MgRm9vdCkgTm90YXRpb25dKGltYWdlcy9JRS1FUkQtMy1FbnRpdGllcy5qcGcpe3dpZHRoPSI3MCUifQoKQXMgYW4gYXNpZGUsIG1hbnkgbW9kZWxzIHVzZSBzaW5ndWxhciBub3VucyBmb3IgZW50aXRpZXMgd2hpbGUgbWFueSByZWxhdGlvbmFsIGRhdGFiYXNlcyB1c2UgdGhlIGVudGl0eSBuYW1lIGluIHRoZSBwbHVyYWwuIEl0IGlzIGEgbWF0dGVyIG9mIHByZWZlcmVuY2UuIFJlbGF0aW9uc2hpcHMgYXJlIGVhc2llciB0byBmb2xsb3cgYW5kIGV4cGxhaW4gaWYgdGhlIGVudGl0aWVzIGFyZSBpbiB0aGUgc2luZ3VsYXIuCgpXaGV0aGVyIGFuIGF0dHJpYnV0ZSBuYW1lIGlzIGJvbGQgb3Igbm90LCBlbnRpdGllcyBhcmUgc2hhZGVkIG9yIGZpbGxlZCwgYW5kIHdoZXRoZXIgdGhlIGF0dHJpYnV0ZXMgYXJlIGJhbmRlZCBoYXMgbm8gbWVhbmluZzsgaXQgaXMgb25seSBpbnRlbmRlZCB0byBpbXByb3ZlIHJlYWRhYmlsaXR5LgoKU29tZSBhbmFseXN0cyBsaWtlIHRvIHVuZGVybGluZSBwcmltYXJ5IGtleSBhdHRyaWJ1dGVzIGFuZCBpdGFsaWNpemUgZm9yZWlnbiBrZXlzLCB3aGljaCBpcyBjb25zaXN0ZW50IHdpdGggcmVsYXRpb25hbCBzY2hlbWEgZGVmaW5pdGlvbnMuCgojIyMgUmVsYXRpb25zaGlwcwoKUmVsYXRpb25zaGlwcyBjb25uZWN0IGVudGl0aWVzLiBJbiBhIHJlbGF0aW9uYWwgbW9kZWwsIHJlbGF0aW9uc2hpcHMgY2FuIG9ubHkgYmUgb25lLXRvLW9uZSBvciBvbmUtdG8tbWFueSwgYnV0IG5vdCBtYW55LXRvLW1hbnkgYXMgdGhlIGxhdHRlciB0eXBlIG9mIHJlbGF0aW9uc2hpcHMgY2Fubm90IGJlIGltcGxlbWVudGVkIHVzaW5nIHRhYmxlcyB3aXRoIGZpeGVkIG51bWJlcnMgb2YgY29sdW1ucyBvciBhdG9taWMgdmFsdWVzIGZvciB0aGUgYXR0cmlidXRlcy4KCkEgcmVsYXRpb25zaGlwIGxpbmUgY2FuIGJlIGF1Z21lbnRlZCB3aXRoIGFuIG9wdGlvbmFsIGxhYmVsIHRvIGV4cGxhaW4gdGhlIHJlbGF0aW9uc2hpcCB1c2luZyAiYnVzaW5lc3MgdGVybWlub2xvZ3kiLiBJdCBjYW4gaGVscCBleHBsYWluIHRoZSBtb2RlbCBhbmQgc2hvdWxkIGJlIHVzZWQuCgpUaGUgZGlhZ3JhbSBiZWxvdyBzaG93cyB0aHJlZSBlbnRpdGllcyBhbmQgdHdvIHJlbGF0aW9uc2hpcHMuIFRoZSByZWxhdGlvbnNoaXBzIGhhdmUgYSBsYWJlbCBhbmQgdGhlIGVuZHMgaW5kaWNhdGUgdGhlIG11bHRpcGxpY2l0eSAoZXhwbGFpbmVkIGluIG5leHQgc2VjdGlvbikuCgohW0VSRCBpbiBJbmZvcm1hdGlvbiBFbmdpbmVlcmluZyAoQ3JvdydzIEZvb3QpIE5vdGF0aW9uXShpbWFnZXMvSUUtRVJELTMtRW50aXRpZXMuanBnKXt3aWR0aD0iNzAlIn0KCiMjIyBNdWx0aXBsaWNpdHkKClRoZSBtdWx0aXBsaWNpdHkgb2YgYSByZWxhdGlvbnNoaXAgaXMgaW5kaWNhdGVkIHdpdGggc3ltYm9scyBhdCB0aGUgZW5kIG9mIHRoZSByZWxhdGlvbnNoaXAgbGluZS4gVGhlIGRpYWdyYW0gYmVsb3cgc3VtbWFyaXplcyB0aGUgc3ltYm9scyBhbmQgdGhlaXIgc2VtYW50aWNzLgoKIVtFUkQgTXVsdGlwbGljaXR5IFN5bWJvbHNdKGltYWdlcy9FUkQtcmVsYXRpb25zaGlwcy5qcGcpe3dpZHRoPSI1MCUifQoKSW4gKEkpLCB0aGVyZSBpcyBubyBtdWx0aXBsaWNpdHkgZGVmaW5lZDsgbW9zdCBsaWtlbHkgYmVjYXVzZSBpdCBpcyBub3QgeWV0IGtub3duLiBpbiAoSUkpLCB3ZSBzaG93IGEgbXVsdGlwbGljaXR5IG9mIDEgb24gb25lIHNpZGUgYW5kIG1hbnkgb2YgdGhlIG90aGVyIHNpemUgLS0gdGhlIHRyaWFuZ2xlLCBjcm93J3MgZm9vdCBsaWtlIHNoYXBlIGluZGljYXRlcyAibWFueSIgd2l0aCBubyBwYXJ0aWN1bGFyIGJvdW5kLiBBIGNpcmNsZSBpbmRpY2F0ZXMgMCwgb3IgYW4gb3B0aW9uYWwgcmVsYXRpb25zaGlwLiBTbywgKElJSSkgc2hvd3MgdGhhdCBpdCBpcyAwIG9yIG1vcmUgb24gb25lIHNpZGUgYW5kIDAgb3IgMSBvbiB0aGUgb3RoZXIuCgpUaGVyZSBpcyBubyBtZWNoYW5pc20gdG8gc2hvdyBhbnkgbXVsdGlwbGljaXR5IGJvdW5kIG90aGVyIHRoYW4gMCwgMSwgYW5kIG1hbnkuIFNvLCBhIFVNTCBDbGFzcyBEaWFncmFtIHNob3dpbmcgYSBtdWx0aXBsaWNpdHkgb2YgNS4uMTAgd291bGQgbmVlZCB0byBiZSBzaG93biBhcyAibWFueSIgaW4gYW4gSUUgRVJELgoKUmVhZGluZyB0aGUgbXVsdGlwbGljaXR5IGlzIGRvbmUgYnkgZml4aW5nIG9uZSBpbnN0YW5jZSBvZiBvbmUgZW50aXR5IGFuZCBtYXBwaW5nIGl0IHRvIHRoZSBvdGhlciBlbnRpdHkuIFNvLCBpbiB0aGUgZGlhZ3JhbSBiZWxvdywgd2UgdmlzdWFsaXplIHRoZSBmYWN0czoKCi0gICAiQW4gKG9uZSkgYXV0aG9yIHdyaXRlcyBtYW55IGxlc3NvbnMsIHBvc3NpYmx5IG5vbmUsICppLmUuKiwgdGhlcmUgYXJlIHNvbWUgYXV0aG9ycyB3aG8gZG8gbm90IHdyaXRlIGFueSBsZXNzb25zLiIKCi0gICAiQSAob25lKSBsZXNzb24gaXMgd3JpdHRlbiBieSBubyBtb3JlIHRoYW4gb25lIGF1dGhvciwgYWx0aG91Z2ggc29tZSBsZXNzb25zIGRvIG5vdCBoYXZlIGFuIGF1dGhvci4iCgpJZiBhIHJlbGF0aW9uc2hpcCBpcyAwLi4xIHRoZW4gaXQgbWVhbnMgdGhhdCB0aGUgZm9yZWlnbiBrZXkgY2FuIGJlICpOVUxMKi4KCiFbRVJEIFJlbGF0aW9uc2hpcCBFeGFtcGxlIEldKGltYWdlcy9FUkQtUmVsYXRpb25zaGlwRXhhbXBsZTEuanBnKXt3aWR0aD0iNTAlIn0KCioqRXhlcmNpc2UqKjogR28gYmFjayB0byB0aGUgZnVsbCBkaWFncmFtIGFuZCBzdGF0ZSBzZW50ZW5jZXMgYWJvdXQgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuICpDb3Vyc2UqIGFuZCAqTGVzc29uKi4KCiMjIyBKdW5jdGlvbiBFbnRpdGllcwoKSWYgdGhlcmUgaXMgYSBtYW55IHRvIG1hbnkgcmVsYXRpb25zaGlwIHRoZW4gaXQgbXVzdCBiZSByZXNvbHZlZCBieSBhZGRpbmcgYSAqanVuY3Rpb24qIG9yICphc3NvY2lhdGlvbiogZW50aXR5LiBGb3IgZXhhbXBsZSwgaWYgd2UgYWxsb3cgYSBsZXNzb24gdG8gYmUgaW4gbW9yZSB0aGFuIG9uZSBjb3Vyc2UsIHRoZW4gd2Ugd291bGQgaGF2ZSBhIG1hbnktdG8tbWFueSByZWxhdGlvbnNoaXAgYmV0d2VlbiAqQ291cnNlKiBhbmQgKkxlc3NvbiogYW5kIHRoYXQgd291bGQgcmVxdWlyZSB0aGUgaW50cm9kdWN0aW9uIG9mIHNvbWUga2luZCBvZiBqdW5jdGlvbiB0YWJsZS4gSW4gdGhlIGV4YW1wbGUgYmVsb3csIHdlIGNob3NlIHRvIG5hbWUgdGhhdCB0YWJsZSAqQ29udGVudE1hcCouCgojIyMgQWRkaW5nIEZvcmVpZ24gS2V5cwoKVGhlIGZvcmVpZ24ga2V5IGlzIGEgIm9uZS10by1tYW55IiByZWxhdGlvbnNoaXAgaXMgYWRkZWQgdG8gdGhlIHRhYmxlIG9uIHRoZSBzaWRlIG9mIHRoZSAibWFueSIgYW5kIGl0IGNvbnRhaW5zIHRoZSB2YWx1ZSBvZiB0aGUgcHJpbWFyeSBrZXkgaXMgdGhlIHNpZGUgb2YgdGhlICJvbmUiLiBTYW1wbGUgdGFibGVzIGNhbiBoZWxwIGNsYXJpZnkgd2hlcmUgdGhleSBzaG91bGQgYmUgcGxhY2VkLgoKIVtJbXBsZW1lbnRpbmcgT25lLXRvLU1hbnkgUmVsYXRpb25zaGlwcyB3aXRoIEZLL1BLIExpbmtzXShpbWFnZXMvcGstZmstb25lLXRvLW1hbnkuanBnKXt3aWR0aD0iODAlIn0KClRoZSBxdWljayBiaXRlIHR1dG9yaWFsIGJlbG93IGlsbHVzdHJhdGVzIGhvdyB0byByZXNvbHZlIG9uZS10by1tYW55IHJlbGF0aW9uc2hpcHMgd2l0aCBGSy9QSyBsaW5rcy4KCjxpZnJhbWUgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyNzAiIHNyYz0iaHR0cHM6Ly93d3cueW91dHViZS5jb20vZW1iZWQveWUzY2tZc0JQenMiIHRpdGxlPSJZb3VUdWJlIHZpZGVvIHBsYXllciIgZnJhbWVib3JkZXI9IjEiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgY2xpcGJvYXJkLXdyaXRlOyBlbmNyeXB0ZWQtbWVkaWE7IGd5cm9zY29wZTsgcGljdHVyZS1pbi1waWN0dXJlIiBhbGxvd2Z1bGxzY3JlZW4gZGF0YS1leHRlcm5hbD0iMSI+Cgo8L2lmcmFtZT4KCiMjIFNRTAoKQXMgd2Ugc2F3LCB0aGUgcmVsYXRpb25hbCBtb2RlbCBoYXMgYSBtZWNoYW5pc20gaW4gd2hpY2ggdG8gZXhwcmVzcyAicXVlcmllcyIgb24gcmVsYXRpb25zIGNhbGxlZCAqcmVsYXRpb25hbCBhbGdlYnJhKiwgYnV0IHRoYXQgaXMgbm90IHVzZWZ1bCBpbiBwcmFjdGljZS4gSW5zdGVhZCwgZGF0YSBpcyBleHRyYWN0ZWQgZnJvbSB0YWJsZXMgaW4gYSByZWxhdGlvbmFsIG1vZGVsICgqaS5lLiosIHRhYmxlcykgdXNpbmcgdGhlIGluZHVzdHJ5LXN0YW5kYXJkIHF1ZXJ5IGxhbmd1YWdlICpTUUwqLiBUaGlzIHNlY3Rpb24gcmV2aWV3cyB0aGUga2V5IG9wZXJhdGlvbnMgb2YgU1FMIGZvciByZXRyaWV2aW5nIGRhdGEgZnJvbSBvbmUgb3IgbW9yZSB0YWJsZXMuIEluIHRoZSBjb250ZXh0IG9mIFNRTCwgd2UgZ2VuZXJhbGx5IHByZWZlciB0aGUgdGVybSAqdGFibGUqIG92ZXIgKnJlbGF0aW9uKi4KCk9uZSBub3Rld29ydGh5IGl0ZW0gaXMgdGhhdCB0YWJsZXMgaW4gcmVsYXRpb25hbCBkYXRhYmFzZXMgYXJlIG5vdCwgaW4gZmFjdCwgcmVsYXRpb25zIGFuZCB0aGVyZWZvcmUgc2V2ZXJhbCB1c2VmdWwgdGhlb3JlbXMgYWJvdXQgdGhlIHJlbGF0aW9uYWwgYWxnZWJyYSBkbyBub3QgaG9sZCBpbiB0aGUgU1FMIGNvdW50ZXJwYXJ0LiBUaGUgcmVhc29uIGlzIHRoYXQgdGhlIFNRTCB0YWJsZSBtb2RlbCBpcyBhIGJhZyAobXVsdGlzZXQpLCByYXRoZXIgdGhhbiBhIHNldCB3aGljaCBjYW5ub3QgY29udGFpbiBkdXBsaWNhdGVzIGFuZCBhbGwgdHVwbGVzIGluIHRoZSBzZXQgbXVzdCBiZSB1bmlxdWUuCgojIyMgRGF0YWJhc2VzCgpUYWJ1bGFyIGRhdGEgY2FuIGJlIHN0b3JlZCBpbiBpbi1tZW1vcnkgZGF0YSBmcmFtZXMgY3JlYXRlZCBmcm9tIHJlYWRpbmcgQ1NWLCBFeGNlbCwgb3IgWE1MIGZpbGVzLiBJbiBhZGRpdGlvbiwgdGFibGVzIGNhbiBiZSBzdG9yZWQgaW4gYSBkYXRhYmFzZSB3aGljaCBpcyBwcmVmZXJhYmxlIHdoZW4gdGhlcmUgYXJlIG1hbnkgdGFibGVzIGFuZCB0aGUgdGFibGVzIGFyZSBsYXJnZXIgdGhhbiB3b3VsZCBmaXQgaW50byBtZW1vcnkgb3Igd2hlbiBtdWx0aXBsZSBhcHBsaWNhdGlvbnMgbmVlZCBhY2Nlc3MgdG8gdGhlIHNhbWUgZGF0YS4KClRoZXJlIGFyZSBtYW55IHJlbGF0aW9uYWwgZGF0YWJhc2UgaW4gdXNlIHRvZGF5LCBpbmNsdWRpbmcgU1FMaXRlLCBNeVNRTCwgTWFyaWFEQiwgT3JhY2xlLCBNaWNyb3NvZnQgU1FMIFNlcnZlciwgSW5mb3JtaXgsIERCMiwgSmF2YURCLCBhbW9uZyBtYW55IG90aGVycy4gVGhlIGFmb3JlbWVudGlvbmVkIGFyZSBhbGwgcmVsYXRpb25hbCBkYXRhYmFzZXMgc3RvcmluZyBkYXRhIGluIHRhYmxlcy4gT3JnYW5pemF0aW9ucyBhbHNvIG1ha2UgdXNlIG9mIG5vbi1yZWxhdGlvbmFsIGRhdGFiYXNlcyB0aGF0IHN0b3JlIGRhdGEgaW4gYSBkaWZmZXJlbnQgc3RydWN0dXJlOiBoaWVyYXJjaGljYWwsIGtleS92YWx1ZSwgY29sdW1uYXIsIG9yIGFzIGRvY3VtZW50cy4gVGhlc2UgdHlwZXMgb2YgZGF0YWJhc2VzIGluY2x1ZGUgQ291Y2hEQiwgTW9uZ29EQiwgTmVvNEosIGFtb25nIG1hbnkgb3RoZXJzLCBhbmQgYXJlIGFsc28gb2Z0ZW4gY2FsbGVkICpOb1NRTCBkYXRhYmFzZXMqIGFzIHRoZSBwcmltYXJ5IHF1ZXJ5IGxhbmd1YWdlIGlzIG5vdCBTUUwuCgojIyMgU1FMIGluIFIKCkluIFIsIHRoZXJlIGFyZSB0d28gd2F5cyB0byB1c2UgU1FMIGZvciByZXRyaWV2aW5nIGRhdGEgZnJvbSB0YWJsZXMgZGVwZW5kaW5nIG9uIHdoZXJlIHRoZSB0YWJsZXMgYXJlOiB0YWJsZXMgaW4gbWVtb3J5IHZlcnN1cyB0YWJsZXMgaW4gYSBkYXRhYmFzZS4gRm9ydHVuYXRlbHksIGl0IGRvZXMgbm90IG1hdHRlciB3aGVyZSB0aGUgdGFibGVzIGFyZSwgd2UgY2FuIHVzZSBTUUwgdG8gcmV0cmlldmUgZGF0YSBmcm9tIHRoZW0uIEhvd2V2ZXIsIHdlIG5lZWQgdG8ga25vdyB3aGVyZSB0aGUgdGFibGVzIGFyZSBzdG9yZWQgc28gdGhhdCB3ZSB1c2UgdGhlIGNvcnJlY3Qgc2V0IG9mIGZ1bmN0aW9ucyBmcm9tIHRoZSByaWdodCBwYWNrYWdlLgoKLSAgICoqc3FsZGYqKjogQSBwYWNrYWdlIGNvbnRhaW5pbmcgZnVuY3Rpb25zIHRvIHJldHJpZXZlIGRhdGEgdXNpbmcgU1FMIGZyb20gaW4tbWVtb3J5IGRhdGEgZnJhbWVzLiBUaGlzIHBhY2thZ2UgaXMgYWN0dWFsbHkgYnVpbHQgb24gdG9wIG9mIHRoZSAqKlJTUUxpdGUqKiBwYWNrYWdlLgotICAgKipSU1FMaXRlKio6IEEgcGFja2FnZSBjb250YWluaW5nIGZ1bmN0aW9ucyB0byByZXRyaWV2ZSBkYXRhIHVzaW5nIFNRTCBmcm9tIHRhYmxlcyBpbiBhIFNRTGl0ZSBkYXRhYmFzZS4KCk9mIGNvdXJzZSwgaWYgd2Ugd2VyZSB0byB1c2UgYSBkaWZmZXJlbnQgZGF0YWJhc2Ugc3VjaCBhcyBNeVNRTCwgd2Ugd291bGQgbmVlZCB0byB1c2UgYSBkaWZmZXJlbnQgcGFja2FnZSAoKmUuZy4qLCAqKlJNeVNRTCoqKSBidXQgY291bGQgc3RpbGwgdXNlIHRoZSBzYW1lIFNRTCBxdWVyaWVzLCBtYWtpbmcgU1FMIGEgbW9yZSBvciBsZXNzIHVuaXZlcnNhbCBxdWVyeSBsYW5ndWFnZS4KCkJlZm9yZSB3ZSBkaXZlIGludG8gZGV0YWlscywgbm90ZSB0aGF0IGFsbCB0YWJsZSBuYW1lcyBhbmQgYWxzbyBhbGwgZGF0YSBmcmFtZXMgdXNlZCB3aXRoICoqc3FsZGYqKiBjYW5ub3QgY29udGFpbiBhIHBlcmlvZCAoLikgb3IsIGlmIHRoZXkgZG8sIHRoZXkgbXVzdCBiZSBlc2NhcGVkIGJ5IGVuY2xvc2luZyB0aGUgdGFibGUgb3IgZGF0YSBmcmFtZSBuYW1lIGluIGJhY2t0aWNrcywgKmUuZy4sKiwgXGBkZi50YWJsZVxgIGFuZCBub3Qgc2ltcGx5ICpkZi50YWJsZSogYXMgdGhlIC4gaXMgaW50ZXJwcmV0ZWQgYXMgYSBzY29wZSBvcGVyYXRvciwgKmkuZS4qLCAqZGYudGFibGUqIG1lYW5zIHRvIFNRTCB0aGUgKnRhYmxlKiBjb2x1bW4gaW4gdGhlIHRhYmxlICpkZiouCgojIyMgc3FsZGYKCioqc3FsZGYqKiBpcyBhIHBhY2thZ2UgdGhhdCBhbGxvd3MgZGF0YSBmcmFtZXMgdG8gYmUgcXVlcmllZCB3aXRoIFNRTCBhcyBpZiB0aGV5IHdlcmUgdGFibGVzIGluIGEgZGF0YWJhc2UuIEl0IGFsbG93cyBhIHByb2dyYW1tZXIgb3IgZGF0YSBhbmFseXN0IHRvIHVzZSBTUUwgdG8gYWNjZXNzLCBwcm9jZXNzLCBzZWFyY2gsIGFuZCBhZ2dyZWdhdGUgZGF0YSBpbiBkYXRhIGZyYW1lcy4KCk1hbnkgcXVlcmllcywgd2hpbGUgZ2VuZXJhbGx5IGRvYWJsZSBpbiBCYXNlIFIsIGFyZSBvZnRlbiBzaW1wbGVyIHdpdGggYSBTUUwgcXVlcnkgLS0gYWxiZWl0IGEgYml0IHNsb3dlciwgYnV0IHRoYXQgcmVkdWN0aW9uIGluIHBlcmZvcm1hbmNlIGlzIG9mdGVuIG5vdCBwZXJjZXB0aWJsZS4gVW5kZXJuZWF0aCB0aGUgaG9vZCwgdGhlICoqc3FsZGYqKiBwYWNrYWdlIGFjdHVhbGx5IGxvYWRzIHRoZSBkYXRhIGZyYW1lIGludG8gYW4gaW4tbWVtb3J5IFNRTGl0ZSBkYXRhYmFzZS4KCioqc3FsZGYqKiBpcyBwcmltYXJpbHkgdXNlZCB0bzoKCi0gICBzdW1tYXJpemUgb2YgZGF0YSBpbiBkYXRhIGZyYW1lcwotICAgaGFybW9uaXplIGRhdGEgYWNjZXNzIHZpYSBTUUwgZm9yIGFsbCB0YWJ1bGFyIGRhdGEKLSAgIGltcG9ydCBwYXJ0cyBvZiBhIENTVgoKSXQgaXMgYWxzbyB1c2VmdWwgZm9yIGxlYXJuaW5nIFNRTCB3aXRob3V0IGJlaW5nIGNvbmNlcm5lZCBhYm91dCBzZXR0aW5nIHVwIGEgc2VwYXJhdGUgcmVsYXRpb25hbCBkYXRhYmFzZS4KCltMZXNzb24gNi4zMzAgUXVlcnlpbmcgRGF0YSBGcmFtZXMgaW4gUiB3aXRoIHNxbGRmXShodHRwOi8vYXJ0aWZpY2l1bS51cy9sZXNzb25zLzA2LnIvbC02LTMzMC1zcWxkZi9sLTYtMzMwLmh0bWwpIGV4cGxhaW5zIGluIG1vcmUgZGV0YWlsIGhvdyB0byB1c2UgXCpcKnNxbGRmXCogdG8gcXVlcnkgZGF0YSBmcmFtZXMgaW4gUi4KCiMjIyBFeGFtcGxlIFJlbGF0aW9uYWwgTW9kZWwKCkluIHRoZSBleHBsYW5hdGlvbiBiZWxvdywgd2Ugd2lsbCB1c2UgYSBzZXQgb2YgdGFibGVzIGZyb20gdGhlIHBhY2thZ2UgKipueWNmbGlnaHRzMTMqKiBjcmVhdGVkIGJ5IEhhZGxleSBXaWNraGFtIG9mIFJTdHVkaW8uIFRoZSB0YWJsZXMgaW4gdGhhdCBwYWNrYWdlIGFyZSBhY3R1YWxseSAqdGliYmxlcyouIFRpYmJsZXMgYXJlIGEgdHlwZSBvZiBkYXRhIGZyYW1lIGNyZWF0ZWQgYnkgV2lja2hhbSBhcyBwYXJ0IG9mIGhpcyAqKnRpZHl2ZXJzZSoqIHBhY2thZ2UuIEhvd2V2ZXIsIHdlIHdpbGwgdHJlYXQgdGhlIHRpYmJsZXMgYXMgZGF0YSBmcmFtZXMgYW5kIGlnbm9yZSB0aGUgZmFjdCB0aGF0IHRoZXkgYXJlICJ0aWJibGVzIi4KCmBgYHtyfQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKYGBgCgpUaGUgZGlhZ3JhbSBiZWxvdyBpbGx1c3RyYXRlcyB0aGUgZGF0YSBtb2RlbCBmb3IgdGhlIHRhYmxlcyBpbiB0aGF0IHBhY2thZ2UsIGV4cHJlc3NlZCBhcyBhbiBFbnRpdHkgUmVsYXRpb25hbCBEaWFncmFtIChFUkQpIGluIHRoZSBJbmZvcm1hdGlvbiBFbmdpbmVlcmluZyAoKmFrYSogIkNyb3cncyBGb290Iikgbm90YXRpb24uIFRoaXMgbm90YXRpb24gaXMgY29tbW9uIGZvciByZWxhdGlvbmFsIGRhdGEgbW9kZWxzLCBhbHRob3VnaCBhIFVNTCBDbGFzcyBEaWFncmFtIGNvdWxkIGFsc28gaGF2ZSBiZWVuIHVzZWQuIElycmVsZXZhbnQgYXR0cmlidXRlcyAoY29sdW1ucykgaGF2ZSBiZWVuIG9taXR0ZWQgZnJvbSB0aGUgZGlhZ3JhbSBmb3IgdGhlIHNha2Ugb2YgcmVhZGFiaWxpdHkuCgohW10oaW1hZ2VzL255Y2ZsaWdodHMxMy1FUkQucG5nKXt3aWR0aD0iODAlIn0KCmBgYHtyIGludmVzdGlnYXRlRGF0YU1vZGVsLCBlY2hvPUYsIGV2YWw9RiwgaW5jbHVkZT1GQUxTRX0KIyB0aGlzIGNvZGUgaXMgbm90IHBhcnQgb2YgdGhlIGtuaXR0ZWQgZmlsZQoKc3RyKGFpcnBvcnRzKQpoZWFkKGFpcnBvcnRzKQoKc3RyKGZsaWdodHMpCmhlYWQoZmxpZ2h0cykKCnN0cihhaXJsaW5lcykKaGVhZChhaXJsaW5lcykKCnN0cihwbGFuZXMpCmhlYWQocGxhbmVzKQoKc3RyKHdlYXRoZXIpCmhlYWQod2VhdGhlcikKYGBgCgpFeHBsb3JlIHRoZSBkYXRhIG1vZGVsLiBJbnNwZWN0IGVhY2ggb2YgdGhlIGRhdGEgZnJhbWVzLgoKIyMjIFN1cnJvZ2F0ZSBBcnRpZmljaWFsIFByaW1hcnkgS2V5cwoKT25lIGl0ZW0gb2YgaW50ZXJlc3RpbmcgaXMgdGhhdCBzb21lIG9mIHRoZSBkYXRhIGZyYW1lcyBoYXZlIGNvbXBvc2l0ZSBwcmltYXJ5IGtleXMgKCppLmUuKiwgcHJpbWFyeSBrZXlzIHRoYXQgY29uc2lzdCBvZiBzZXZlcmFsIGNvbHVtbnMpLiBGb3IgZXhhbXBsZSwgdGhlIHByaW1hcnkga2V5IHRvIHVuaXF1ZWx5IGlkZW50aWZ5IGEgcm93IGluICpmbGlnaHRzKiBpcyB0aGUgY29tYmluYXRpb24gb2YgdGhlIGNhcnJpZXIsIGZsaWdodCBbbnVtYmVyXSwgeWVhciwgbW9udGgsIGFuZCBkYXkuIFRoaXMgY2FuIGJlIHZlcnkgaW5jb252ZW5pZW50IHdoZW4gd2Ugd2FudCB0byBjb21iaW5lIGRhdGEgZnJvbSBtdWx0aXBsZSBkYXRhIGZyYW1lcyB3aXRoIGpvaW5zLiBTbywgd2UgZ2VuZXJhbGx5IGFkZCBhIG5ldyBjb2x1bW4sIGFuIGlkZW50aWZpZXIsIHRoYXQgaXMgYSBzaW5nbGUgbnVtYmVyIHRoYXQgaXMgdW5pcXVlIGFuZCB1c2UgdGhhdCBhcyB0aGUgcHJpbWFyeSBrZXkgaW5zdGVhZC4gU3VjaCAiaW52ZW50ZWQgb3IgYXJ0aWZpY2lhbCIgcHJpbWFyeSBrZXlzIGFyZSBhbHNvIG9mdGVuIGNhbGxlZCBhIHN1cnJvZ2F0ZSBrZXkuIFRoZXkgYXJlIHF1aXRlIGNvbW1vbi4KCkEgc2ltcGxlIHdheSB0byBkbyB0aGlzIGlzIHRvIGFkZCBhIGNvbHVtbiB0aGF0IGlzIGEgc2VxdWVuY2UgbnVtYmVyLiBUaGUgY29kZSBiZWxvdyBkb2VzIHRoaXMgLS0gbm90ZSB0aGUgdXNlIG9mIGEgc3RhcnRpbmcgbnVtYmVyIHNvIHRoYXQgaXQgaXMgYSBiaXQgZWFzaWVyIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4ga2V5cyBvZiBkaWZmZXJlbnQgZGF0YSBmcmFtZXM7IG5vdCBuZWNlc3NhcnkgYnV0IGNvbnZlbmllbnQuCgpgYGB7ciBhZGRBcnRpZmljaWFsS2V5c30KIyBhZGQgYXJ0aWZpY2lhbCBrZXkgdG8gImZsaWdodHMiCmZsaWdodHMkZmlkIDwtIHNlcSgxMDAwOigxMDAwK25yb3coZmxpZ2h0cyktMSkpCgojIGFkZCBhcnRpZmljaWFsIGtleSB0byAid2VhdGhlciIKd2VhdGhlciR3aWQgPC0gc2VxKDE6KG5yb3cod2VhdGhlcikpKQpgYGAKCldlIG5vdyBoYXZlIHR3byBwb3NzaWJsZSBwcmltYXJ5IGtleXMgZm9yIHRoZSBkYXRhIGZyYW1lcyAqd2VhdGhlciogYW5kICpmbGlnaHRzKjogdGhlIGNvbWJpbmF0aW9uIG9mIHRoZSBjb2x1bW5zIGFuZCB0aGUgbmV3IHN1cnJvZ2F0ZSBJRC4gV2Ugbm93IG5lZWQgdG8gZGVzaWduYXRlIG9uZSB0aGUgcHJpbWFyeSBrZXksIG1ha2luZyB0aGUgb3RoZXIgYW4gKmFsdGVybmF0ZSBrZXkqLiBFYWNoIHdvdWxkIGJlIGNhbGxlZCBhICJjYW5kaWRhdGUga2V5Ii4gT25lIG9mIHRoZSBjYW5kaWRhdGUga2V5cywgdGhlIGNvbXBvdW5kIGtleSwgaXMgYSBuYXR1cmFsIGtleSBhcyBpdCBjb21lcyBmcm9tIGRhdGEgaW4gdGhlIGJ1c2luZXNzLCB3aGlsZSB0aGUgb3RoZXIgaXMgYW4gYXJ0aWZpY2lhbCBrZXkuCgojIyMgR2V0dGluZyBTdGFydGVkIHdpdGggU1FMCgpOb3RlIHRoYXQgU1FMIGlzIG5vdCBjYXNlIHNlbnNpdGl2ZSwgc28gYFNFTEVDVGAgYW5kIGBzZWxlY3RgIGFyZSBlcXVhbGx5IHZhbGlkLiBCeSBjb252ZW50aW9uLCBTUUwga2V5d29yZHMgYXJlIHdyaXR0ZW4gaW4gdXBwZXIgY2FzZS4gVGhlIHNlbWljb2xvbiBpcyBnZW5lcmFsbHkgcmVxdWlyZWQgYnkgc29tZSAqYWQgaG9jKiBxdWVyeSB0b29scyBvciB3aGVuIHVzZWQgaW4gYSBzY3JpcHQuIFdlIGdlbmVyYWxseSBpbmNsdWRlIGl0IGZvciBjb21wYXRpYmlsaXR5IGFuZCBkdWUgdG8gY29udmVudGlvbi4KClRoZSByZXN1bHQgb2YgZXZlcnkgU1FMIHN0YXRlbWVudCBpcyBhIHRhYmxlIHdoaWNoIG1lYW5zIHRoYXQgYSBxdWVyeSBjYW4gYmUgInF1ZXJpZWQiLiBJbiBvdGhlciB3b3JkcywgYSBxdWVyeSBjYW4gYmUgdXNlZCB3aGVyZXZlciBhIHRhYmxlIGlzIHJlcXVpcmVkIGFuZCBzbyB3ZSBjYW4gZm9ybXVsYXRlIHN1YnF1ZXJpZXMuCgpCZWZvcmUgd2UgY2FuIHVzZSAqKnNxbGRmKiosIHJlbWVtYmVyIHRvIGxvYWQgdGhlIGxpYnJhcnkgLS0gYW5kIGluc3RhbGwgaWYgbm90IHlldCBpbnN0YWxsZWQuCgpJbiB0aGUgYHNxbGRmOjpzcWxkZigpYCBmdW5jdGlvbiwgdGFibGVzIGFyZSBwcmVzdW1lZCB0byBiZSB0aGUgaWRlbnRpZmllcnMgZm9yIGRhdGEgZnJhbWVzIHRoYXQgaGF2ZSBiZWVuIGxvYWRlZCBvciBjcmVhdGVkLiBTbywgaW4gdGhlIFNRTCBzdGF0ZW1lbnQgdGhhdCBpcyBwYXNzZWQgYHNxbGRmOjpzcWxkZigpYCBiZWxvdywgImFpcmxpbmVzIiByZWZlcnMgdG8gdGhlIGRhdGEgZnJhbWUgKHdlbGwsIHRpYmJsZSwgdG8gYmUgbW9yZSBwcmVjaXNlLCBidXQgdGhhdCBkb2Vzbid0IG1hdHRlcikgKmFpcmxpbmVzKi4KClRoZSBTUUwgc3RhdGVtZW50IGNhbiBiZSBlbmNsb3NlZCBlaXRoZXIgaW4gc2luZ2xlICcgb3IgZG91YmxlIHF1b3RlcyAiLiBVc2luZyBvbmUgb3IgdGhlIG90aGVyIGlzIHVzZWZ1bCBpZiB3ZSB3YW50IHRvIHRoZSB0aGUgb3RoZXIgd2l0aGluIHRoZSBxdWVyeS4KCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoc3FsZGYpCmBgYAoKIyMjIFJldHJpZXZpbmcgUm93czogU0VMRUNUCgpUaGUgYFNFTEVDVGAgc3RhdGVtZW50IGlzIHVzZWQgdG8gcmV0cmlldmUgcm93cyB0aGF0IG1lZXQgY2VydGFpbiBjb25kaXRpb25zLiBJdCBoYXMgdGhlIGdlbmVyYWwgZm9ybToKCmBgYCAgICAgICAgIApTRUxFQ1Qge2NvbHVtbnMgfCAqIHwgZXhwcmVzc2lvbiB8IGxpdGVyYWx9IAogIEZST00ge3RhYmxlc30KIFtXSEVSRSB7Y29uZGl0aW9uc31dCiBbR1JPVVAgQlkge2NvbHVtbn1dCiBbSEFWSU5HIHtncm91cCBjcml0ZXJpYX1dCiBbT1JERVIgQlkge2NvbHVtbnN9XQogW0xJTUlUIG51bWJlcl07CmBgYAoKIyMjIFJldHJpZXZlIEFsbCBSb3dzCgpUaGUgc2ltcGxlc3QgYFNFTEVDVGAgcXVlcnkgcmV0cmlldmVzIGFsbCByb3dzIGZyb20gYSBzaW5nbGUgdGFibGUuIFRoZSAnXConIHNwZWNpZmllcyBhbGwgY29sdW1ucyBpbiBhIHRhYmxlLiBJbiB0aGUgZXhhbXBsZSBiZWxvdywgd2UgZ2V0IHRoZSBmaXJzdCBmaXZlIHJvd3MgZnJvbSB0aGUgdGFibGUgKmFpcmxpbmVzKiBieSB1c2luZyB0aGUgKkxJTUlUKiBrZXl3b3JkOyBpZiB3ZSBkbyBub3Qgc3BlY2lmeSBhIGxpbWl0IHRoZW4gd2Ugd291bGQgZ2V0IGFsbCByb3dzIHdoaWNoIG1pZ2h0IGJlIHZlcnkgbGFyZ2UuCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQpsaWJyYXJ5KHNxbGRmKQoKc3FsZGY6OnNxbGRmKCJTRUxFQ1QgKiBGUk9NIGFpcmxpbmVzIExJTUlUIDUiKQpgYGAKCklmIHdlIG5lZWQgdG8gcHJvY2VzcyB0aGUgcmVzdWx0IGZ1cnRoZXIsIHRoZW4gd2Ugd291bGQgYXNzaWduIHRoZSByZXR1cm4gdmFsdWUgb2YgYHNxbGRmOjpzcWxkZigpYCB0byBhIHZhcmlhYmxlLgoKYGBge3IgfQpycyA8LSBzcWxkZjo6c3FsZGYoIlNFTEVDVCAqIEZST00gYWlybGluZXMgTElNSVQgNSIpCmBgYAoKU2luY2UgdGhlIHJlc3VsdCBpcyBhIGRhdGEgZnJhbWUsIHdlIGNhbiB0aGVuIGFwcGx5IFIgZnVuY3Rpb25zIHRvIHRoYXQgZGF0YSBmcmFtZS4KCioqV0FSTklORyoqLiBJZiB5b3VyIGRhdGEgZnJhbWUgaWRlbnRpZmllciBuYW1lIGNvbnRhaW5zIGEgcGVyaW9kIChjb21tb24gd2F5IHRvIG5hbWUgZGF0YSBmcmFtZXMgaW4gUiBhcyBwZXJpb2QgaXMgYSBsZWdhbCBpZGVudGlmaWVyIGNoYXJhY3RlciBpbiBSKSwgdGhlbiB5b3UgbXVzdCBlc2NhcGUgdGhlIGRhdGEgZnJhbWUgd2hlbiB1c2VkIGluICoqc3FsZGYqKiB3aXRoIGJhY2t0aWNrcy4gTGV0J3Mgc2F5IHRoZSBkYXRhIGZyYW1lIHdhcyAqZGYuYWlybGluZXMqIGFuZCBub3QgKmFpcmxpbmVzKi4gTm90IGVuY2xvc2luZyB0aGUgZGF0YSBmcmFtZSBpbiBiYWNrdGlja3MgY2hhbmdlcyBpdHMgbWVhbmluZyBmb3IgU1FMIHRvOiBhY2Nlc3MgY29sdW1uICphaXJsaW5lcyogaW4gdGhlIHRhYmxlICpkZiogYW5kIHNpbmNlIHRoYXQgZG9lcyBub3QgZXhpc3QsIGl0J2xsIHRocm93IGEgU1FMIGVycm9yLgoKVGhlIGNvZGUgZnJhZ21lbnQgYmVsb3cgaWxsdXN0cmF0ZXMgdGhlIGNvcnJlY3Qgd2F5IHRvIGRvIHRoaXMuCgpgYGB7ciBlY2hvPUZ9CmRmLmFpcmxpbmVzIDwtIGFpcmxpbmVzCmBgYAoKYGBge3IgfQpycyA8LSBzcWxkZjo6c3FsZGYoIlNFTEVDVCAqIEZST00gYGRmLmFpcmxpbmVzYCBMSU1JVCA1IikKYGBgCgpUaGUgc2FtZSBhcHByb2FjaCBvZiB1c2luZyBiYWNrdGlja3MgYWxzbyBhcHBsaWVzIHRvIGNvbHVtbiBuYW1lcyBjb250YWluaW5nIHBlcmlvZHMuCgojIyMgUmV0cmlldmUgU3BlY2lmaWMgQ29sdW1ucwoKUmF0aGVyIHRoYW4gdXNpbmcgJ1wqJyB0byBnZXQgYWxsIGNvbHVtbnMsIHdlIGNhbiBzcGVjaWZ5IGEgc3Vic2V0IG9mIHRoZSBjb2x1bW5zIGJ5IG5hbWUuCgpgYGB7ciB9CnJzIDwtIHNxbGRmOjpzcWxkZigiU0VMRUNUIGNhcnJpZXIgRlJPTSBhaXJsaW5lcyBMSU1JVCA1IikKcHJpbnQocnMpCmBgYAoKIyMjIE9yZGVyaW5nIFJlc3VsdHM6IE9SREVSIEJZCgpUaGUgcm93cyBhcmUgcmV0dXJuZWQgaW4gc29tZSBvcmRlciAtLSBhbmQgbGlrZWx5IG5vdCB0aGUgb3JkZXIgaW4gd2hpY2ggdGhleSB3ZXJlIGluc2VydGVkIG9yIG1pZ2h0IGhhdmUgYXBwZWFyZWQgaW4gYW5vdGhlciBxdWVyeS4gU28sIG5ldmVyIHJlbHkgb24gdGhlIG9yZGVyIHVubGVzcyB5b3Ugc3BlY2lmaWNhbGx5IHNwZWNpZnkgYW4gb3JkZXJpbmcgd2l0aCBgT1JERVIgQllgLiBBbiBvcHRpb25hbCBzb3J0aW5nIGRpcmVjdGlvbiBjYW4gYmUgYWRkZWQ7ICpERVNDKiBvciBkZXNjZW5kaW5nIGFuZCAqQVNDKiBmb3IgYXNjZW5kaW5nLgoKYGBge3IgfQpycyA8LSBzcWxkZjo6c3FsZGYoIlNFTEVDVCBuYW1lIEZST00gYWlybGluZXMgT1JERVIgQlkgbmFtZSBERVNDIExJTUlUIDUiKQpwcmludChycykKYGBgCgojIyMgUmVuYW1lIENvbHVtbnMKCkJ5IGRlZmF1bHQsIHRoZSBuYW1lcyBvZiB0aGUgY29sdW1ucyBpbiB0aGUgcmVzdWx0IHRhYmxlIGFyZSB0aGUgc2FtZSBhcyB0aGUgc291cmNlIHRhYmxlLCBidXQgdGhleSBjYW4gYmUgcmVuYW1lZCB1c2luZyBgQVNgLgoKYGBge3IgfQpycyA8LSBzcWxkZjo6c3FsZGYoIlNFTEVDVCBuYW1lIEFTIGFpcmxpbmUgRlJPTSBhaXJsaW5lcyBPUkRFUiBCWSBuYW1lIExJTUlUIDUiKQpwcmludChycykKYGBgCgojIyMgRXhwcmVzc2lvbnMKClRoZSBTRUxFQ1Qgc3RhdGVtZW50IGNhbiBpbmNsdWRlIGV4cHJlc3Npb25zIGFzIGEgImNvbHVtbiIuIFRoZSBleHByZXNzaW9uIGlzIG9mdGVuIHJlbmFtZWQgd2l0aCBhbiAqQVMqLiBJbiB0aGUgZXhhbXBsZSBiZWxvdywgd2UgY3JlYXRlIGEgbmV3IGNvbHVtbiAiaHJfZGVsYXkiIGFzIHRoZSBkZXBhcnR1cmUgZGVsYXkgZXhwcmVzc2VkIGluIGhvdXJzIHJhdGhlciB0aGFuIG1pbnV0ZXMsIHJvdW5kaW5nIHRvIHR3byBzaWduaWZpY2FudCBkaWdpdHMgLS0gbm90IHBhcnRpY3VsYXJseSB1c2VmdWwgYnV0IGluc3RydWN0aXZlLgoKV2UgYXJlIGFsc28gdXNpbmcgdGhlIGBwYXN0ZTAoKWAgZnVuY3Rpb24gdG8gYnJlYWsgdXAgdGhlIFNRTCBzdGF0ZW1lbnQgaW50byBtdWx0aXBsZSBsaW5lcyBzbyBpdCBpcyBlYXNpZXIgdG8gcmVhZC4gUiBkb2VzIG5vdCBhbGxvdyBsaW5lIGJyZWFrcyBpbiBzdHJpbmdzIGxpa2Ugb3RoZXIgbGFuZ3VhZ2VzLgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjYXJyaWVyLCBmbGlnaHQsIHJvdW5kKGRlcF9kZWxheSAvIDYwLCAyKSBBUyBocl9kZWxheSIsCiAgIiAgRlJPTSBmbGlnaHRzIiwKICAiIExJTUlUIDU7IikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKIyMjIFNwZWNpZmljIFJvd3Mgd2l0aCBXSEVSRQoKQWRkaW5nIGEgKldIRVJFKiBjbGF1c2UgdG8gYSBxdWVyeSByZXRyaWV2ZXMgc3BlY2lmaWMgcm93cyB0aGF0IG1lZXQgdGhlIGNvbmRpdGlvbnMgb2YgdGhlICpXSEVSRSogY2xhdXNlLiBJdCBpcyBlcXVpdmFsZW50IHRvIGEgcmVsYXRpb25hbCBzZWxlY3Rpb24gb3BlcmF0aW9uLgoKVGhlIGNsYXVzZSBpcyBhIEJvb2xlYW4gZXhwcmVzc2lvbiBhbmQgY2FuIGNvbnRhaW4gQU5ELCBPUiwgYW5kIE5PVCBwbHVzIGxvZ2ljYWwgb3BlcmF0aW9ucyBpbmNsdWRlIGVxdWFsaXR5ID0sIGxlc3MgdGhhbiBcPCwgbGVzcyB0aGFuIG9yIGVxdWFsIFw8PSwgZ3JlYXRlciB0aGFuIFw+LCBncmVhdGVyIHRoYW4gb3IgZXF1YWwgXD49LCBhbmQgbm90IGVxdWFsIFw8XD4uCgojIyMjIFNpbXBsZSBDb25kaXRpb25zCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGNhcnJpZXIsIG5hbWUiLAogICIgIEZST00gYWlybGluZXMiLAogICIgV0hFUkUgY2FycmllciA9ICdVQSciKQpycyA8LSBzcWxkZjo6c3FsZGYoc3FsKQpwcmludChycykKYGBgCgojIyMjIEJvb2xlYW4gQ2xhdXNlcwoKQ29tcGxleCBzZWxlY3Rpb24gZmlsdGVycyBjYW4gYmUgY3JlYXRlZCB1c2luZyBCb29sZWFuIGV4cHJlc3Npb25zIGNvbnRhaW5pbmcgKkFORCosICpPUiosIGFuZCAqTk9UKi4gVXNpbmcgcGFyZW50aGVzaXMgaGVscHMgZW5mb3JjZSBwcmVjZWRlbmNlLgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjYXJyaWVyLCBuYW1lIiwKICAiICBGUk9NIGFpcmxpbmVzIiwKICAiIFdIRVJFIGNhcnJpZXIgPSAnVUEnIiwKICAiICAgIE9SIGNhcnJpZXIgPSAnQUEnIikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKIyMjIyBUZXN0aW5nIGZvciBOVUxMCgpUbyB0ZXN0IGlmIHRoZSB2YWx1ZSBpcyAqTlVMTCogcmVxdWlyZXMgdXNpbmcgKklTIE5VTEwqIG9yICpJUyBOT1QgTlVMTCouIFVzaW5nID0gb3IgXDxcPiByZXN1bHRzIGluIGFuIGVycm9yIGFzICpOVUxMKiBpcyBub3QgYSB2YWx1ZS4gKk5VTEwqIGluZGljYXRlcyB0aGUgYWJzZW5jZSBvZiBhIHZhbHVlIGFuZCBpcyB1c2VkIGZvciAibWlzc2luZyIgdmFsdWVzIC0tIGl0IGlzIHRoZSBlcXVpdmFsZW50IG9mICpOQSogaW4gUiBhbmQgcmVhZGluZyBhIHRhYmxlIGludG8gYSBkYXRhIGZyYW1lIHdvdWxkIGNoYW5nZSBhbGwgKk5VTEwqIHZhbHVlcyB0byAqTkEqLgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBvcmlnaW4sIHllYXIsIGRheSwgbW9udGgiLAogICIgIEZST00gd2VhdGhlciIsCiAgIiBXSEVSRSB3aW5kX2d1c3QgSVMgTlVMTCIsCiAgIiBMSU1JVCAxMCIpCnJzIDwtIHNxbGRmOjpzcWxkZihzcWwpCnByaW50KHJzKQpgYGAKCiMjIyMgRXhwcmVzc2lvbnMKClRvIHVzZSBhbiBleHByZXNzaW9uIGluIHRoZSAqV0hFUkUqIGNsYXVzZSByZXF1aXJlcyB0aGF0IHRoZSBleHByZXNzaW9uIGJlIGFsaWFzZWQuIEluIHRoZSBleGFtcGxlIGJlbG93LCB3ZSB3YW50IHRvIGZpbmQgYWxsIGZsaWdodHMgdGhhdCBoYXZlIG1vcmUgdGhhbiBhIDE1IGhvdXIgZGVsYXkuIFdlIGFkZCBhIGNvbHVtbiB0aGF0IGlzIGFuIGFsaWFzIGZvciBhbiBleHByZXNzaW9uIHRoYXQgY29udmVydHMgdGhlIGRlcGFydHVyZSBkZWxheSBmcm9tIG1pbnV0ZXMgdG8gaG91cnMgYW5kIHRoZW4gdXNlIHRoYXQgYWxpYXMgaW4gdGhlICpXSEVSRSogY2xhdXNlLiBBbmQsIHllcywgd2UgZG8gcmVhbGl6ZSB0aGF0IHdlIGNvdWxkIGhhdmUganVzdCB0ZXN0ZWQgZm9yICovXD4gOTAwKiBidXQgdGhlbiB3ZSBjb3VsZG4ndCBoYXZlIGRlbW9uc3RyYXRlZCBob3cgdG8gdXNlIGV4cHJlc3Npb25zIGluICpXSEVSRSogY2xhdXNlcy4uLgoKVGhlIFNRTCBzdGF0ZW1lbnQgYmVsb3cgYWxzbyBhZGRzIGFuICpPUkRFUiBCWSogY2xhdXNlIHRvIHNvcnQgdGhlIHJlc3VsdHMgYnkgZGVwYXJ0dXJlIGRlbGF5LgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjYXJyaWVyLCBmbGlnaHQsICIsCiAgIiAgICAgICByb3VuZCgoZGVwX2RlbGF5LzYwLjApLDEpIGFzIGRlcEhyIiwKICAiICBGUk9NIGZsaWdodHMiLAogICIgV0hFUkUgZGVwSHIgPiAxNSIsCiAgIiBPUkRFUiBCWSBkZXBIciBERVNDIikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKIyMjIyBUZXh0IFNlYXJjaAoKU1FMIGhhcyBsaW1pdGVkIHN1cHBvcnQgZm9yIHNlYXJjaGluZyB0ZXh0IHN0cmluZ3M7IGl0IGRvZXMgbm90IHN1cHBvcnQgcmVndWxhciBleHByZXNzaW9ucy4gTW9yZSBjb21wbGV4IHN0cmluZyBwcm9jZXNzaW5nIG11c3QgYmUgZG9uZSBpbiBhIHByb2dyYW1taW5nIGxhbmd1YWdlLiBUaGUga2V5d29yZCAqTElLRSogaXMgdXNlZCBmb3IgbWF0Y2hpbmcgc3RyaW5ncyB0aGF0IGZpdCBhIHBhdHRlcm4uCgpUaGUgZm9sbG93aW5nIG1hdGNoaW5nIGNoYXJhY3RlcnMgYXJlIGF2YWlsYWJsZToKCi0gICBcJSBtYXRjaGVzIDAsIG9uZSBvciBtb3JlIGNoYXJhY3RlcnMKLSAgIFxfIG1hdGNoZXMgYW55IG9uZSAoc2luZ2xlKSBjaGFyYWN0ZXIKClNvLCB0byBmaW5kIGFsbCBhaXJsaW5lcyB0aGF0IGNvbnRhaW4gdGhlIHN1YnN0cmluZyAiQW1lcmljYSIgd291bGQgcmVxdWlyZSB0aGUgcGF0dGVybiAqIiVBbWVyaWNhJSIqLiBpdCB3b3VsZCBtYXRjaCAiQW1lcmljYW4gQWlybGluZXMiLCAiVmlyZ2luIEFtZXJpY2EiLCBhbmQgIkFsbCBBbWVyaWNhbiBBaXJ3YXlzIi4KClRvIHBlcmZvcm0gc3RyaW5nIGVxdWFsaXR5IHRlc3RpbmcsIGRvIG5vdCB1c2UgKkxJS0UqLCB1c2UgKj0qIGluc3RlYWQuCgpUaGUgZXhhbXBsZSBpbGx1c3RyYXRlcyB0aGUgdXNlIG9mICpMSUtFKiBmb3Igc3RyaW5nIG1hdGNoaW5nLgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUZ9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjYXJyaWVyLCBuYW1lIiwKICAiICBGUk9NIGFpcmxpbmVzIiwKICAiIFdIRVJFIG5hbWUgTElLRSAnJUFtZXJpY2ElJyIpCnJzIDwtIHNxbGRmOjpzcWxkZihzcWwpCnByaW50KHJzKQpgYGAKCiMjIyMgU2V0IE1lbWJlcnNoaXAKClRoZSBzZXQgbWVtYmVyc2hpcCBvcGVyYXRvciAqSU4qIHRlc3RzIGlmIGEgdmFsdWUgaXMgb25lIG9mIHNldmVyYWwgdmFsdWVzIGluIGEgc2V0LiBJdCBpcyBvZnRlbiBzaW1wbGVyIHRoYW4gdXNpbmcgbXVsdGlwbGUgPSB3aXRoICpPUiouCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGNhcnJpZXIsIG5hbWUiLAogICIgIEZST00gYWlybGluZXMiLAogICIgV0hFUkUgY2FycmllciBJTiAoJ1VBJywnQUEnLCdMSCcsJ1ZYJykiKQpycyA8LSBzcWxkZjo6c3FsZGYoc3FsKQpwcmludChycykKYGBgCgpSYXRoZXIgdGhhbiB0ZXN0aW5nIGlmIGEgdmFsdWUgaXMgaW4gYSBzZXQsIGl0IGlzIG9mdGVuIG5lY2Vzc2FyeSB0byB0ZXN0IGlmIGl0IGlzIG5vdCBpbiB0aGUgc2V0LiBTbywgaW4gdGhlIHF1ZXJ5IGJlbG93LCB3ZSB3b3VsZCBnZXQgYWxsIGFpcmxpbmVzIHRoYXQgYXJlIG5vdCBpbiB0aGUgc3BlY2lmaWVkIHNldCwgKmkuZS4qLCBldmVyeXRoaW5nIGVsc2UuCgpgYGB7ciB3YXJuaW5nPUYsIG1lc3NhZ2U9Rn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGNhcnJpZXIsIG5hbWUiLAogICIgIEZST00gYWlybGluZXMiLAogICIgV0hFUkUgY2FycmllciBOT1QgSU4gKCdVQScsJ0FBJywnTEgnLCdWWCcpIikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKU2V0IG1lbWJlcnNoaXAgaXMgb2Z0ZW4gY29tYmluZWQgd2l0aCBzdWJxdWVyaWVzIHdoZXJlIHRoZSBzdWJxdWVyeSBpcyB0aGUgc2V0IG9mIHZhbHVlcy4KCiMjIyMgRGF0ZXMKCkRhdGVzIGFyZSB1bmlxdWUgZGF0YSB0eXBlcyBpbiBTUUwuIFRoZSB0d28gbW9zdCBjb21tb24gZGF0ZSByZWxhdGVkIGRhdGEgdHlwZXMgYXJlICpEQVRFKiBhbmQgKkRBVEVUSU1FKi4gVGhlIGRhdGEgdHlwZSAqREFURSogYXJlIHZhbHVlcyBpbiB0aGUgZm9ybSAqIllZWVktTU0tREQiKiB3aGlsZSAqREFURVRJTUUqIGFyZSB2YWx1ZXMgaW4gdGhlIGZvcm0gb2YgKiJZWVlZLU1NLUREIEhIOk1JOlNTIiouCgpUbyBjaGVjayBpZiBhIGRhdGUgY29sdW1uIGlzIGEgcGFydGljdWxhciBkYXRlLCB5b3UgY2FuIHVzZSA9IGFuZCB0byBjaGVjayBpZiBpdCBpcyB3aXRoaW4gYSBkYXRlIHJhbmdlIHlvdSBjYW4gdXNlICpCRVRXRUVOKi4KCk91ciBkYXRhIGRvZXMgbm90IGhhdmUgYW55IGRhdGUgZGF0YSB0eXBlcyBidXQgaWYgd2UgaGFkIGEgY29sdW1uICpmbGlnaHRkYXRlKiBpbiAqZmxpZ2h0cyogYW5kIHdhbnQgYWxsIGZsaWdodHMgaW4gYSBkYXRlIHJhbmdlLCB3ZSBjb3VsZCB1c2UgdGhlIHF1ZXJ5IGJlbG93LgoKYGBge3Igd2FybmluZz1GLCBtZXNzYWdlPUYsIGV2YWw9Rn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGNhcnJpZXIsIGZsaWdodCwgZmxpZ2h0ZGF0ZSIsCiAgIiAgRlJPTSBmbGlnaHRzIiwKICAiIFdIRVJFIGZsaWdodGRhdGUgQkVUV0VFTiAnMjAxMy0wMS0wMScgQU5EICcyMDEzLTAxLTMxJyIpCnJzIDwtIHNxbGRmOjpzcWxkZihzcWwpCnByaW50KHJzKQpgYGAKCiMjIyBVbmlxdWUgUm93cwoKSXQgaXMgcG9zc2libGUgdGhhdCBhIHF1ZXJ5IHJlc3VsdHMgaW4gZHVwbGljYXRlIHJvd3MuIFRoZSBrZXl3b3JkICpESVNUSU5DVCogZWxpbWluYXRlcyBkdXBsaWNhdGVzIGluIHRoZSByZXN1bHQgc2V0LgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBESVNUSU5DVCBjYXJyaWVyLCBmbGlnaHQiLAogICIgIEZST00gZmxpZ2h0cyBMSU1JVCA1IikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKVHJ5IHJ1bm5pbmcgdGhlIGFib3ZlIHF1ZXJ5IHdpdGhvdXQgKkRJU1RJTkNUKiBhbmQgb2JzZXJ2ZSB0aGUgZGlmZmVyZW5jZS4gSG93IG1hbnkgcm93cyBkb2VzIHRoZSByZXN1bHQgaGF2ZSB3aGVuIHlvdSByZW1vdmUgKkRJU1RJTkNUKj8KCiMjIyBBZ2dyZWdhdGlvbgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBhdmcoZGVwX2RlbGF5KSBBUyBhdmdfZGVsYXksIG1heChkZXBfZGVsYXkpIEFTIG1heF9kZWxheSIsCiAgIiAgRlJPTSBmbGlnaHRzIikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKIyMjIENvdW50aW5nIFJvd3MKCkFnZ3JlZ2F0aW9uIGlzIGFsc28gb2Z0ZW4gdXNlZCB0byBjb3VudCByb3dzIGluIHRoZSByZXN1bHQgc2V0LiBGb3IgZXhhbXBsZSwgdGhpcyBxdWVyeSBmaW5kcyB0aGUgbnVtYmVyIG9mIGZsaWdodHMuCgpgYGB7cn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGNvdW50KCopIiwKICAiICBGUk9NIGZsaWdodHMiKQpycyA8LSBzcWxkZjo6c3FsZGYoc3FsKQpwcmludChycykKYGBgCgpUaGUgKkRJU1RJTkNUKiBrZXl3b3JkIHJlbW92ZXMgZHVwbGljYXRlcyBhbmQgdGhlcmVmb3JlIG9ubHkgY291bnRzIHVuaXF1ZSByb3dzLgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjb3VudChESVNUSU5DVCBjYXJyaWVyKSIsCiAgIiAgRlJPTSBmbGlnaHRzIikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKQWxzbywgbm90ZSB0aGF0IHRoZSAqQ09VTlQqIGZ1bmN0aW9uIGRvZXMgbm90IHJlcXVpcmUgYSBjb2x1bW4gbmFtZSBsaWtlIG90aGVyIGFnZ3JlZ2F0aW9uIGZ1bmN0aW9ucyBiZWNhdXNlIGl0IGlzIGNvdW50aW5nIHJvd3MgYW5kIHRoYXQgaXMgdGhlIHNhbWUgcmVnYXJkbGVzcyBvZiB3aGljaCBjb2x1bW4gb25lIHNwZWNpZmllcy4gSG93ZXZlciwgaWYgd2UgdXNlIHRoZSBESVNUSU5DVCBrZXl3b3JkLCB0aGVuIGEgY29sdW1uIG5hbWUgaXMgcmVxdWlyZWQgYW5kIGl0IGNvdW50cyB0aGUgZGlzdGluY3Qgb3IgdW5pcXVlIHZhbHVlcyBmb3IgdGhhdCBjb2x1bW4uCgojIyMgR1JPVVAgQlkKCmBgYHtyfQpzcWwgPC0gcGFzdGUwKAogICJTRUxFQ1QgY2Fycmllciwgcm91bmQoYXZnKGRlcF9kZWxheSksMCkgQVMgYXZnX2RlbGF5X21pbnMiLAogICIgIEZST00gZmxpZ2h0cyIsCiAgIiBHUk9VUCBCWSBjYXJyaWVyIiwKICAiIExJTUlUIDY7IikKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbCkKcHJpbnQocnMpCmBgYAoKQ2FuIHlvdSBtb2RpZnkgdGhlIGFib3ZlIFNRTCBzbyB0aGF0IGl0IGZpbmRzIHRoZSBhdmVyYWdlIGFuZCBtYXhpbXVtIGRlbGF5IChyb3VuZGVkIHRvIHRoZSBuZWFyZXN0IG1pbnV0ZSkgZm9yIGVhY2ggY2Fycmllcj8gV2hhdCBhYm91dCBwZXIgYWlycG9ydD8gT3IgcGVyIGFpcnBvcnQgcGVyIGNhcnJpZXI/IFRoZXNlIGFyZSBjb21tb24gYW5hbHl0aWNhbCBxdWVyaWVzIGFuZCBhcmUgb2Z0ZW4gc3VwcG9ydGVkIGJ5IGZhY3QgdGFibGVzIGluIGRhdGEgd2FyZWhvdXNlcy4KClRoZSBncm91cHMgY2FuIGJlIHNlbGVjdGVkIHVzaW5nIHRoZSAqSEFWSU5HKiBjbGF1c2UuIFRoZSAqSEFWSU5HKiBjbGF1c2UgYXBwbGllcyB0byBncm91cHMsIHdoaWxlIHRoZSAqV0hFUkUqIGNsYXVzZSBhcHBsaWVzIHRvIHRoZSByb3dzIHRoYXQgd2lsbCBiZSBncm91cGVkLiBJbiBvdGhlciB3b3JkcywgdGhlIGNvbmRpdGlvbnMgb2YgdGhlICpXSEVSRSogY2xhdXNlIGFuZCBhcHBsaWVkIGZpcnN0LCB0aGVuICpHUk9VUCBCWSogYW5kIHRoZW4gKkhBVklORyo7IGFuZCBmaW5hbGx5LCAqT1JERVIgQlkqLgoKIyMjIElOTkVSIEpPSU4KCkFuIGlubmVyIGpvaW4gKG9yIHNpbXBseSBhIGpvaW4pIGlzIGFuIGVxdWktam9pbiB0aGF0IHNlbGVjdHMgYWxsIHJvd3Mgd2hlcmUgdGhlIGZvcmVpZ24ga2V5IHZhbHVlIG9mIG9uZSBkYXRhIGZyYW1lICh0YWJsZSkgbWF0Y2hlcyBhIHByaW1hcnkga2V5IHZhbHVlIG9mIHRoZSBsaW5rZWQgZGF0YSBmcmFtZSAodGFibGUpLgoKQXMgYW4gZXhhbXBsZSwgbGV0J3MgZmluZCBjYXJyaWVyLCBmbGlnaHQgbnVtYmVyLCBhbmQgbmFtZSBvZiB0aGUgb3JpZ2luIGFpcnBvcnQuIFJlY2FsbCB0aGF0IHRoZSBkYXRhIGZyYW1lICpmbGlnaHRzKiBoYXMgYSBmb3JlaWduIGtleSBjb2x1bW4gKm9yaWdpbiogdGhhdCBpcyB0aGUgYWlycG9ydCBjb2RlIG9mIHRoZSBvcmlnaW4sICppLmUuKiwgYSBsaW5rIHRvIHRoZSBwcmltYXJ5IGtleSBpbiB0aGUgKmFpcnBvcnRzKiB0YWJsZS4KClRvIGJ1aWxkIGEgam9pbiwgd2UgbGlzdCBib3RoIHRhYmxlcyBpbiB0aGUgKipGUk9NKiogY2xhdXNlIGFuZCB0aGVuIGFkZCB0aGUga2V5d29yZCAqSk9JTiogKG9yICpJTk5FUiBKT0lOKiBpZiBvbmUgd2FudHMgdG8gYmUgbW9yZSBzcGVjaWZpYyBhcyB0byB3aGljaCBqb2luIGlzIGJlaW5nIGFwcGxpZWQpIGJldHdlZW4gdGhlbSBhbmQgaW4gcGFyZW50aGVzaXMgdGhlIGpvaW4gY29uZGl0aW9uIGFmdGVyIHRoZSBrZXl3b3JkICpPTiouCgpgYGB7cn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGNhcnJpZXIsIGZsaWdodCwgbmFtZSIsCiAgIiAgRlJPTSBmbGlnaHRzIEpPSU4gYWlycG9ydHMgT04gKG9yaWdpbiA9IGZhYSkiLAogICIgTElNSVQgNTsiKQpycyA8LSBzcWxkZjo6c3FsZGYoc3FsKQpwcmludChycykKYGBgCgpUaGUgYWJvdmUgU1FMIHN0YXRlbWVudCBpcyBlcXVpdmFsZW50IHRvIHRoZSByZWxhdGlvbmFsIGFsZ2VicmEgZXhwcmVzc2lvbiBiZWxvdzoKCiRccGlfe2NhcnJpZXIsZmxpZ2h0LG5hbWV9KChmbGlnaHRzKVxib3d0aWVfe29yaWdpbj1mYWF9KGFpcnBvcnRzKSkkCgpBIHBvaW50IG9mIGNsYXJpZmljYXRpb246IHRoZSBhYm92ZSBTUUwgd29ya3MgYXMgdGhlIHNhbWUgYXR0cmlidXRlcyBuYW1lcyBkbyBub3QgYXBwZWFyIGluIHRoZSB0d28gdGFibGVzLiBJZiB3ZSBoYWQgYW4gYXR0cmlidXRlICphdHQqIGluIGJvdGggdGFibGVzLCB0aGVuIHdlIHdvdWxkIGhhdmUgdG8gZXhwbGljaXRseSBzY29wZSB3aGljaCB0YWJsZSB3ZSBtZWFuIGFuZCB3ZSB3b3VsZCBjb21tb25seSBhbGlhcyB0aGUgdGFibGUgbmFtZXMuIFRoaXMgaXMgYWxzbyBkb25lIGV2ZW4gaWYgdGhlcmUncyBubyBjb25mdXNpb24gaW4gb3JkZXIgdG8gbWFrZSB0aGUgcXVlcnkgZWFzaWVyIHRvIHVuZGVyc3RhbmQuIFRoZSB1cGRhdGVkIFNRTCBiZWxvdyBpbGx1c3RyYXRlcyB0aGlzOgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBmLmNhcnJpZXIsIGYuZmxpZ2h0LCBhLm5hbWUiLAogICIgIEZST00gZmxpZ2h0cyBBUyBmIEpPSU4gYWlycG9ydHMgQVMgYSBPTiAoZi5vcmlnaW4gPSBhLmZhYSkiLAogICIgTElNSVQgNTsiKQpycyA8LSBzcWxkZjo6c3FsZGYoc3FsKQpwcmludChycykKYGBgCgpPZiBjb3Vyc2UsIHdlIGNhbiBjb21iaW5lIHNldmVyYWwgdGFibGVzLiBMZXQncyBzYXkgd2UgbmVlZCB0byBrbm93IHRoZSBjYXJyaWVyIG5hbWUgcmF0aGVyIHRoYW4gdGhlIGNhcnJpZXIncyBhYmJyZXZpYXRpb24gY29kZSBmb3IgdGhlIGFib3ZlIHF1ZXJ5LCAqaS5lLiosICJBbWVyaWNhbiBBaXJsaW5lcyIgcmF0aGVyIHRoYW4gIkFBIi4gVG8gZG8gdGhhdCwgd2UgbmVlZCB0byBjb21iaW5lIHRocmVlIHRhYmxlczogKmZsaWdodHMqLCAqYWlybGluZXMqLCBhbmQgKmFpcnBvcnRzLioKCmBgYHtyfQpzcWwgPC0gcGFzdGUwKAogICJTRUxFQ1Qgci5uYW1lLCBmLmZsaWdodCwgYS5uYW1lIiwKICAiICBGUk9NIGZsaWdodHMgQVMgZiAiLAogICIgICAgICAgSk9JTiBhaXJwb3J0cyBBUyBhIE9OIChmLm9yaWdpbiA9IGEuZmFhKSIsCiAgIiAgICAgICBKT0lOIGFpcmxpbmVzIEFTIHIgT04gKGYuY2FycmllciA9IHIuY2FycmllcikiLAogICIgTElNSVQgNTsiKQpycyA8LSBzcWxkZjo6c3FsZGYoc3FsKQpwcmludChycykKYGBgCgpTUUwgc3VwcG9ydHMgb3RoZXIgam9pbnMgYXMgd2VsbCwgaW5jbHVkaW5nIG91dGVyIGpvaW4uLiBTZWUgW0xlc3NvbiA3MC4xMTIgUmV0cmlldmluZyBEYXRhIGZyb20gTXVsdGlwbGUgVGFibGVzIFVzaW5nIFZhcmlvdXMgSm9pbnNdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvNzAuc3FsL2wtNzAtMTEyLWpvaW5zL2wtNzAtMTEyLmh0bWwpIGlmIHlvdSB3YW50IHRvIGxlYXJuIG1vcmUuCgojIyMgU3VicXVlcmllcwoKU2luY2UgdGhlIHJlc3VsdCBvZiBhICpTRUxFQ1QqIHN0YXRlbWVudCBpcyBhIHRhYmxlLCB3ZSBjYW4gdXNlIGEgKlNFTEVDVCogc3RhdGVtZW50IHdoZXJldmVyIGEgdGFibGUgaXMgcmVxdWlyZWQsIFxfZS5nLiwgaW4gdGhlICpGUk9NKiBjbGF1c2UuIFdlIGNhbiB1c2UgYSAqU0VMRUNUKiBzdGF0ZW1lbnQgdGhhdCByZXR1cm4gb25lIHJvdyBhbmQgb25lIGNvbHVtbiAoYSBzaW5nbGUgdmFsdWUpIHdoZXJldmVyIGEgc2luZ2xlIHZhbHVlIGlzIGV4cGVjdGVkLCBzdWNoIGFzIGluIGEgY29uZGl0aW9uIGluIGEgKldIRVJFKiBjbGF1c2UuCgpUaGUgcXVlcnkgYmVsb3cgZmluZHMgYWxsIGZsaWdodHMgdGhhdCBoYXZlIGEgYmVsb3cgYXZlcmFnZSBkZXBhcnR1cmUgZGVsYXkuCgpgYGB7cn0Kc3FsIDwtIHBhc3RlMCgKICAiU0VMRUNUIGYuZmxpZ2h0LCBmLmRlcF9kZWxheSIsCiAgIiAgRlJPTSBmbGlnaHRzIEFTIGYiLAogICIgV0hFUkUgZi5hcnJfZGVsYXkgPCAoU0VMRUNUIGF2ZyhhcnJfZGVsYXkpIiwKICAiICAgICAgICAgICAgICAgICAgICAgICAgRlJPTSBmbGlnaHRzKSIsCiAgIiBMSU1JVCA1OyIpCnJzIDwtIHNxbGRmOjpzcWxkZihzcWwpCnByaW50KHJzKQpgYGAKCkluIHRoaXMgcXVlcnksIHdlIHVzZSBhIHN1YnF1ZXJ5IHRvIGdlbmVyYXRlIGEgbGlzdCBvZiB2YWx1ZXMgd2hpY2ggd2UgdGhlbiB1c2UgaW4gYSBzZXQgb3BlcmF0aW9uLCBzdWNoIGFzICpJTiouCgojIyBTUUwgb24gVGFidWxhciBEYXRhIGluIEZpbGVzCgojIyMgVXNpbmcgU1FMIG9uIENTViBGaWxlcwoKVG8gcXVlcnkgZGF0YSBpbiBhIENTViBmaWxlIHVzaW5nIFNRTCBjYW4gYmUgZG9uZSBpbiB0d28gd2F5czoKCjEuICBsb2FkIHRoZSBDU1YgaW50byBhIGRhdGEgZnJhbWUgYW5kIHVzZSBgc3FsZGY6OnNxbGRmKClgCjIuICBydW4gdGhlIFNRTCBxdWVyeSBkaXJlY3RseSBhZ2FpbnN0IHRoZSBDU1YgdXNpbmcgYHNxbGRmOjpyZWFkLmNzdi5zcWwoKWAKCk5vdGUgdGhhdCBpbiAoMiksIHVuZGVybmVhdGggUiB3aWxsIHN0aWxsIGxvYWQgdGhlIGRhdGEgZnJvbSB0aGUgQ1NWIGludG8gbWVtb3J5IGFuZCB0aGVuIGNvcHkgdGhlIGRhdGEgZnJhbWUgaW50byBhbiBpbi1tZW1vcnkgU1FMaXRlIGRhdGFiYXNlIG9uIHdoaWNoIHRoZSBTUUwgcXVlcnkgaXMgYWN0dWFsbHkgZXhlY3V0ZWQuIEJ1dCB0aGF0IGFsbCBoYXBwZW5zIHVuZGVybmVhdGggdGhlIGhvb2QgYW5kIHdoaWxlIG5vIG1vcmUgZWZmaWNpZW50IGl0IGlzIHNpbXBsZXIgdG8gdXNlLgoKTGV0J3MgcXVlcnkgdGhlIGRhdGEgaW4gdGhlIENTViBbQ2VyZWFsRGF0YUNTVi5jc3ZdKENlcmVhbERhdGFDU1YuY3N2KSB1c2luZyBib3RoIG9mIHRoZXNlIGFwcHJvYWNoZXMuIFlvdSBjYW4gZGVjaWRlIHdoaWNoIHlvdSBwcmVmZXIuCgoqKkFwcHJvYWNoIEk6IExvYWQgQ1NWIGludG8gZGF0YSBmcmFtZSBhbmQgdXNlIGBzcWxkZigpYCoqCgpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFLCBldmFsPUZ9CmxpYnJhcnkoc3FsZGYpCgojIGxvYWQgdGhlIENTViBpbnRvIGEgZGF0YSBmcmFtZQpjZXJlYWxzIDwtIHJlYWQuY3N2KCJDZXJlYWxEYXRhQ1NWLmNzdiIpCgpzcWxTdG10IDwtIHBhc3RlMCgKICAiU0VMRUNUIENlcmVhbE5hbWUsQ2Fsb3JpZXMsU29kaXVtLEZpYmVyLENhcmJzICIsCiAgIiAgRlJPTSBjZXJlYWxzICIsCiAgIiBXSEVSRSBTb2RpdW0gPiAyNTAiKQoKcnMgPC0gc3FsZGY6OnNxbGRmKHNxbFN0bXQpCgpwcmludChycykKYGBgCgoqKkFwcHJvYWNoIElJOiBSdW4gU1FMIGRpcmVjdGx5IHVzaW5nIGByZWFkLmNzdi5zcWwoKWAqKgoKVGhlIGZ1bmN0aW9uIHJlYWRzIGEgQ1NWIGZpbHRlcmVkIGJ5IGEgU1FMIHN0YXRlbWVudCwgcmVzdWx0aW5nIGluIGEgc3Vic2V0IG9mIHRoZSBkYXRhIGNvbnRhaW5lZCBpbiB0aGUgQ1NWIGJlaW5nIGltcG9ydGVkIGludG8gYSBkYXRhIGZyYW1lLiBTaW5jZSB0aGUgdGFibGUgaXMgYSBmaWxlIGl0IGhhcyBubyBuYW1lLCBzbyBpbiB0aGUgU1FMIHN0YXRlbWVudCB3ZSByZWZlciB0byB0aGUgInRhYmxlIiBhcyAiZmlsZSIuCgpOb3RlIGhvdyB3ZSBidWlsZCB0aGUgU1FMICJzdHJpbmciIHVzaW5nIGBwYXN0ZTAoKWAgc28gdGhhdCB3ZSBjYW4gc3BsaXQgaXQgb3ZlciBtdWx0aXBsZSBsaW5lcywgZW1iZWQgdGhlIHZhbHVlIG9mIFIgdmFyaWFibGVzLCBhbmQgbWFrZSBpdCBtb3JlIHJlYWRhYmxlLgoKYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgZXZhbD1GfQpsaWJyYXJ5KHNxbGRmKQoKc3FsU3RtdCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBDZXJlYWxOYW1lLENhbG9yaWVzLFNvZGl1bSxGaWJlcixDYXJicyAiLAogICIgIEZST00gZmlsZSAiLAogICIgV0hFUkUgU29kaXVtID4gMjUwIikKCnJzIDwtIHNxbGRmOjpyZWFkLmNzdi5zcWwoZmlsZSA9ICJDZXJlYWxEYXRhQ1NWLmNzdiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHNxbCA9IHNxbFN0bXQpCgpwcmludChycykKYGBgCgojIyMgVXNpbmcgU1FMIG9uIFhNTCBGaWxlcwoKVG8gdXNlIFNRTCBhZ2FpbnN0IGFuIFhNTCBkb2N1bWVudCwgdGhlIFhNTCBlbGVtZW50cyBtdXN0IGJlIHJlYWQgaW50byBtZW1vcnksIHNwbGl0IGludG8gdGFibGVzLCBhbmQgb25seSB0aGVuIGNhbiBiZSBxdWVyaWVkLiBbTGVzc29uIDYuMzI4IFBhcnNpbmcgYW4gWE1MIERvY3VtZW50IGFuZCBTYXZpbmcgdG8gU1FMaXRlIERhdGFiYXNlIGluIFJdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMDYuci9sLTYtMzI4LXhtbC10by1yZWxkYi1zcWxpdGUvbC02LTMyOC5odG1sKSBleHBsYWlucyB0aGlzIHByb2Nlc3MgaW4gZGV0YWlsIHNob3VsZCB5b3UgYmUgaW50ZXJlc3RlZC4KCiMjIFJlbGF0aW9uYWwgRGF0YWJhc2VzCgpBIHJlbGF0aW9uYWwgZGF0YWJhc2UgaXMgYSBwZXJzaXN0ZW50IGNvbGxlY3Rpb24gb2YgdGFibGVzIHdoZXJlIGVhY2ggdGFibGUgaG9sZHMgaW5mb3JtYXRpb24gYWJvdXQgb25lIGVudGl0eSwgKmUuZy4qLCBhIHRhYmxlIG9mIGN1c3RvbWVycyBvciBhIHRhYmxlIG9mIHNhbGVzLiBFYWNoIHRhYmxlIGlzIGxpa2UgYSBDU1YgZmlsZSBvciBhIGRhdGEgZnJhbWU7IGl0IGhhcyByb3dzIGFuZCBjb2x1bW5zLCBidXQgdW5saWtlIGRhdGEgZnJhbWVzLCB0aGUgcm93cyBhcmUgbm90IG51bWJlcmVkIGFuZCBjYW5ub3QgYmUgYWNjZXNzZWQgYnkgYSByb3cgbnVtYmVyLgoKRWFjaCB0YWJsZSBob2xkcyBpbmZvcm1hdGlvbiBhYm91dCBvbmUgZW50aXR5LCAqZS5nLiosIGEgdGFibGUgb2YgY3VzdG9tZXJzIG9yIGEgdGFibGUgb2Ygc2FsZXMuIEVhY2ggdGFibGUgaXMgbGlrZSBhIENTViBmaWxlIG9yIGEgZGF0YSBmcmFtZTsgaXQgaGFzIHJvd3MgYW5kIGNvbHVtbnMsIGJ1dCB1bmxpa2UgZGF0YSBmcmFtZXMsIHRoZSByb3dzIGFyZSBub3QgbnVtYmVyZWQgYW5kIGNhbm5vdCBiZSBhY2Nlc3NlZCBieSBhIHJvdyBudW1iZXIuCgpBIHJlbGF0aW9uYWwgZGF0YWJhc2UgaXMgZ2VuZXJhbGx5IG9ubHkgbmVjZXNzYXJ5IHdoZW4gdGhlIGRhdGEgZG9lcyBub3QgZml0IGludG8gbWVtb3J5LCBzaGFyaW5nIG9mIENTViBvciBYTUwgZmlsZXMgaXMgaW5jb252ZW5pZW50LCBvciBtdWx0aXBsZSBhcHBsaWNhdGlvbnMgbmVlZCBhY2Nlc3MgdG8gdGhlIHNhbWUgZGF0YSBhdCB0aGUgc2FtZSB0aW1lLiBPdGhlciB0aW1lcywgdGFidWxhciBmaWxlcyBhcmUgb2Z0ZW4gc3VmZmljaWVudC4KCiMjIyBTUUxpdGUKCk9uZSBvZiB0aGUgbW9zdCBwb3B1bGFyIHJlbGF0aW9uYWwgZGF0YWJhc2VzIGlzIFNRTGl0ZS4gSXQgaXMgYSBzZXJ2ZXItbGVzcywgZmlsZS1iYXNlZCBkYXRhYmFzZSB0aGF0IGlzIGZhc3QgYW5kIHNtYWxsIHNvIGl0IHJ1bnMgb24gbW9zdCBjb21tb24gZGV2aWNlcyBhbmQgb3BlcmF0aW5nIHN5c3RlbXMsIGluY2x1ZGluZyBXaW5kb3dzLCBNYWNPUywgTGludXgsIEFuZHJvaWQsIGFuZCBpT1MuCgpUaGlzIGlzIG5vdCBhIGNvbXBsZXRlIHR1dG9yaWFsIG9uIFNRTGl0ZSBkYXRhYmFzZXMsIHJhdGhlciB0aGlzIHNlY3Rpb24gc2Vla3MgdG8gc2hvdyBob3cgdG8gY3JlYXRlIGFuZCBuZXcgU1FMaXRlIGRhdGFiYXNlIGFuZCBob3cgdG8gY29ubmVjdCB0byBhIGV4aXN0aW5nIFNRTGl0ZSBkYXRhYmFzZSBmcm9tIFIuIElmIHlvdSB3YW50IHRvIGtub3cgbW9yZSBhYm91dCBTUUxpdGUsIHJlYWQgW0xlc3NvbiA3MC44MDEgVGhlIFNRTGl0ZSBEYXRhYmFzZV0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy83MC5zcWwvbC03MC04MDEtaW50cm8tc3FsaXRlL2wtNzAtODAxLmh0bWwpLgoKVG8gY29ubmVjdCB0byBvciBhY2Nlc3MgYSBTUUxpdGUgZGF0YWJhc2UgZnJvbSBSIHJlcXVpcmVzIHR3byBzdGVwczoKCjEuICBsb2FkIHRoZSAqKlJTUUxpdGUqKiBwYWNrYWdlW14yXQoyLiAgY2FsbCB0aGUgZnVuY3Rpb24gPGNvZGU+ZGJDb25uZWN0KCk8L2NvZGU+CgpbXjJdOiBOYXR1cmFsbHksIHlvdSBtdXN0IGZpcnN0IGluc3RhbGwgdGhlICoqUlNRTGl0ZSoqIHBhY2thZ2UgaWYgaXMgbm90IHlldCBpbnN0YWxsZWQgb24geW91ciBpbnN0YWxsYXRpb24gb2YgUi4KCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHVzaW5nIFNRTGl0ZSB3aXRoIFIsIGNvbnN1bHQgW0xlc3NvbiA2LjMwMCBTUUxpdGUgd2l0aCBSOiBBIFByaW1lcl0oaHR0cDovL2FydGlmaWNpdW0udXMvbGVzc29ucy8wNi5yL2wtNi0zMDAtY3JlYXRlLXNxbGl0ZWRiLWluLXIvbC02LTMwMC5odG1sKS4KCiMjIyMgQ29ubmVjdCB0byBTUUxpdGUgRGF0YWJhc2UKClRoZSBjb2RlIGJlbG93IGRlbW9uc3RyYXRlcyBjb25uZWN0aW5nIHRvIGEgU1FMaXRlIGRhdGFiYXNlIGxvY2F0ZWQgaW4gdGhlIGZpbGUgKiJmbGlnaHRzREIuZGIiKiB3aGljaCBpcyBsb2NhdGVkIHdpdGhpbiB0aGUgY3VycmVudCBwcm9qZWN0IGZvbGRlciBvciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5LiBJZiBpdCBpcyBub3QsIHRoZW4gdGhlIGZ1bGwgcGF0aCB0byB0aGUgZmlsZSBtdXN0IGJlIHNwZWNpZmllZC4gSWYgdGhlIGRhdGFiYXNlIGZpbGUgZG9lcyBub3QgYWxyZWFkeSBleGlzdCwgYSBuZXcgZGF0YWJhc2UgaXMgY3JlYXRlZC4KCmBgYHtyIGNyZWF0ZURCLCBlY2hvPVQsIGV2YWw9VH0KbGlicmFyeShSU1FMaXRlKQpkYmNvbiA8LSBkYkNvbm5lY3QoUlNRTGl0ZTo6U1FMaXRlKCksICJmbGlnaHRzREIuZGIiKQpgYGAKClRoZSBkYXRhYmFzZSBpcyBhIHJlcGxpY2Egb2YgdGhlIHRhYmxlcyBpbiB0aGUgKipueWNmbGlnaHRzMTMqKiBwYWNrYWdlIHRoYXQgd2UgaGF2ZSBiZWVuIHVzaW5nLgoKIyMjIFJ1biBTUUwgUXVlcmllcyBpbiBTUUxpdGUKCk9uY2UgdGhlIGNvbm5lY3Rpb24gdG8gdGhlIGRhdGFiYXNlIGhhcyBiZWVuIG1hZGUsIHlvdSBjYW4gd29yayB3aXRoIHRoZSBkYXRhYmFzZSBmcm9tIFIgdXNpbmcgZnVuY3Rpb25zIGZyb20gdGhlICoqUlNRTGl0ZSoqIHBhY2thZ2UsIHN1Y2ggYXMgPGNvZGU+ZGJFeGVjdXRlKCk8L2NvZGU+LCA8Y29kZT5kYkdldFF1ZXJ5KCk8L2NvZGU+LCBhbmQgPGNvZGU+ZGJTZW5kU3RhdGVtZW50KCk8L2NvZGU+LiBBbHRlcm5hdGl2ZWx5LCBpbiBSIE5vdGVib29rcywgU1FMIGNvZGUgYmxvY2tzIGNhbiBiZSBpbnNlcnRlZCB0aGF0IGFyZSB0aGVuICJrbml0dGVkIiBpbnRvIHRoZSBhZm9yZW1lbnRpb25lZCBSIGZ1bmN0aW9ucy4gTm90ZSB0aGF0IHRoaXMgb25seSB3b3JrcyBpbiBSIE5vdGVib29rcyBhbmQgbm90IFIgU2NyaXB0cyAocHJvZ3JhbXMpLgoKIyMjIyMgPGNvZGU+ZGJHZXRRdWVyeSgpPC9jb2RlPgoKVGhlIGNvZGUgYmVsb3cgZGVtb25zdHJhdGVzIGhvdyB0byBydW4gYSBTUUwgcXVlcnkgKCpTRUxFQ1QqKSBvbiB0aGUgdGFibGVzIGluIHRoZSBjb25uZWN0ZWQgKiJmbGlnaHRzREIuZGIiKiBTUUxpdGUgZGF0YWJhc2UgdGhhdCBjb250YWlucyB0aGUgc2FtZSB0YWJsZXMgYXMgd2UgaGF2ZSBiZWVuIHVzaW5nIGZyb20gdGhlIHBhY2thZ2UgKipueWNmbGlnaHRzMTMqKi4KClRoZSBmdW5jdGlvbiBgZGJnZXRRdWVyeSgpYCBjYW4gb25seSBiZSB1c2VkIGZvciAqU0VMRUNUKiBzdGF0ZW1lbnRzIGFuZCBpdCBhbHdheXMgcmV0dXJucyBhIGRhdGEgZnJhbWUgY29udGFpbmluZyB0aGUgcmVzdWx0IHNldC4gVGhlIGZ1bmN0aW9uIDxjb2RlPmRiRXhlY3V0ZSgpPC9jb2RlPiwgYW5kIDxjb2RlPmRiU2VuZFN0YXRlbWVudCgpPC9jb2RlPiBjYW4gYmUgdXNlZCBmb3IgKlNFTEVDVCogcXVlcmllcyBhbmQgYWxzbyBmb3Igb3RoZXIgU1FMIHN0YXRlbWVudHMsIHN1Y2ggYXMgKklOU0VSVCogb3IgKkRST1AgVEFCTEUqLiBUaGlzIGlzIGJleW9uZCB0aGUgc2NvcGUgb2YgdGhpcyB0dXRvcmlhbC4gQ29uc3VsdCBbTGVzc29uIDYuMzAxIOKUhiBXb3JraW5nIHdpdGggRGF0YWJhc2VzIGluIFJdKGh0dHA6Ly9hcnRpZmljaXVtLnVzL2xlc3NvbnMvMDYuci9sLTYtMzAxLXNxbGl0ZS1mcm9tLXIvbC02LTMwMS5odG1sKSBpZiB5b3Ugd2FudCB0byBsZWFybiBtb3JlLgoKIyMjIyMgU1FMIENvZGUgQ2h1bmtzCgpJZiB3ZSBhcmUgYnVpbGRpbmcgUiBOb3RlYm9vaywgdGhlbiB3ZSBjYW4gYWxzbyB1c2UgU1FMIGNvZGUgY2h1bmtzLiBXaGVuIHRoZSBSIE5vdGVib29rIGlzIGtuaXR0ZWQsIHRoZSBTUUwgY2h1bmsgaXMgY29udmVydGVkIHRvIGEgY2FsbCB0byBhbiBSIGZ1bmN0aW9uLCBzbyBpdCBpcyBubyBtb3JlIG9yIGxlc3MgZWZmaWNpZW50OyBpdCBpcyBtZXJlbHkgYSBzaW1wbGVyIGFuZCBtb3JlIGNvbnZlbmllbnQgd2F5IHRvIHJ1biBTUUwgc3RhdGVtZW50cy4KCkEgU1FMIGNvZGUgY2h1bmsgaXMgaW4gYSBjb2RlIGZlbmNlIHRoYXQgc3RhcnRzIHdpdGgge3NxbCBjb25uZWN0aW9uPSpkYmNvbip9IHdoZXJlICpkYmNvbiogaXMgYSBkYXRhYmFzZSBjb25uZWN0aW9uIG9idGFpbmVkIGZyb20gYSBjYWxsIHRvIGBkYkNvbm5lY3QoKWAuCgpUaGUgY29kZSBjaHVuayBhcyBpdCB3b3VsZCBiZSB0eXBlZCBpbnRvIGFuIFIgTWFya2Rvd24gZG9jdW1lbnQgaXMgc2hvd24gaW4gdGhlIGltYWdlIGJlbG93OgoKIVtTUUwgQ29kZSBGZW5jZV0oaW1hZ2VzL3NxbC1jb2RlLWZlbmNlLmpwZykKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbiwgZWNobz1GfQpzZWxlY3QgbC5uYW1lIGFzICdhaXJsaW5lJywgCiAgICAgICBmLmNhcnJpZXIgYXMgJ2NvZGUnLCAKICAgICAgIGYuZmxpZ2h0LCAKICAgICAgIGEubmFtZSBhcyAnb3JpZ2luJwogIGZyb20gZmxpZ2h0cyBmIGpvaW4gYWlycG9ydHMgYSBvbiAoZi5vcmlnaW4gPSBhLmZhYSkKICAgICAgICAgICAgICAgICBqb2luIGFpcmxpbmVzIGwgb24gKGYuY2FycmllciA9IGwuY2FycmllcikKIGxpbWl0IDU7CmBgYAoKUnVubmluZyBhIFNRTCBxdWVyeSBpbiBhIGNvZGUgY2h1bmsgZGlzcGxheXMgdGhlIHJlc3VsdCBzZXQuIEhvd2V2ZXIsIGlmIHdlIG5lZWQgdG8gZnVydGhlciBwcm9jZXNzIHRoZSByZXN1bHQgc2V0IGluIFIgb3Igd2FudCB0byBydW4gYW5hbHl0aWNzLCB0aGVuIHdlIG5lZWQgdG8gY2FwdHVyZSB0aGUgcmVzdWx0IHNldCBpbiBhIGRhdGEgZnJhbWUuIFdoZW4gd2UgdXNlZCBgZGJHZXRRdWVyeSgpYCB0aGlzIHdhcyB0aGUgcmV0dXJuIHZhbHVlLCBidXQgaW4gYSBjb2RlIGNodW5rIHdlIG5lZWQgdG8gdXNlIGEgcGFyYW1ldGVyOiAqb3V0cHV0LnZhcioKClNvLCB0aGUgY29kZSBjaHVuayB3ZSBhZGRlZCB0byB0aGUgUiBOb3RlYm9vayBsb29rZWQgbGlrZSB0aGlzOgoKIVtTUUwgQ29kZSBGZW5jZSB3aXRoIENhcHR1cmVkIFJlc2V0IFNldF0oaW1hZ2VzL3NxbC1jb2RlLW91dHB1dHZhci5qcGcpCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb24sIG91dHB1dC52YXI9ImRmLmZsaWdodHMiLCBlY2hvPUZ9CnNlbGVjdCBsLm5hbWUgYXMgJ2FpcmxpbmUnLCAKICAgICAgIGYuY2FycmllciBhcyAnY29kZScsIAogICAgICAgZi5mbGlnaHQsIAogICAgICAgYS5uYW1lIGFzICdvcmlnaW4nCiAgZnJvbSBmbGlnaHRzIGYgam9pbiBhaXJwb3J0cyBhIG9uIChmLm9yaWdpbiA9IGEuZmFhKQogICAgICAgICAgICAgICAgIGpvaW4gYWlybGluZXMgbCBvbiAoZi5jYXJyaWVyID0gbC5jYXJyaWVyKQogbGltaXQgNTsKYGBgCgpPbmNlIGNhcHR1cmVkIGluIGEge3NxbH0gY29kZSBjaHVuaywgd2UgY2FuIHVzZSBpdCBpbiBhbiBSIGNvZGUgY2h1bmsuCgpgYGB7cn0KaGVhZChkZi5mbGlnaHRzKQpgYGAKCk9mIGNvdXJzZSwgdGhlIFIgY29kZSBhYm92ZSBpcyBub3QgdmVyeSB1c2VmdWwgYnV0IGl0IGlsbHVzdHJhdGVzIHRoYXQgdGhlIHJlc3VsdCBzZXQgaXMgbm93IGluIGEgZGF0YSBmcmFtZSBhbmQgY2FuIGJlIHByb2Nlc3NlZCBhY2NvcmRpbmdseS4KCiMjIENyZWF0aW5nIGEgUmVsYXRpb25hbCBEYXRhYmFzZQoKQSBuZXcgZGF0YWJhc2UgY2FuIGJlIGNyZWF0ZWQgaW4gdHdvIHdheXM6CgoxLiAgY3JlYXRpbmcgdGFibGVzIGFuZCB0aGVuIGluc2VydGluZyBkYXRhCjIuICB3cml0aW5nIGV4aXN0aW5nIGRhdGEgZnJvbSBhIENTViBvciBkYXRhIGZyYW1lcyB0byB0YWJsZXMKCiMjIyBDUkVBVEUgVEFCTEUKCkJ1aWxkaW5nIGEgZGF0YWJhc2UgbWVhbnMgY3JlYXRpbmcgdGFibGVzIHdpdGggYXR0cmlidXRlcy4gVGhpcyBpcyBkb25lIHVzaW5nIHRoZSBgQ1JFQVRFIFRBQkxFYCBTUUwgc3RhdGVtZW50LiBTUUwgaXMgbm90IGNhc2Ugc2Vuc2l0aXZlLCBzbyBpdCBkb2VzIG5vdCBtYXR0ZXIgd2hldGhlciB3ZSBzYXkgYENSRUFURSBUQUJMRWAgb3IgYGNyZWF0ZSB0YWJsZWAuIENyZWF0aW5nIGEgdGFibGUgdGhhdCBhbHJlYWR5IGV4aXN0cyByZXN1bHRzIGluIGEgZXJyb3IuCgpPbmNlIGEgdGFibGUgaXMgY3JlYXRlZCwgaXQgY2FuIGJlIHJlbW92ZWQgb3IgbW9kaWZpZWQgKHRvIHNvbWUgZXh0ZW50IGFuZCB3aXRoIHNvbWUgcmVzdHJpY3Rpb25zKS4gUmVtb3ZpbmcgYSB0YWJsZSBpcyBkb25lIHdpdGggdGhlIGBEUk9QIFRBQkxFYCBzdGF0ZW1lbnQsIHdoaWxlIG1vZGlmeWluZyBhIHRhYmxlIGlzIGRvbmUgd2l0aCBgQUxURVIgVEFCTEVgLgoKQmVmb3JlIHdlIGNhbiBhZGQgdGFibGVzIGFuZCBkYXRhIHRvIGEgZGF0YWJhc2UsIHdlIG5lZWQgdG8gY29ubmVjdCB0byB0aGUgZGF0YWJhc2UuIHJlY2FsbCB0aGF0IGNvbm5lY3RpbmcgdG8gYSBTUUxpdGUgZGF0YWJhc2UgdGhhdCBkb2VzIG5vdCBleGlzdCB3aWxsIGNyZWF0ZSBpdCAob2YgY291cnNlLCB3aXRob3V0IGFueSB0YWJsZXMgaW4gaXQpLgoKYGBge3J9CmxpYnJhcnkoUlNRTGl0ZSkKCmxlc3NvbkRCIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgImxlc3NvbkRCLmRiIikKYGBgCgpJbiB0aGUgZm9sbG93aW5nIGNvZGUgYmxvY2tzLCB3ZSB3aWxsIGNyZWF0ZSBhIG5ldyB0YWJsZSAqbGVzc29ucyouIEJ1dCwgaW4gY2FzZSBpdCBhbHJlYWR5IGV4aXN0cywgd2Ugd2lsbCBmaXJzdCByZW1vdmUgaXQuIE5vdGUgdGhhdCB0aGUge3NxbH0gY29kZSBmZW5jZSBoYXMgdG8gc2V0ICpjb25uZWN0aW9uPWxlc3NvbkRCKi4KCmBgYHtzcWwgY29ubmVjdGlvbj1sZXNzb25EQiwgZWNobz1GfQpkcm9wIHRhYmxlIGlmIGV4aXN0cyBsZXNzb25zOwpgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1sZXNzb25EQn0KY3JlYXRlIHRhYmxlIGxlc3NvbnMgKAogIGxuYW1lIHRleHQgbm90IG51bGwsCiAgbGxlbmd0aCBpbnRlZ2VyIG5vdCBudWxsCik7CmBgYAoKIyMjIEluc2VydGluZyBEYXRhCgpPbmNlIHdlIGhhdmUgYSBuZXcgdGFibGUsIHdlIGNhbiBhZGQgZGF0YSB1c2luZyB0aGUgU1FMICpJTlNFUlQqIHN0YXRlbWVudC4gTGV0J3MgYWRkIGEgZmV3IHJvd3Mgb2YgZGF0YSB0byB0aGUgdGFibGUgc28gd2UgY2FuIHNlZSBob3cgU1FMaXRlIGFzc2lnbnMgYSByb3cgaWQgYXMgYSBwcmltYXJ5IGtleS4KCmBgYHtzcWwgY29ubmVjdGlvbj1sZXNzb25EQn0KaW5zZXJ0IGludG8gbGVzc29ucyB2YWx1ZXMKICAoJ0ludHJvIEMrKycsIDkwKSwKICAoJ0ludHJvIEphdmEnLCA5MCksCiAgKCdTUUwgSm9pbnMnLCA2MCkKYGBgCgpSYXRoZXIgdGhhbiB1c2luZyBhIHtzcWx9IGNvZGUgY2h1bmsgd2UgY2FuIGFsc28gdXNlIFIgZnVuY3Rpb25zIHN1Y2ggYXMgYGRiU2VuZFN0YXRlbWVudCgpYC4gVGhpcyBpcyBhbiBvcHRpb24gZm9yIFIgTm90ZWJvb2tzIGJ1dCByZXF1aXJlZCBpZiB3ZSB3cml0ZSBhbiBSIFNjcmlwdCB3aGVyZSBjb2RlIGZlbmNlcyBjYW5ub3QgYmUgdXNlZC4KCk5vdywgZmluYWxseSwgbGV0J3MgcnVuIGEgcXVlcnkgdGhhdCByZXR1cm5zIHNvbWUgZGF0YSBzbyB3ZSBjYW4gYmUgc3VyZSBpdCB3b3JrZWQuCgpgYGB7c3FsIGNvbm5lY3Rpb249bGVzc29uREJ9CnNlbGVjdCAqIGZyb20gbGVzc29uczsKYGBgCgpTbywgbm93IHlvdSBoYXZlIHNlZW4gaG93IHRvIGNyZWF0ZSwgY29ubmVjdCB0bywgYW5kIHdvcmsgd2l0aCBhIFNRTGl0ZSBkYXRhYmFzZSBpbiBSLgoKIyMjIFdyaXRpbmcgRGF0YSBGcmFtZXMKCkFzIGFuIGFsdGVybmF0aXZlIHRvIGluc2VydGluZyBkYXRhIG9uZSAob3IgbXVsdGlwbGUpIHJvd3MgYXQgYSB0aW1lLCBSIGhhcyBhIGZ1bmN0aW9uIHRoYXQgd3JpdGVzIGFuIGVudGlyZSBkYXRhIGZyYW1lIHRvIGEgdGFibGUgaW4gdGhlIGRhdGFiYXNlW14zXS4KClteM106IFRoaXMgcmVxdWlyZXMgc3VwcG9ydCBmcm9tIHRoZSBkYXRhYmFzZSBmb3IgImJ1bGsgaW5zZXJ0aW9uIi4gU1FMaXRlIHN1cHBvcnRzIHRoaXMgYnkgZGVmYXVsdCwgd2hpbGUgTXlTUUwgYW5kIG90aGVyIGRhdGFiYXNlcyBtdXN0IGJlIGNvbmZpZ3VyZWQgdG8gYWxsb3cgdGhhdC4gU29tZSBjbG91ZCBkYXRhYmFzZSBzZXJ2aWNlcyBzdWNoIGFzICpkYjRmcmVlLm5ldCogZG8gbm90IGFsbG93IHRoZSB1c2Ugb2YgYGRiV3JpdGVUYWJsZWAuCgpUaGUgY29kZSBiZWxvdyBjcmVhdGVzIHRoZSAqImZsaWdodHNEQi5kYiIqIGRhdGFiYXNlIGZyb20gdGhlIGRhdGEgZnJhbWVzIGluIHRoZSAqKm55Y2ZsaWdodHMxMyoqIHBhY2thZ2UgdGhhdCB3ZSBoYXZlIGJlZW4gdXNpbmcuCgpgYGB7ciBjcmVhdGVOWUNGTElHSFQxM1NRTGl0ZURCLCBpbmNsdWRlPUZ9CmxpYnJhcnkobnljZmxpZ2h0czEzKQpsaWJyYXJ5KFJTUUxpdGUpCgojIGNyZWF0ZSBuZXcgZGF0YWJhc2Ugb3IgY29ubmVjdCBpZiBkYXRhYmFzZSBleGlzdHMKZmxpZ2h0c0RCIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgImZsaWdodHNEQi5kYiIpCgojIHdyaXRlIGVhY2ggZGF0YSBmcmFtZSB0byBhIHRhYmxlCmRiV3JpdGVUYWJsZShmbGlnaHRzREIsICJmbGlnaHRzIiwgZmxpZ2h0cywgb3ZlcndyaXRlPVQsIHJvdy5uYW1lcz1GKQpkYldyaXRlVGFibGUoZmxpZ2h0c0RCLCAiYWlycG9ydHMiLCBhaXJwb3J0cywgb3ZlcndyaXRlPVQsIHJvdy5uYW1lcz1GKQpkYldyaXRlVGFibGUoZmxpZ2h0c0RCLCAiYWlybGluZXMiLCBhaXJsaW5lcywgb3ZlcndyaXRlPVQsIHJvdy5uYW1lcz1GKQpkYldyaXRlVGFibGUoZmxpZ2h0c0RCLCAid2VhdGhlciIsIHdlYXRoZXIsIG92ZXJ3cml0ZT1ULCByb3cubmFtZXM9RikKZGJXcml0ZVRhYmxlKGZsaWdodHNEQiwgInBsYW5lcyIsIHBsYW5lcywgb3ZlcndyaXRlPVQsIHJvdy5uYW1lcz1GKQpgYGAKClRvIHZlcmlmeSB0aGF0IHRoZSBjcmVhdGlvbiBvZiB0aGUgZGF0YWJhc2Ugd2FzIHN1Y2Nlc3NmdWwsIHdlIHdpbGwgcmV0cmlldmUgc29tZSBkYXRhIGZyb20gbXVsdGlwbGUgdGFibGVzIHVzaW5nIGEgam9pbi4gRm9yIGNvbnZlbmllbmNlLCB3ZSdsbCB1c2UgYSBTUUwgY29kZSBjaHVuayByYXRoZXIgdGhhbiB1c2luZyBhbiBSIGZ1bmN0aW9uLgoKYGBge3NxbCBjb25uZWN0aW9uPWZsaWdodHNEQn0Kc2VsZWN0IGwubmFtZSBhcyAnYWlybGluZScsIAogICAgICAgZi5jYXJyaWVyIGFzICdjb2RlJywgCiAgICAgICBmLmZsaWdodCwgCiAgICAgICBhLm5hbWUgYXMgJ29yaWdpbicKICBmcm9tIGZsaWdodHMgZiBqb2luIGFpcnBvcnRzIGEgb24gKGYub3JpZ2luID0gYS5mYWEpCiAgICAgICAgICAgICAgICAgam9pbiBhaXJsaW5lcyBsIG9uIChmLmNhcnJpZXIgPSBsLmNhcnJpZXIpCiBsaW1pdCA1OwpgYGAKClNvLCBub3cgd2UgaGF2ZSBzZWVuIGhvdyB0byB3cml0ZSBlbnRpcmUgZGF0YSBmcmFtZXMgdG8gYSByZWxhdGlvbmFsIGRhdGFiYXNlLgoKIyMgU1FMIHZzIFIKCkluIG1hbnkgY2FzZXMsIFNRTCBpcyBzaW1wbGVyIGFuZCBtb3JlICJ1bml2ZXJzYWwiIGJ1dCBSIGlzIG9mdGVuIGZhc3Rlci4gU29tZSBxdWVyaWVzLCBwYXJ0aWN1bGFybHkgYWNyb3NzIG11bHRpcGxlIGRhdGEgZnJhbWVzIGFuZCBpbnZvbHZpbmcgZ3JvdXBpbmcsIGlzIHNpbXBsZXN0IGluIFNRTC4gT3RoZXJzLCBjYW4gYmUgZG9uZSBlaXRoZXIgd2l0aCBTUUwgb3Igd2l0aCBSIGZ1bmN0aW9ucy4gSW4gbWFueSBzaXR1YXRpb25zLCBpZiB0aGUgZGF0YSBmcmFtZSBpcyBpbiBtZW1vcnksIHRoZW4gUiBmdW5jdGlvbnMgYXJlIG11Y2ggZmFzdGVyLgoKTGV0J3MgbG9vayBhdCBhIGZldyBxdWVyaWVzIGFuZCBjYXJyeSB0aGVtIG91dCB3aXRoIFNRTCBhbmQgd2l0aCBCYXNlIFIuCgojIyBXb3JrZWQgRXhhbXBsZXMKClRoZSB3b3JrZWQgZXhhbXBsZXMgYmVsb3cgdXNlIHRoZSB0YWJsZXMgZGVmaW5lZCBpbiB0aGUgKm55Y2ZsaWdodHMxMyogZGF0YWJhc2UgYW5kIHRoZSBkYXRhIGZyYW1lcyBmcm9tIHRoZSAqbnljZmxpZ2h0MTMqIHBhY2thZ2UuIFdlIHdpbGwgc3dpdGNoIGJldHdlZW4gdGhlIHR3byBidXQgdGhlIGRhdGEgbW9kZWwgaXMgdGhlIHNhbWUgYW5kIHNob3duIGJlbG93IGFnYWluIGZvciBjb252ZW5pZW5jZS4KCjo6OiB7c3R5bGU9IndpZHRoOiA2NDBweDsgaGVpZ2h0OiA0ODBweDsgbWFyZ2luOiAxcHg7IHBvc2l0aW9uOiByZWxhdGl2ZTsifQo8aWZyYW1lIGFsbG93ZnVsbHNjcmVlbiBmcmFtZWJvcmRlcj0iMSIgc3R5bGU9IndpZHRoOjY0MHB4OyBoZWlnaHQ6NDgwcHgiIHNyYz0iaHR0cHM6Ly9sdWNpZC5hcHAvZG9jdW1lbnRzL2VtYmVkZGVkLzQwOTA2YjgwLWNlMTktNGUyNi1hMmUyLTViYzJmZjk3OWNmMiIgaWQ9IjdzYn5WTn5sflFPYyI+Cgo8L2lmcmFtZT4KOjo6CgpCZWZvcmUgd2UgY2FuIG1ha2UgYW55IHF1ZXJpZXMgaW4gYSBkYXRhYmFzZSwgd2Ugd2lsbCBuZWVkIHRvIGNvbm5lY3QgdG8gdGhlIGRhdGFiYXNlLgoKVG8gZm9sbG93IGFsb25nIGFuZCB0cnkgdGhlIGV4YW1wbGVzIGZvciB5b3Vyc2VsZiwgZm9sbG93IHRoZXNlIHN0ZXBzOgoKMS4gIENyZWF0ZSBhIG5ldyBSIFByb2plY3QKMi4gIERvd25sb2FkIHRoZSBTUUxpdGUgZGF0YWJhc2U6IFtmbGlnaHRzREIuZGJdKGZsaWdodHNEQi5kYilbXjRdCjMuICBDb3B5IHRoZSBkb3dubG9hZGVkICoiZmxpZ2h0c0RCLmRiIiogaW50byB5b3VyIHByb2plY3QgZm9sZGVyCjQuICBDcmVhdGUgYSBuZXcgUiBOb3RlYm9vawo1LiAgQ29ubmVjdCB0byB0aGUgZGF0YWJhc2UKClteNF06IEJlIHN1cmUgdG8gcmlnaHQtY2xpY2sgb24gdGhlIGxpbmsgYW5kIGNob29zZSAqU2F2ZSBBcy4uLiogb3IgKlNhdmUgTGluayBBcy4uLiogcmF0aGVyIHRoYW4gY2xpY2tpbmcgb24gdGhlIGxpbmsgd2hpY2ggd2lsbCBub3Qgd29yayBhcyB0aGUgYnJvd3NlciB3b3VsZCB0cnkgdG8gZGlzcGxheSB0aGUgZGF0YWJhc2UgZmlsZSBhcyBhIGRvY3VtZW50LgoKVGhlIGNvZGUgdG8gY29ubmVjdCB0byB0aGUgZGF0YWJhc2UgaXMgcmVwZWF0ZWQgYmVsb3cuIEl0IGFsc28gbG9hZHMgdGhlIGRhdGEgZnJhbWVzIGZyb20gdGhlICoqbnljZmxpZ2h0czEzKiogcGFja2FnZSB0byBkZW1vbnN0cmF0ZSAqKnNxbGRmKiouCgpgYGB7cn0KbGlicmFyeShueWNmbGlnaHRzMTMpCmxpYnJhcnkoUlNRTGl0ZSkKCmRiY29uIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgImZsaWdodHNEQi5kYiIpCmBgYAoKQXMgeW91IGNhbiBzZWUsIHRoZSBjb25uZWN0aW9uIG9iamVjdCBmb3Ige3NxbH0gY2h1bmtzIGFuZCBSIGRhdGFiYXNlIGZ1bmN0aW9ucyBpcyAqZGJjb24qLgoKIyMjIEV4YW1wbGUgMSB7LnRhYnNldH0KCiMjIyMgUXVlc3Rpb24KCkxpc3QgdGhlIGNhcnJpZXIgYW5kIGZsaWdodCBudW1iZXIgZm9yIGFsbCBmbGlnaHRzIHdpdGggYW4gYWlydGltZSBvZiBtb3JlIHRoYW4gNiBob3Vycy4gTGltaXQgdGhlIHJlc3VsdCB0byB0aGUgZmlyc3QgMTAgcm93cy4KCiMjIyMgU29sdXRpb24KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0IGNhcnJpZXIsIGZsaWdodCwgYWlyX3RpbWUgCiAgZnJvbSBmbGlnaHRzIAogd2hlcmUgYWlyX3RpbWUgPiAoNio2MCkKIGxpbWl0IDEwOwpgYGAKCiMjIyBFeGFtcGxlIDIgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpIb3cgbWFueSBmbGlnaHRzIGhhdmUgYW4gYWlydGltZSBvZiBsZXNzIHRoYW4gMSBob3VyLgoKIyMjIyBTb2x1dGlvbiBJOiBTUUwKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0IGNvdW50KCopIGFzICdOdW1GbGlnaHRzTFRPbmVIcicgCiAgZnJvbSBmbGlnaHRzCiB3aGVyZSBhaXJfdGltZSA8IDYwOwpgYGAKCiMjIyMgU29sdXRpb24gSUk6IFIKCkFzc3VtaW5nIHRoYXQgd2UgaGF2ZSB0aGUgZGF0YSBpbiBtZW1vcnkgYW5kIGFzc3VtaW5nIHRoYXQgdGhlIGRhdGEgZml0cyBpbnRvIG1lbW9yeSwgdGhlbiB3ZSBjYW4gYWxzbyBjcmFmdCBhIHNvbHV0aW9uIHRoYXQgdXNlcyBSIGZ1bmN0aW9ucyBhbmQgZG9lcyBub3QgdXNlIFNRTC4KCmBgYHtyfQpuIDwtIGxlbmd0aCh3aGljaChmbGlnaHRzJGFpcl90aW1lIDwgNjApKQoKcHJpbnQobikKYGBgCgojIyMjIFNvbHV0aW9uIElJSTogUmVsYXRpb25hbCBBbGdlYnJhCgokXHBpJAoKIyMjIEV4YW1wbGUgMyB7LnRhYnNldH0KCiMjIyMgUXVlc3Rpb24KClVzZSB0aGUgaW4tbWVtb3J5IGRhdGEgZnJhbWVzIGFuZCBjcmVhdGUgYSBxdWVyeSB0aGF0IGZpbmRzIHRoZSBudW1iZXIgb2YgZmxpZ2h0cyBwZXIgYWlybGluZS4KCiMjIyMgU29sdXRpb24KClRoZSBzb2x1dGlvbiB1c2VzIFNRTCB3aXRoICoqc3FsZGYqKi4gVXNpbmcgQmFzZSBSIHdvdWxkIGJlIHZlcnkgZGlmZmljdWx0IC0tIGlmIHlvdSBkbyBub3QgYWdyZWUsIHRoZW4gYnVpbGQgYSBzb2x1dGlvbiB1c2luZyBSIGFuZCBub3QgdXNpbmcgU1FMLgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjYXJyaWVyLCBjb3VudCgqKSBBUyBudW1GbGlnaHRzIiwKICAiICBGUk9NIGZsaWdodHMiLAogICIgR1JPVVAgQlkgY2FycmllciIpCnJzIDwtIHNxbGRmOjpzcWxkZihzcWwpCnByaW50KHJzKQpgYGAKCiMjIyBFeGFtcGxlIDQgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpVc2UgdGhlIGluLW1lbW9yeSBkYXRhIGZyYW1lcyBhbmQgY3JlYXRlIGEgcXVlcnkgdXNpbmcgKipzcWxkZioqIHRoYXQgZmluZHMgdGhlIGNhcnJpZXJzIHdpdGggZmV3ZXIgdGhhbiAxMDAwIGZsaWdodHMgc29ydGVkIGFscGhhYmV0aWNhbGx5LgoKIyMjIyBTb2x1dGlvbgoKYGBge3J9CnNxbCA8LSBwYXN0ZTAoCiAgIlNFTEVDVCBjYXJyaWVyLCBjb3VudCgqKSBBUyBudW1GbGlnaHRzIiwKICAiICBGUk9NIGZsaWdodHMiLAogICIgR1JPVVAgQlkgY2FycmllciAiLAogICJIQVZJTkcgbnVtRmxpZ2h0cyA8IDEwMDAiLAogICIgT1JERVIgQlkgY2FycmllciIpCnJzIDwtIHNxbGRmOjpzcWxkZihzcWwpCnByaW50KHJzKQpgYGAKCiMjIyBFeGFtcGxlIDUgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpIb3cgbWFueSB1bmlxdWUgY2FycmllcnMgYXJlIHRoZXJlPwoKIyMjIyBTb2x1dGlvbiBJOiBTUUwKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KU0VMRUNUIGNvdW50KERJU1RJTkNUIGNhcnJpZXIpIEFTIG51bUNhcnJpZXJzIEZST00gZmxpZ2h0cwpgYGAKCiMjIyBFeGFtcGxlIDYgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpXaGljaCBmbGlnaHRzIGhhZCBhIGJlbG93IGF2ZXJhZ2UgZGVwYXJ0dXJlIGRlbGF5PyBMaXN0IHRoZSBjYXJyaWVyIGFuZCBmbGlnaHQgbnVtYmVyIGFuZCB0aGVpciBkZXBhcnR1cmUgZGVsYXkuCgojIyMjIFNvbHV0aW9uIEk6IFNRTAoKV2UgbmVlZCB0byBmaW5kIHRoZSBhdmVyYWdlIGRlcGFydHVyZSBkZWxheSBhbmQgdGhlbiB1c2UgdGhpcyB2YWx1ZSBhcyBhbiBhcmd1bWVudCBmb3IgdGhlICp3aGVyZSogY2xhdXNlLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpTRUxFQ1QgZi5jYXJyaWVyLCBmLmZsaWdodCwgZi5kZXBfZGVsYXkKICBGUk9NIGZsaWdodHMgQVMgZgogV0hFUkUgZi5hcnJfZGVsYXkgPCAoU0VMRUNUIGF2ZyhhcnJfZGVsYXkpCiAgICAgICAgICAgICAgICAgICAgICAgIEZST00gZmxpZ2h0cykKIExJTUlUIDU7CmBgYAoKIyMjIyBTb2x1dGlvbiBJSTogUgoKYGBge3J9CiMgY2FsY3VsYXRlIHRoZSBhdmVyYWdlIGRlcGFydHVyZSBkZWxheSBpZ25vcmluZyBtaXNzaW5nIHZhbHVlcyAoTkEpCm0gPC0gbWVhbihmbGlnaHRzJGRlcF9kZWxheSwgbmEucm0gPSBUKQoKIyBzZWxlY3Qgcm93cyB3aGVyZSBkZXBhcnR1cmUgZGVsYXkgaXMgYmVsb3cgYXZlcmFnZQpycyA8LSBmbGlnaHRzW3doaWNoKGZsaWdodHMkZGVwX2RlbGF5IDwgbSksYygiY2FycmllciIsImZsaWdodCIsImRlcF9kZWxheSIpXQoKaGVhZChycywgNSkKYGBgCgojIyMgRXhhbXBsZSA3IHsudGFic2V0fQoKIyMjIyBRdWVzdGlvbgoKTGlzdCB0aGUgbmFtZXMgb2YgYWxsIGFpcmxpbmVzIHRoYXQgaGF2ZSBhdCBsZWFzdCBvbmUgZmxpZ2h0IGluIGFscGhhYmV0aWNhbCBvcmRlci4gUmVtb3ZlIGFsbCBkdXBsaWNhdGVzLgoKIyMjIyBTb2x1dGlvbjogU1FMCgpUaGVyZSBtYXkgYmUgYWlybGluZXMgaW4gdGhlICphaXJsaW5lcyogdGFibGVzIHRoYXQgYXJlIG5vdCBsaW5rZWQgdG8gZnJvbSB0aGUgKmZsaWdodHMqIHRhYmxlLCAqaS5lLiosIHRoZXJlIGlzIG5vdCBmbGlnaHQgZm9yIHRoZW0uIFNvLCB0byBsaXN0IG9ubHkgdGhlIGFpcmxpbmVzIHRoYXQgaGF2ZSBhdCBsZWFzdCBvbmUgZmxpZ2h0IHdlIG5lZWQgdG8gam9pbiAqYWlybGluZXMqIGFuZCAqZmxpZ2h0cyouCgpJbiB0aGUgc29sdXRpb24gd2UgYXJlIGludHJvZHVjaW5nIGFsaWFzZXMgZm9yIHRoZSB0d28gdGFibGVzIGFuZCB3ZSBhcmUgdXNpbmcgdGhlbSBpbiB0aGUgam9pbiBjbGF1c2UuIFRoZXkgYXJlIHJlcXVpcmVkIGFzIHRoZSAqY2FycmllciogYXR0cmlidXRlIGlzIGluIHR3byB0YWJsZXMgYW5kIGlzIHRoZXJlZm9yZSBhbWJpZ3VvdXMuIFRoZSBrZXl3b3JkICphcyogY291bGQgaGF2ZSBiZWVuIG9taXR0ZWQuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBkaXN0aW5jdCBuYW1lIAogIGZyb20gZmxpZ2h0cyBhcyBmIGpvaW4gYWlybGluZXMgYXMgYSBvbiAoZi5jYXJyaWVyID0gYS5jYXJyaWVyKQogb3JkZXIgYnkgbmFtZTsKYGBgCgojIyMgRXhhbXBsZSA4IHsudGFic2V0fQoKIyMjIyBRdWVzdGlvbgoKV2hhdCBpcyB0aGUgdG90YWwgZGVsYXkgKGluIGhvdXJzKSBmb3IgZWFjaCBhaXJsaW5lPyBEaXNwbGF5IHRoZSBjYXJyaWVyICgqZS5nLiosIFVBIG9yIEFBKSBhbmQgdGhlIHRvdGFsIGRlbGF5IChzdW0pLgoKIyMjIyBTb2x1dGlvbjogU1FMCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBjYXJyaWVyLCBzdW0oZGVwX2RlbGF5KQogIGZyb20gZmxpZ2h0cwogZ3JvdXAgYnkgY2FycmllcgpgYGAKCiMjIyBFeGFtcGxlIDkgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpIb3cgbWFueSBmbGlnaHRzIGRlcGFydGVkIGZyb20gZWFjaCBhaXJwb3J0PyBMaXN0IHRoZSBuYW1lIG9mIHRoZSBhaXJwb3J0IGFuZCBudW1iZXIgb2YgZmxpZ2h0cy4KCiMjIyMgU29sdXRpb246IFNRTAoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgbmFtZSwgY291bnQoKikKICBmcm9tIGZsaWdodHMgZiBqb2luIGFpcnBvcnRzIGEgb24gKGYub3JpZ2luID0gYS5mYWEpCiBncm91cCBieSBvcmlnaW4KIGxpbWl0IDEwCmBgYAoKIyMjIEV4YW1wbGUgMTAgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpMaXN0IHRoZSBuYW1lIG9mIHRoZSBhaXJwb3J0IGFuZCBudW1iZXIgb2YgZmxpZ2h0cyBkdXJpbmcgdGhlIHdpbnRlciBtb250aHMgKERlYywgSmFuLCBGZWIsIE1hcikgZm9yIGFsbCBhaXJwb3J0cyB0aGF0IGhhZCBhdCBsZWFzdCAxMDAwMCBmbGlnaHRzLCBzb3J0ZWQgZnJvbSBoaWdoZXN0IHRvIGxvd2VzdC4KCiMjIyMgU29sdXRpb246IFNRTAoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgbmFtZSwgY291bnQoKikgYXMgJ251bURlcGFydHVyZXMnCiAgZnJvbSBmbGlnaHRzIGYgam9pbiBhaXJwb3J0cyBhIG9uIChmLm9yaWdpbiA9IGEuZmFhKQogIHdoZXJlIG1vbnRoIGluICgxMiwxLDIsMykKICBncm91cCBieSBvcmlnaW4KIGhhdmluZyBudW1EZXBhcnR1cmVzID4gMTAwMDAKICBvcmRlciBieSBudW1EZXBhcnR1cmVzIERFU0MKYGBgCgojIyMgRXhhbXBsZSAxMSB7LnRhYnNldH0KCiMjIyMgUXVlc3Rpb24KCkxpc3QgdGhlIGFpcmxpbmUgbmFtZSwgY2FycmllciBjb2RlLCBhbmQgdGhlIHRvdGFsIG51bWJlciBvZiBmbGlnaHRzIGVhY2ggaGFkLgoKIyMjIyBTb2x1dGlvbjogU1FMCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBuYW1lLCBjb3VudCgqKQogIGZyb20gZmxpZ2h0cyBmIGpvaW4gYWlycG9ydHMgYSBvbiAoZi5vcmlnaW4gPSBhLmZhYSkKIGdyb3VwIGJ5IG9yaWdpbgogbGltaXQgMTAKYGBgCgojIyMgRXhhbXBsZSAxMiB7LnRhYnNldH0KCiMjIyMgUXVlc3Rpb24KCkZpbmQgYWxsIGFpcmxpbmVzIHRoYXQgY29udGFpbiB0aGUgd29yZCAiQW1lcmljYSIuIExpc3QgdGhlIGFpcmxpbmUgY2FycmllciBjb2RlIGFuZCBhaXJsaW5lIG5hbWUuCgojIyMjIFNvbHV0aW9uOiBTUUwKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0IGNhcnJpZXIsIG5hbWUgCiAgZnJvbSBhaXJsaW5lcwogIHdoZXJlIG5hbWUgbGlrZSAnJUFtZXJpY2ElJwpgYGAKCiMjIyBFeGFtcGxlIDEzIHsudGFic2V0fQoKIyMjIyBRdWVzdGlvbgoKSG93IG1hbnkgZmxpZ2h0cyB3ZXJlIGRlbGF5ZWQgZHVyaW5nIHRoZSBzdW1tZXIgbW9udGhzIChKdW5lIHRvIFNlcHQpIGR1ZSB0byB3aW5kX2d1c3RzPwoKIyMjIyBTb2x1dGlvbjogU1FMCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBjb3VudCgqKSBhcyAnZGVsR3VzdHMnIAogIGZyb20gZmxpZ2h0cyBhcyBmIGpvaW4gd2VhdGhlciBhcyB3IG9uIAogICAgICAgICAoZi55ZWFyID0gdy55ZWFyIGFuZAogICAgICAgICAgZi5tb250aCA9IHcubW9udGggYW5kCiAgICAgICAgICBmLmRheSA9IHcuZGF5IGFuZAogICAgICAgICAgZi5ob3VyID0gdy5ob3VyKQogd2hlcmUgZi5tb250aCBpbiAoNiw3LDgsOSkgYW5kCiAgICAgICB3LndpbmRfZ3VzdCBub3QgbnVsbApgYGAKCiMjIyBFeGFtcGxlIDE0IHsudGFic2V0fQoKIyMjIyBRdWVzdGlvbgoKRmluZCB0aGUgZmxpZ2h0IG51bWJlciBhbmQgYWlybGluZSBuYW1lIHBsdXMgdGhlIGRhdGUgb2YgZGVwYXJ0dXJlIGFuZCB0aGUgbmFtZSBvZiB0aGUgZGVwYXJ0dXJlIGFpcnBvcnQgdGhhdCBoYWQgdGhlIGxvbmdlc3QgYXJyaXZhbCBkZWxheS4gRGlzcGxheSB0aGUgZGF0ZSBpbiB0aGUgZm9ybWF0ICdNL0QvWVlZWScgYW5kIHRoZSBkZWxheSBpbiBob3VycyByb3VuZGVkIHRvIG9uZSBkaWdpdCBvZiBwcmVjaXNpb24uCgojIyMjIFNvbHV0aW9uOiBTUUwKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0IG5hbWUsIGNhcnJpZXIsIGZsaWdodCwgcm91bmQoZGVwX2RlbGF5LzYwLDEpIGFzICdkZWxheScsIAogICAgICAgbW9udGggfHwgJy8nIHx8IGRheSB8fCAnLycgfHwgeWVhciBhcyAnZGF0ZScKICBmcm9tIGZsaWdodHMgam9pbiBhaXJwb3J0cyBvbiAob3JpZ2luID0gZmFhKQogd2hlcmUgZGVwX2RlbGF5ID0gKHNlbGVjdCBtYXgoZGVwX2RlbGF5KSBmcm9tIGZsaWdodHMpCmBgYAoKIyMjIEV4YW1wbGUgMTUgey50YWJzZXR9CgojIyMjIFF1ZXN0aW9uCgpGaW5kIHRoZSBmbGlnaHQgbnVtYmVyIGFuZCBhaXJsaW5lIG5hbWUgcGx1cyB0aGUgZGF0ZSBvZiBkZXBhcnR1cmUgYW5kIHRoZSBuYW1lIG9mIHRoZSBkZXBhcnR1cmUgYWlycG9ydCB0aGF0IGhhZCB0aGUgKnNlY29uZCBsb25nZXN0KiBhcnJpdmFsIGRlbGF5LiBEaXNwbGF5IHRoZSBkYXRlIGluIHRoZSBmb3JtYXQgJ00vRC9ZWVlZJyBhbmQgdGhlIGRlbGF5IGluIGhvdXJzIHJvdW5kZWQgdG8gb25lIGRpZ2l0IG9mIHByZWNpc2lvbi4KCiMjIyMgU29sdXRpb246IFNRTAoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgbmFtZSwgY2FycmllciwgZmxpZ2h0LCByb3VuZChkZXBfZGVsYXkvNjAsMSkgYXMgJ2RlbGF5JywgCiAgICAgICBtb250aCB8fCAnLycgfHwgZGF5IHx8ICcvJyB8fCB5ZWFyIGFzICdkYXRlJwogIGZyb20gZmxpZ2h0cyBqb2luIGFpcnBvcnRzIG9uIChvcmlnaW4gPSBmYWEpCiB3aGVyZSBkZXBfZGVsYXkgPSAoc2VsZWN0IG1pbihkZXBfZGVsYXkpCiAgICAgICAgICAgICAgICAgICAgICBmcm9tIChzZWxlY3QgZGVwX2RlbGF5IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmcm9tIGZsaWdodHMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hlcmUgZGVwX2RlbGF5ID4gMCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlciBieSBkZXBfZGVsYXkgREVTQyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdCAyKSkKIAoKYGBgCgojIyBMZWN0dXJlcyBhbmQgVHV0b3JpYWxzCgpUaGUgKGhvdXItbG9uZykgdmlkZW8gb2YgYSBsZWN0dXJlIG9uIHRoZXNlIHRvcGljcyBieSBEci4gTWFydGluIFNjaGVkbGJhdWVyIG9mIEtob3VyeSBPbmxpbmUgaXMgcHJvdmlkZWQgYmVsb3cuIFdlIHJlY29tbWVuZCB3YXRjaGluZyBpdCB0byBnZXQgYWRkaXRpb25hbCBpbnNpZ2h0cywgc2VlIHRoZSBjb25uZWN0aW9ucyBiZXR3ZWVuIEVSRHMsIFVNTCwgU1FMLCBSZWxhdGlvbmFsIERhdGFiYXNlcywgU1FMaXRlLCBhbmQgUi4gVGhlIGxlY3R1cmUgYWxzbyBwcm92aWRlZCBhbiBpbnRyb2R1Y3Rpb24gdG8gdG9vbHMgZm9yIGJ1aWxkaW5nIEVSRCBkaWFncmFtcywgd2hlcmUgaGUgZGVtb25zdHJhdGVzIGhvdyB0byBidWlsZCBhIHJlbGF0aW9uYWwgbW9kZWwgYXMgYSAqQ3JvdydzIEZvb3QqIEVSRCB1c2luZyBbTHVjaWRDaGFydF0oaHR0cDovL2x1Y2lkLmFwcCkgYW5kIGltcGxlbWVudCB0aGF0IG1vZGVsIGluIFtTUUxpdGVdKGh0dHA6Ly9zcWxpdGUub3JnKSBpbiBhbiBSIE5vdGVib29rIHVzaW5nIHtzcWx9IGNodW5rcy4KCjxpZnJhbWUgc3JjPSJodHRwczovL25vcnRoZWFzdGVybi5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9FbWJlZC5hc3B4P2lkPTBmNzUwY2M5LTdkM2EtNDZkZC1iYTE0LWFjZGIwMDI3MDNjNSZhbXA7YXV0b3BsYXk9ZmFsc2UmYW1wO29mZmVydmlld2VyPWZhbHNlJmFtcDtzaG93dGl0bGU9ZmFsc2UmYW1wO3Nob3dicmFuZD1mYWxzZSZhbXA7Y2FwdGlvbnM9ZmFsc2UmYW1wO2ludGVyYWN0aXZpdHk9YWxsIiBoZWlnaHQ9IjI3MCIgd2lkdGg9IjQ4MCIgc3R5bGU9ImJvcmRlcjogMXB4IHNvbGlkICM0NjQ2NDY7IiBhbGxvd2Z1bGxzY3JlZW4gYWxsb3c9ImF1dG9wbGF5IiBkYXRhLWV4dGVybmFsPSIxIj4KCjwvaWZyYW1lPgoKSW4gdGhpcyB2aWRlbyB0dXRvcmlhbCBiZWxvdywgS2hvdXJ5IEJvc3RvbidzIFByb2YuIFNjaGVkbGJhdWVyIGV4cGxhaW5zIGhvdyB0byBtYXAgdmFyaW91cyBhc3NvY2lhdGlvbnMsIGFnZ3JlZ2F0aW9ucywgbXVsdGktdmFsdWVkIGF0dHJpYnV0ZXMsIGFuZCBnZW5lcmFsaXphdGlvbnMgdG8gYSByZWxhdGlvbmFsIHNjaGVtYS4gR2VuZXJhbGl6YXRpb24gZGVmaW5lcyBhIHNwZWNpYWxpemF0aW9uIGhpZXJhcmNoeSBvZiBjbGFzc2VzIGluIGEgY29uY2VwdHVhbCBtb2RlbC4gV2hpbGUgc29tZSBkYXRhYmFzZXMgc3VwcG9ydCBnZW5lcmFsaXphdGlvbiBvciB0eXBlIGhpZXJhcmNoaWVzLCByZWxhdGlvbmFsIGRhdGFiYXNlIGRvIG5vdC4gTmV2ZXJ0aGVsZXNzLCB0aGVyZSBhcmUgd2F5cyB0byBpbXBsZW1lbnQgZ2VuZXJhbGl6YXRpb24gaW4gYSByZWxhdGlvbmFsIG1vZGVsLgoKPGlmcmFtZSBzdHlsZT0iZmxvYXQ6IGxlZnQ7IG1hcmdpbi1yaWdodDogMjBweDsgYm9yZGVyOiAxcHggc29saWQgIzQ2NDY0NjsiIHNyYz0iaHR0cHM6Ly9ub3J0aGVhc3Rlcm4uaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvRW1iZWQuYXNweD9pZD1kODFhYWE0NS0yOTE4LTQ3OWQtYjAyNy1hYmYyMDE2MmY5MTUmYW1wO2F1dG9wbGF5PWZhbHNlJmFtcDtvZmZlcnZpZXdlcj10cnVlJmFtcDtzaG93dGl0bGU9ZmFsc2UmYW1wO3Nob3dicmFuZD1mYWxzZSZhbXA7c3RhcnQ9MCZhbXA7aW50ZXJhY3Rpdml0eT1hbGwiIHdpZHRoPSI0ODAiIGhlaWdodD0iMjcwIiBhbGxvd2Z1bGxzY3JlZW49ImFsbG93ZnVsbHNjcmVlbiIgYWxsb3c9ImF1dG9wbGF5IiBkYXRhLWV4dGVybmFsPSIxIj4KCjwvaWZyYW1lPgoKIyMgU3VtbWFyeQoKVGhpcyBsZXNzb24gc2hvd2VkIHRoYXQgU1FMIGlzIGEgdW5pdmVyc2FsIGxhbmd1YWdlIGZvciBxdWVyeWluZyBpbmZvcm1hdGlvbiBpbiByZWxhdGlvbmFsbHkgb3JnYW5pemVkIHRhYnVsYXIgc3RydWN0dXJlcy4gSW4gYWRkaXRpb24sIHRoZSBsZXNzb24gZXhwbGFpbmVkIGtleSBvcGVyYXRpb25zIG9mIHJlbGF0aW9uYWwgYWxnZWJyYSB3aGljaCBmb3JtcyB0aGUgYmFzaXMgb2YgU1FMLgoKRmluYWxseSwgdGhlIGxlc3NvbiBkZW1vbnN0cmF0ZWQsIHRocm91Z2ggZXhhbXBsZXMsIGhvdyB0byBleGVjdXRlIFNRTCBxdWVyaWVzIG9uIGluLW1lbW9yeSBkYXRhIGZyYW1lcyB1c2luZyAqKnNxbGRmKiogYW5kIG9uIHRhYmxlcyBpbiBTUUxpdGUgZGF0YWJhc2VzIHVzaW5nIHtzcWx9IGNvZGUgY2h1bmtzIGFuZCBSIGZ1bmN0aW9ucy4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgRmlsZXMgJiBSZXNvdXJjZXMKCmBgYHtyIHppcEZpbGVzLCBlY2hvPUZBTFNFfQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LAogICAgICAgICAgICAgICAgIHBhcmFtcyRudW1iZXIpCgp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIAogICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksIi4iLHBhcmFtcyRudW1iZXIpCgojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlIKa25pdHI6OnJhd19odG1sKGRvd25sb2FkRmlsZXNMaW5rKCIuIiwgemlwTmFtZSwgdGV4dEFMaW5rKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFJlZmVyZW5jZXMKCi0gICBbU1FMaXRlIFR1dG9yaWFsXShodHRwczovL3d3dy5zcWxpdGV0dXRvcmlhbC5uZXQvKQoKLSAgIFtTZWlkbWFuLCBDbGF1ZGUgKEp1bmUgMjksIDIwMTgpLiBSIGFuZCBEYXRhIC0tIFdoZW4gU2hvdWxkIHdlIFVzZSBSZWxhdGlvbmFsIERhdGFiYXNlcz8uIFItQmxvZ2dlcnMuXShodHRwczovL3d3dy5yLWJsb2dnZXJzLmNvbS8yMDE4LzA2L3ItYW5kLWRhdGEtd2hlbi1zaG91bGQtd2UtdXNlLXJlbGF0aW9uYWwtZGF0YWJhc2VzLykKCi0gICBbV2lja2hhbSwgSC4sIEdyb2xlbXVuZCwgRy4gKDIwMjMpLiBSIGZvciBEYXRhIFNjaWVuY2UgLS0gQ2hhcHRlciAxMzogUmVsYXRpb25hbCBkYXRhLiAybmQgRWRpdGlvbi4gTydSZWlsbHkuXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3JlbGF0aW9uYWwtZGF0YS5odG1sKQoKIyMgRXJyYXRhCgpOb25lIGNvbGxlY3RlZCB5ZXQuIExldCB1cyBrbm93LgoKYGBgez1odG1sfQo8c2NyaXB0IHNyYz0iaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tL3N0YXRpYy9mZWVkYmFjazIuanMiIHR5cGU9InRleHQvamF2YXNjcmlwdCI+CiAgbmV3IEpvdGZvcm1GZWVkYmFjayh7CiAgICBmb3JtSWQ6ICIyMTIxODcwNzI3ODQxNTciLAogICAgYnV0dG9uVGV4dDogIkZlZWRiYWNrIiwKICAgIGJhc2U6ICJodHRwczovL2Zvcm0uam90Zm9ybS5jb20vIiwKICAgIGJhY2tncm91bmQ6ICIjRjU5MjAyIiwKICAgIGZvbnRDb2xvcjogIiNGRkZGRkYiLAogICAgYnV0dG9uU2lkZTogImxlZnQiLAogICAgYnV0dG9uQWxpZ246ICJjZW50ZXIiLAogICAgdHlwZTogZmFsc2UsCiAgICB3aWR0aDogNzAwLAogICAgaGVpZ2h0OiA1MDAsCiAgICBpc0NhcmRGb3JtOiBmYWxzZQogIH0pOwo8L3NjcmlwdD4KYGBgCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19kZXBsb3lLbml0LlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgo=