Introduction

Joins are an essential element of most queries that involve data that is stored in multiple tables. This tutorial shows the difference between the three most common types of joins: inner, natural, anti, and outer. Be sure to follow along in R.

The video tutorial below provides an overview and the rest of the lesson goes a bit deeper using a sample database. Watch the video first and follow along with the lesson. Of course, if you prefer to read and follow along with actual code, then you can skip the video tutorial and go straight to the lesson below.

Slide Deck for Video: SQL Primer - Joins

Before proceeding with the remainder of the tutorial, download the R Notebook for this tutorial and open it in R Studio.

Sample Database

The code fragments below create a small database useful for demonstrating the join queries. This SQLite database is created in memory rather than on disk. The database contains two tables: empl representing employees in some organization and office which tracks offices and their locations. The schema is defined as follows:

  • empl (eid, name, oid)
  • office (oid, num)
library(RSQLite)
dbcon <- dbConnect(RSQLite::SQLite(), ":memory:")

Turn off support for foreign key constraint checking, so we can add an unmatched row in a table for explanatory purposes. This might be done in practice during the loading of external data from a CSV or XML file.

PRAGMA foreign_keys = OFF

Create Tables

create table office (
 oid integer primary key,
 num text
);
create table empl (
 eid integer primary key,
 name text,
 oid integer,
 foreign key (oid) references office(oid)
);

Add Sample Data

Notice that some employees do not have an office and that some office are unoccupied. This will be important later when we demonstrate outer joins.

insert into office values 
 (10,"NI 132F"),
 (20,"WVH 310A"),
 (30,"RY 611"),
 (40,"CH 103"),
 (50,"106A");
insert into empl values 
 (601,"Jeff Goldblum",10),
 (602,"Ann Hathaway",20),
 (603,"Michael Keaton",30),
 (604,"Jennifer Hudson",NULL),
 (605,"Mark Wahlberg",44),
 (609,"Helen Miren", 50);

Show Table Contents

select * from office;
Table 1: 5 records
oid num
10 NI 132F
20 WVH 310A
30 RY 611
40 CH 103
50 106A
select * from empl;
Table 2: 6 records
eid name oid
601 Jeff Goldblum 10
602 Ann Hathaway 20
603 Michael Keaton 30
604 Jennifer Hudson NA
605 Mark Wahlberg 44
609 Helen Miren 50

Cartesian Product

When a query contains two or more tables in the FROM clause, SQL produces the Cartesian Product of the two tables, i.e., all combinations of rows from all tables. In the example below, if table empl has n rows and table office has m rows, then the Cartesian Product has n × m rows. Note that the foreign key (FK) and primary key (PK) are the same only for a few of the rows – these are the rows that are actually related and must be selected. This is done by an inner join where the FK and PK values are matched.

select *
  from empl, office;
Table 3: Displaying records 1 - 10
eid name oid oid num
601 Jeff Goldblum 10 10 NI 132F
601 Jeff Goldblum 10 20 WVH 310A
601 Jeff Goldblum 10 30 RY 611
601 Jeff Goldblum 10 40 CH 103
601 Jeff Goldblum 10 50 106A
602 Ann Hathaway 20 10 NI 132F
602 Ann Hathaway 20 20 WVH 310A
602 Ann Hathaway 20 30 RY 611
602 Ann Hathaway 20 40 CH 103
602 Ann Hathaway 20 50 106A

Note that the oid value from the empl table only matches some of the oid values of the office table; those are the ones where there is actually a link. For example, “Jeff Goldblum” is actually assigned to office 10, but he is shown with all rows of the office table. The diagram below makes that more clear.

PK/FK Matches
PK/FK Matches

So, what we want to do is select the rows where the value in the foreign key column of one table (oid from the empl table in the example above) is the same as the primary key value of the related table (oid from the office table in the example above). This is the essence of an inner join.

Inner Join

An inner join is the most common form of join: it results in rows from two tables where the PK in one table matches the FK in the other table. There are four different ways to express an inner join. All forms are equivalent and there’s no difference in performance.

Standard WHERE Clause with FK/PK Matching

In this classic approach, the primary key/foreign key match is explicitly expressed in the where clause of the query. While simple and common, it is not always apparent to a novice that an inner join is being performed.

select *
  from empl as e, office as o
 where e.oid = o.oid;
Table 4: 4 records
eid name oid oid num
601 Jeff Goldblum 10 10 NI 132F
602 Ann Hathaway 20 20 WVH 310A
603 Michael Keaton 30 30 RY 611
609 Helen Miren 50 50 106A

Join with a selection:

select e.name as EmployeeName, o.num as OfficeNumber
  from empl e, office o
 where e.oid = o.oid;
Table 5: 4 records
EmployeeName OfficeNumber
Jeff Goldblum NI 132F
Ann Hathaway WVH 310A
Michael Keaton RY 611
Helen Miren 106A

The keyword “as” is optional but good practice to add as it makes the code’s intent clearer.

select e.name as EmployeeName, o.num as OfficeNumber
  from empl as e, office as o
 where e.oid = o.oid;
Table 6: 4 records
EmployeeName OfficeNumber
Jeff Goldblum NI 132F
Ann Hathaway WVH 310A
Michael Keaton RY 611
Helen Miren 106A

INNER JOIN Syntax

This syntax makes it clear that an inner join occurs and what the matching key fields are. It is the preferred way to express an inner join.

select *
  from empl e inner join office o on (e.oid = o.oid);
Table 7: 4 records
eid name oid oid num
601 Jeff Goldblum 10 10 NI 132F
602 Ann Hathaway 20 20 WVH 310A
603 Michael Keaton 30 30 RY 611
609 Helen Miren 50 50 106A

And, again, with a projection:

select e.name, o.num
  from empl e inner join office o on (e.oid = o.oid);
Table 8: 4 records
name num
Jeff Goldblum NI 132F
Ann Hathaway WVH 310A
Michael Keaton RY 611
Helen Miren 106A

The example below removes the inner keyword.

select e.name, o.num
  from empl e join office o on (e.oid = o.oid);
Table 9: 4 records
name num
Jeff Goldblum NI 132F
Ann Hathaway WVH 310A
Michael Keaton RY 611
Helen Miren 106A

The aforementioned inner join syntax separates the join criteria from any other selection criteria in the where clause.

select e.name, o.num
  from empl e join office o on (e.oid = o.oid)
 where o.num like 'NI%';
Table 10: 1 records
name num
Jeff Goldblum NI 132F

Of course, it can also be combined with an ORDER BY clause:

select e.name, o.num
  from empl e join office o on (e.oid = o.oid)
  order by e.name asc, o.oid desc;
Table 11: 4 records
name num
Ann Hathaway WVH 310A
Helen Miren 106A
Jeff Goldblum NI 132F
Michael Keaton RY 611

Natural Join

A natural join is an inner join where the database matches two tables based on common column names. The intent is that PK/FK columns would be the only ones that are named the same. Of course, this is not always true and could result is some pretty wrong queries when two joined tables happen to have to columns with the same name but that are not PK or FK columns intended to link the table. Imagine if the num column on the office table were named name instead. Then the natural join below would only select rows where the oid columns match and where the name columns match. Of course, the name of an office and the name of an employee are two completely different attributes and will never match.

select *
  from empl natural join office;
Table 12: 4 records
eid name oid num
601 Jeff Goldblum 10 NI 132F
602 Ann Hathaway 20 WVH 310A
603 Michael Keaton 30 RY 611
609 Helen Miren 50 106A

Consider the following update to the database where some future database architect adds a name column to the office table so that ‘nicknames’ can be added to offices and meeting rooms. Since not all offices have nicknames, the column allows null values.

As an aside, one way to keep schema independence and confine changes to SQL statements to very few query updates is to use views.

alter table office
  add column name text;

Let’s add a couple of nicknames to some offices.

update office
   set name = 'Fishbowl'
where oid = 40;

Now let’s run the natural join query again. Recall that a natural join matches on common column names. Why are the suddenly no results? Simple: a natural join matches on common values between two table based on common column names.

select *
  from empl natural join office;

Because the two tables empl and office have oid and (now) name in common, the above query is actually equivalent to:

select *
  from empl e, office o
 where e.oid = o.oid
   and e.name = o.name;

So, you are looking for all offices where the office id in empl is equal to the office id in office which makes sense. But, you also only want the offices whose name is the same as the name of the employee – which makes no sense, of course.

So, be careful with natural joins. They might be convenient but can lead to unexpected (and nonsensical) behavior and query results.

Joining Multiple Tables

Joining multiple tables is accomplished by joining pairs of tables. In a join the tables must be somehow connect. They do not all have to be connected to each other, but there has to be a path from every table to every other table, perhaps through another table.

Let’s say that we expand the database with another table. One that tracks the campus and address for each office. So, we’ll need a new table campus in order to keep the database normalized.

create table campus (
 cid text primary key,
 name text not null,
 city text not null,
 state text not null,
 country text not null
);

We will also need to add another column to the office table to track the campus on which an office is located.

alter table office
  add column cid text;

Now, let’s add a few campus locations and then update the offices for their campus location.

insert into campus values 
 ('BOS', 'Main Campus Boston', 'Boston', 'MA', 'USA'),
 ('SV', 'Silicon Valley', 'San Jose', 'CA', 'USA'),
 ('TOR', 'Toronto', 'Toronto', 'ON', 'Canada'),
 ('VAN', 'Vancouver', 'Vancouver', 'BC', 'Canada'),
 ('NCH', 'New College of Humanities', 'London', '', 'UK'),
 ('CLT', 'Charlotte', 'Charlotte', 'NC', 'USA'),
 ('SF', 'San Francisco', 'San Francisco', 'CA', 'USA'),
 ('OL', 'Online', 'Online', '', ''),
 ('BUR', 'Costa Research Center', 'Burlington', 'MA', 'USA'),
 ('NAH', 'Marine Research Center', 'Nahant', 'MA', 'USA'),
 ('RXI', 'Roux Institute', 'Portland', 'ME', 'USA'),
 ('DC', 'Cyber Security Institute', 'Washington', 'VA', 'USA'),
 ('SEA', 'Seattle', 'Seattle', 'WA', 'USA');
update office
   set cid = 'BOS'
where oid IN (10,20,30,40);
update office
   set cid = 'SEA'
where oid IN (50);

So, now that we have three tables, let’s find all employees, their office, and their campus name. Once again, we can accomplish this task with either an explicit join clause or using pair-wise join statements. Both approaches are shown below.

select e.name, o.num, c.name
  from empl e, office o, campus c
 where e.oid = o.oid and o.cid = c.cid;
Table 13: 4 records
name num name
Jeff Goldblum NI 132F Main Campus Boston
Ann Hathaway WVH 310A Main Campus Boston
Michael Keaton RY 611 Main Campus Boston
Helen Miren 106A Seattle
select e.name, o.num, c.name
  from empl e join office o on (e.oid = o.oid) join campus c on (o.cid = c.cid);
Table 14: 4 records
name num name
Jeff Goldblum NI 132F Main Campus Boston
Ann Hathaway WVH 310A Main Campus Boston
Michael Keaton RY 611 Main Campus Boston
Helen Miren 106A Seattle

Note that the order in which the pair-wise joins are done doesn’t matter as the database query plan does a full Cartesian product first and then selects based on the join clauses.

Outer Joins

There are three flavors of outer joins: left, right, and full. While in an inner join, you get only matching rows, the various outer joins also add unmatched rows. The visual below shows the differences between the joins.

Joins Explained Visually
Joins Explained Visually

Left Outer Join

A left outer join select all rows in common between two tables, i.e., those linked by a PK/FK relationship, plus all unmatched rows from the left table in the join specification.

select *
  from empl e left join office o on (e.oid = o.oid)
Table 15: 6 records
eid name oid oid num name cid
601 Jeff Goldblum 10 10 NI 132F NA BOS
602 Ann Hathaway 20 20 WVH 310A NA BOS
603 Michael Keaton 30 30 RY 611 NA BOS
604 Jennifer Hudson NA NA NA NA NA
605 Mark Wahlberg 44 NA NA NA NA
609 Helen Miren 50 50 106A NA SEA

The example below removes the matched rows to only show unmatched rows. This can be useful to find those rows where there is a referential integrity issue that may have gone undetected, i.e., there’s an FK that doesn’t have a matching PK in the related table. While this should not happen, it could happen when low-quality data is imported.

select *
  from empl e left join office o on (e.oid = o.oid)
except
select *
  from empl e inner join office o on (e.oid = o.oid);
Table 16: 2 records
eid name oid oid num name cid
604 Jennifer Hudson NA NA NA NA NA
605 Mark Wahlberg 44 NA NA NA NA

The query below shows only those rows where there’s a missing FK; it is similar to above, although it finds only those employees who do not have an office, while a left outer join would also show employees who have been assigned an office that does not exist.

select *
  from empl
 where empl.oid is null;
Table 17: 1 records
eid name oid
604 Jennifer Hudson NA

Find Unmatched Foreign Keys

To find all of the unmatched foreign keys, i.e., where a foreign key has a value that does not correspond to a primary key, use the query below. This can be useful to detect FK/PK mismatches that may have occurred during a bulk loading of the database when referential integrity checking may have been temporarily suspended for performance reasons. Naturally, it is important to find such mismatches as this might otherwise influence queries and analytics results.

select e.name, e.oid
  from empl e left join office o on (e.oid = o.oid)
except
select e.name, e.oid
  from empl e inner join office o on (e.oid = o.oid)
except
select e.name, e.oid
  from empl e
 where e.oid is null;
Table 18: 1 records
name oid
Mark Wahlberg 44

Right Outer Join

A right outer join is the same as a left outer join except that the unmatched rows come from the table on the right in the join specification. SQLite does not support an explicit right outer join; it only support left outer join. But, one needs to simply reverse the tables from a left outer join to get a right outer join.

select *
  from office o left join empl e on (e.oid = o.oid)
Table 19: 5 records
oid num name cid eid name oid
10 NI 132F NA BOS 601 Jeff Goldblum 10
20 WVH 310A NA BOS 602 Ann Hathaway 20
30 RY 611 NA BOS 603 Michael Keaton 30
40 CH 103 Fishbowl BOS NA NA NA
50 106A NA SEA 609 Helen Miren 50

Full Outer Join

A full outer join is simply the union of a left outer join and a right outer join. It shows all matching rows (i.e., an inner join), plus unmatched rows from the right a left table. SQLite does not support a full outer join directly. However, it is simply the union of a left and right outer join.

But for this to work you need to specify the order of the columns; using * does not work as the two queries return the columns in a different order and the data is mangled – it does not return an error, it just doesn’t return a meaningful result. Note how the first column lists both office names and employee names – which is nonsense.

select *
  from office o left join empl e on (e.oid = o.oid)
union
select *
  from empl e left join office o on (e.oid = o.oid)
Table 20: Displaying records 1 - 10
oid num name cid eid name oid
10 NI 132F NA BOS 601 Jeff Goldblum 10
20 WVH 310A NA BOS 602 Ann Hathaway 20
30 RY 611 NA BOS 603 Michael Keaton 30
40 CH 103 Fishbowl BOS NA NA NA
50 106A NA SEA 609 Helen Miren 50
601 Jeff Goldblum 10 10 0 NA 0
602 Ann Hathaway 20 20 0 NA 0
603 Michael Keaton 30 30 0 NA 0
604 Jennifer Hudson NA NA NA NA NA
605 Mark Wahlberg 44 NA NA NA NA

Here is the corrected version that explicitly specifies the column order:

select e.name, o.num
  from office o left join empl e on (e.oid = o.oid)
union
select e.name, o.num
  from empl e left join office o on (e.oid = o.oid)
Table 21: 7 records
name num
NA CH 103
Ann Hathaway WVH 310A
Helen Miren 106A
Jeff Goldblum NI 132F
Jennifer Hudson NA
Mark Wahlberg NA
Michael Keaton RY 611

Anti-Join

In an anti-join we want to find all rows that are in one table but not the other. For example, we might want to find all office which are not occupied, i.e., in our schema from this tutorial, all offices in the office table which are not linked to from the empl table.

To find all the rows which are in office that are not in empl we first do a left join on the tables and then filter out those where the FK is NULL.

select o.oid, o.name, o.cid 
  from office as o
  left join empl as e on (o.oid = e.oid)
 where e.oid is null;
Table 22: 1 records
oid name cid
40 Fishbowl BOS

Self Joins

Self joins (or, reflexive joins) in SQL are a unique type of join where a table is joined with itself. This might sound a bit unusual at first, but self joins are quite useful in certain scenarios.

  1. Same Table, Different Aliases: In a self join, the same table appears twice in the query, but it’s represented by different aliases. This allows you to compare rows within the same table.

  2. Syntax: It follows the same syntax as a regular join. The difference lies in that both the left and right side of the join are the same table, just with different aliases.

    SELECT A.column_name, B.column_name
    FROM table_name AS A
    JOIN table_name AS B
    ON A.common_field = B.common_field;
  3. Types of Joins: You can use any type of join (INNER, LEFT, RIGHT, FULL) as a self join, depending on the requirement.

Use Cases for Self Joins

  1. Hierarchical Data: Useful in scenarios where the table has a hierarchical structure. For example, an employee table where each employee has a manager, and both employees and managers are in the same table.

  2. Comparisons within a Table: To compare rows within the same table. For instance, finding pairs of customers who live in the same city.

  3. Time-based Analysis: In tables where you have time-series data, you might want to compare a row with its preceding or succeeding row.

  4. Detecting Duplicates: To find duplicate entries based on certain criteria without using GROUP BY.

  5. Path Finding: In network or graph-based data, to find paths or connections between nodes stored in a single table.

Example

Imagine an Employees table with columns EmployeeID, Name, and ManagerID, where ManagerID is also an EmployeeID in the same table. To list each employee with their manager’s name, you’d use a self join:

SELECT E1.Name AS Employee, E2.Name AS Manager
FROM Employees AS E1
JOIN Employees AS E2 ON E1.ManagerID = E2.EmployeeID;

In this query, E1 and E2 are aliases for the same Employees table. E1 represents the employees, and E2 represents their managers.

Demonstration

In this advanced code walk and demonstration, Khoury Boston’s Dr. Dan Feinberg provides use cases for self joins and shows how to build them in MySQL, although the same syntax and principles apply to most other databases, including SQLite.

Conclusion

Self joins are a powerful tool in SQL, especially for handling hierarchical data, complex comparisons, and data analysis within the same table. Understanding when and how to use them can greatly enhance the efficiency and capability of your SQL queries.

Summary

This tutorial explained the several common types of joins: inner, natural, outer, anti, and reflexive. It did omit two types of (uncommon) joins though: the cross join and the theta join.


Files & Resources

All Files for Lesson 70.112

References

No references.

Errata

Let us know.

LS0tCnRpdGxlOiAiUmV0cmlldmluZyBEYXRhIGZyb20gTXVsdGlwbGUgVGFibGVzIFVzaW5nIFZhcmlvdXMgSm9pbnMiCnBhcmFtczoKICBjYXRlZ29yeTogNzAKICBudW1iZXI6IDExMgogIHRpbWU6IDQ1CiAgbGV2ZWw6IGJlZ2lubmVyCiAgdGFnczogInNxbCxqb2lucyxpbm5lcixvdXRlcixuYXR1cmFsIgogIGRlc2NyaXB0aW9uOiAiRXhwbGFpbnMgaW5uZXIsIG91dGVyLCBuYXR1cmFsLCBhbmQgYW50aSBqb2lucyBpbiBTUUwuIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgojIyBJbnRyb2R1Y3Rpb24KCkpvaW5zIGFyZSBhbiBlc3NlbnRpYWwgZWxlbWVudCBvZiBtb3N0IHF1ZXJpZXMgdGhhdCBpbnZvbHZlIGRhdGEgdGhhdCBpcyBzdG9yZWQgaW4gbXVsdGlwbGUgdGFibGVzLiBUaGlzIHR1dG9yaWFsIHNob3dzIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdGhlIHRocmVlIG1vc3QgY29tbW9uIHR5cGVzIG9mIGpvaW5zOiBpbm5lciwgbmF0dXJhbCwgYW50aSwgYW5kIG91dGVyLiBCZSBzdXJlIHRvIGZvbGxvdyBhbG9uZyBpbiBSLgoKVGhlIHZpZGVvIHR1dG9yaWFsIGJlbG93IHByb3ZpZGVzIGFuIG92ZXJ2aWV3IGFuZCB0aGUgcmVzdCBvZiB0aGUgbGVzc29uIGdvZXMgYSBiaXQgZGVlcGVyIHVzaW5nIGEgc2FtcGxlIGRhdGFiYXNlLiBXYXRjaCB0aGUgdmlkZW8gZmlyc3QgYW5kIGZvbGxvdyBhbG9uZyB3aXRoIHRoZSBsZXNzb24uIE9mIGNvdXJzZSwgaWYgeW91IHByZWZlciB0byByZWFkIGFuZCBmb2xsb3cgYWxvbmcgd2l0aCBhY3R1YWwgY29kZSwgdGhlbiB5b3UgY2FuIHNraXAgdGhlIHZpZGVvIHR1dG9yaWFsIGFuZCBnbyBzdHJhaWdodCB0byB0aGUgbGVzc29uIGJlbG93LgoKPGlmcmFtZSBzdHlsZT0iYm9yZGVyOiAxcHggc29saWQgIzQ2NDY0NjsiIHNyYz0iaHR0cHM6Ly9ub3J0aGVhc3Rlcm4uaG9zdGVkLnBhbm9wdG8uY29tL1Bhbm9wdG8vUGFnZXMvRW1iZWQuYXNweD9pZD1kNWZjZTMyZC03ODg1LTRlMjUtOGY0Mi1hYzk5MDBlYzYwNWUmYW1wO2F1dG9wbGF5PWZhbHNlJmFtcDtvZmZlcnZpZXdlcj10cnVlJmFtcDtzaG93dGl0bGU9ZmFsc2UmYW1wO3Nob3dicmFuZD1mYWxzZSZhbXA7c3RhcnQ9MCZhbXA7aW50ZXJhY3Rpdml0eT1hbGwiIHdpZHRoPSI0ODAiIGhlaWdodD0iMjcwIiBhbGxvd2Z1bGxzY3JlZW49ImFsbG93ZnVsbHNjcmVlbiIgYWxsb3c9ImF1dG9wbGF5IiBkYXRhLWV4dGVybmFsPSIxIj4KCjwvaWZyYW1lPgoKKlNsaWRlIERlY2sgZm9yIFZpZGVvOiogW1NRTCBQcmltZXIgLSBKb2luc10ocy03MC0xMTItc3FsLXByaW1lci1qb2lucy5wcHR4KQoKQmVmb3JlIHByb2NlZWRpbmcgd2l0aCB0aGUgcmVtYWluZGVyIG9mIHRoZSB0dXRvcmlhbCwgZG93bmxvYWQgdGhlIFtSIE5vdGVib29rIGZvciB0aGlzIHR1dG9yaWFsXShsLTcwLTExMi5SbWQpIGFuZCBvcGVuIGl0IGluIFIgU3R1ZGlvLgoKIyMgU2FtcGxlIERhdGFiYXNlCgpUaGUgY29kZSBmcmFnbWVudHMgYmVsb3cgY3JlYXRlIGEgc21hbGwgZGF0YWJhc2UgdXNlZnVsIGZvciBkZW1vbnN0cmF0aW5nIHRoZSBqb2luIHF1ZXJpZXMuIFRoaXMgU1FMaXRlIGRhdGFiYXNlIGlzIGNyZWF0ZWQgaW4gbWVtb3J5IHJhdGhlciB0aGFuIG9uIGRpc2suIFRoZSBkYXRhYmFzZSBjb250YWlucyB0d28gdGFibGVzOiAqZW1wbCogcmVwcmVzZW50aW5nIGVtcGxveWVlcyBpbiBzb21lIG9yZ2FuaXphdGlvbiBhbmQgKm9mZmljZSogd2hpY2ggdHJhY2tzIG9mZmljZXMgYW5kIHRoZWlyIGxvY2F0aW9ucy4gVGhlIHNjaGVtYSBpcyBkZWZpbmVkIGFzIGZvbGxvd3M6CgotICAgZW1wbCAoKiplaWQqKiwgbmFtZSwgKm9pZCopCi0gICBvZmZpY2UgKCoqb2lkKiosIG51bSkKCmBgYHtyfQpsaWJyYXJ5KFJTUUxpdGUpCmRiY29uIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgIjptZW1vcnk6IikKYGBgCgpUdXJuIG9mZiBzdXBwb3J0IGZvciBmb3JlaWduIGtleSBjb25zdHJhaW50IGNoZWNraW5nLCBzbyB3ZSBjYW4gYWRkIGFuIHVubWF0Y2hlZCByb3cgaW4gYSB0YWJsZSBmb3IgZXhwbGFuYXRvcnkgcHVycG9zZXMuIFRoaXMgbWlnaHQgYmUgZG9uZSBpbiBwcmFjdGljZSBkdXJpbmcgdGhlIGxvYWRpbmcgb2YgZXh0ZXJuYWwgZGF0YSBmcm9tIGEgQ1NWIG9yIFhNTCBmaWxlLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpQUkFHTUEgZm9yZWlnbl9rZXlzID0gT0ZGCmBgYAoKIyMjIENyZWF0ZSBUYWJsZXMKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KY3JlYXRlIHRhYmxlIG9mZmljZSAoCiBvaWQgaW50ZWdlciBwcmltYXJ5IGtleSwKIG51bSB0ZXh0Cik7CmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpjcmVhdGUgdGFibGUgZW1wbCAoCiBlaWQgaW50ZWdlciBwcmltYXJ5IGtleSwKIG5hbWUgdGV4dCwKIG9pZCBpbnRlZ2VyLAogZm9yZWlnbiBrZXkgKG9pZCkgcmVmZXJlbmNlcyBvZmZpY2Uob2lkKQopOwpgYGAKCiMjIyBBZGQgU2FtcGxlIERhdGEKCk5vdGljZSB0aGF0IHNvbWUgZW1wbG95ZWVzIGRvIG5vdCBoYXZlIGFuIG9mZmljZSBhbmQgdGhhdCBzb21lIG9mZmljZSBhcmUgdW5vY2N1cGllZC4gVGhpcyB3aWxsIGJlIGltcG9ydGFudCBsYXRlciB3aGVuIHdlIGRlbW9uc3RyYXRlIG91dGVyIGpvaW5zLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQppbnNlcnQgaW50byBvZmZpY2UgdmFsdWVzIAogKDEwLCJOSSAxMzJGIiksCiAoMjAsIldWSCAzMTBBIiksCiAoMzAsIlJZIDYxMSIpLAogKDQwLCJDSCAxMDMiKSwKICg1MCwiMTA2QSIpOwpgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KaW5zZXJ0IGludG8gZW1wbCB2YWx1ZXMgCiAoNjAxLCJKZWZmIEdvbGRibHVtIiwxMCksCiAoNjAyLCJBbm4gSGF0aGF3YXkiLDIwKSwKICg2MDMsIk1pY2hhZWwgS2VhdG9uIiwzMCksCiAoNjA0LCJKZW5uaWZlciBIdWRzb24iLE5VTEwpLAogKDYwNSwiTWFyayBXYWhsYmVyZyIsNDQpLAogKDYwOSwiSGVsZW4gTWlyZW4iLCA1MCk7CmBgYAoKIyMjIFNob3cgVGFibGUgQ29udGVudHMKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0ICogZnJvbSBvZmZpY2U7CmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgKiBmcm9tIGVtcGw7CmBgYAoKIyMgQ2FydGVzaWFuIFByb2R1Y3QKCldoZW4gYSBxdWVyeSBjb250YWlucyB0d28gb3IgbW9yZSB0YWJsZXMgaW4gdGhlICpGUk9NKiBjbGF1c2UsIFNRTCBwcm9kdWNlcyB0aGUgQ2FydGVzaWFuIFByb2R1Y3Qgb2YgdGhlIHR3byB0YWJsZXMsICppLmUuKiwgYWxsIGNvbWJpbmF0aW9ucyBvZiByb3dzIGZyb20gYWxsIHRhYmxlcy4gSW4gdGhlIGV4YW1wbGUgYmVsb3csIGlmIHRhYmxlICplbXBsKiBoYXMgKm4qIHJvd3MgYW5kIHRhYmxlICpvZmZpY2UqIGhhcyAqbSogcm93cywgdGhlbiB0aGUgQ2FydGVzaWFuIFByb2R1Y3QgaGFzICpuIMOXIG0qIHJvd3MuIE5vdGUgdGhhdCB0aGUgZm9yZWlnbiBrZXkgKEZLKSBhbmQgcHJpbWFyeSBrZXkgKFBLKSBhcmUgdGhlIHNhbWUgb25seSBmb3IgYSBmZXcgb2YgdGhlIHJvd3MgLS0gdGhlc2UgYXJlIHRoZSByb3dzIHRoYXQgYXJlIGFjdHVhbGx5IHJlbGF0ZWQgYW5kIG11c3QgYmUgc2VsZWN0ZWQuIFRoaXMgaXMgZG9uZSBieSBhbiAqaW5uZXIgam9pbiogd2hlcmUgdGhlIEZLIGFuZCBQSyB2YWx1ZXMgYXJlIG1hdGNoZWQuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCAqCiAgZnJvbSBlbXBsLCBvZmZpY2U7CmBgYAoKTm90ZSB0aGF0IHRoZSAqb2lkKiB2YWx1ZSBmcm9tIHRoZSAqZW1wbCogdGFibGUgb25seSBtYXRjaGVzIHNvbWUgb2YgdGhlICpvaWQqIHZhbHVlcyBvZiB0aGUgKm9mZmljZSogdGFibGU7IHRob3NlIGFyZSB0aGUgb25lcyB3aGVyZSB0aGVyZSBpcyBhY3R1YWxseSBhIGxpbmsuIEZvciBleGFtcGxlLCAiSmVmZiBHb2xkYmx1bSIgaXMgYWN0dWFsbHkgYXNzaWduZWQgdG8gb2ZmaWNlIDEwLCBidXQgaGUgaXMgc2hvd24gd2l0aCBhbGwgcm93cyBvZiB0aGUgKm9mZmljZSogdGFibGUuIFRoZSBkaWFncmFtIGJlbG93IG1ha2VzIHRoYXQgbW9yZSBjbGVhci4KCiFbUEsvRksgTWF0Y2hlc10oQ2FydFByb2QucG5nKQoKU28sIHdoYXQgd2Ugd2FudCB0byBkbyBpcyBzZWxlY3QgdGhlIHJvd3Mgd2hlcmUgdGhlIHZhbHVlIGluIHRoZSBmb3JlaWduIGtleSBjb2x1bW4gb2Ygb25lIHRhYmxlICgqb2lkKiBmcm9tIHRoZSAqZW1wbCogdGFibGUgaW4gdGhlIGV4YW1wbGUgYWJvdmUpIGlzIHRoZSBzYW1lIGFzIHRoZSBwcmltYXJ5IGtleSB2YWx1ZSBvZiB0aGUgcmVsYXRlZCB0YWJsZSAoKm9pZCogZnJvbSB0aGUgKm9mZmljZSogdGFibGUgaW4gdGhlIGV4YW1wbGUgYWJvdmUpLiBUaGlzIGlzIHRoZSBlc3NlbmNlIG9mIGFuICoqaW5uZXIgam9pbioqLgoKIyMgSW5uZXIgSm9pbgoKQW4gaW5uZXIgam9pbiBpcyB0aGUgbW9zdCBjb21tb24gZm9ybSBvZiBqb2luOiBpdCByZXN1bHRzIGluIHJvd3MgZnJvbSB0d28gdGFibGVzIHdoZXJlIHRoZSBQSyBpbiBvbmUgdGFibGUgbWF0Y2hlcyB0aGUgRksgaW4gdGhlIG90aGVyIHRhYmxlLiBUaGVyZSBhcmUgZm91ciBkaWZmZXJlbnQgd2F5cyB0byBleHByZXNzIGFuIGlubmVyIGpvaW4uIEFsbCBmb3JtcyBhcmUgZXF1aXZhbGVudCBhbmQgdGhlcmUncyBubyBkaWZmZXJlbmNlIGluIHBlcmZvcm1hbmNlLgoKIyMjIFN0YW5kYXJkIFdIRVJFIENsYXVzZSB3aXRoIEZLL1BLIE1hdGNoaW5nCgpJbiB0aGlzIGNsYXNzaWMgYXBwcm9hY2gsIHRoZSBwcmltYXJ5IGtleS9mb3JlaWduIGtleSBtYXRjaCBpcyBleHBsaWNpdGx5IGV4cHJlc3NlZCBpbiB0aGUgKndoZXJlKiBjbGF1c2Ugb2YgdGhlIHF1ZXJ5LiBXaGlsZSBzaW1wbGUgYW5kIGNvbW1vbiwgaXQgaXMgbm90IGFsd2F5cyBhcHBhcmVudCB0byBhIG5vdmljZSB0aGF0IGFuIGlubmVyIGpvaW4gaXMgYmVpbmcgcGVyZm9ybWVkLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgKgogIGZyb20gZW1wbCBhcyBlLCBvZmZpY2UgYXMgbwogd2hlcmUgZS5vaWQgPSBvLm9pZDsKYGBgCgpKb2luIHdpdGggYSBzZWxlY3Rpb246CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBlLm5hbWUgYXMgRW1wbG95ZWVOYW1lLCBvLm51bSBhcyBPZmZpY2VOdW1iZXIKICBmcm9tIGVtcGwgZSwgb2ZmaWNlIG8KIHdoZXJlIGUub2lkID0gby5vaWQ7CmBgYAoKVGhlIGtleXdvcmQgKiJhcyIqIGlzIG9wdGlvbmFsIGJ1dCBnb29kIHByYWN0aWNlIHRvIGFkZCBhcyBpdCBtYWtlcyB0aGUgY29kZSdzIGludGVudCBjbGVhcmVyLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgZS5uYW1lIGFzIEVtcGxveWVlTmFtZSwgby5udW0gYXMgT2ZmaWNlTnVtYmVyCiAgZnJvbSBlbXBsIGFzIGUsIG9mZmljZSBhcyBvCiB3aGVyZSBlLm9pZCA9IG8ub2lkOwpgYGAKCiMjIyBJTk5FUiBKT0lOIFN5bnRheAoKVGhpcyBzeW50YXggbWFrZXMgaXQgY2xlYXIgdGhhdCBhbiBpbm5lciBqb2luIG9jY3VycyBhbmQgd2hhdCB0aGUgbWF0Y2hpbmcga2V5IGZpZWxkcyBhcmUuIEl0IGlzIHRoZSBwcmVmZXJyZWQgd2F5IHRvIGV4cHJlc3MgYW4gaW5uZXIgam9pbi4KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0ICoKICBmcm9tIGVtcGwgZSBpbm5lciBqb2luIG9mZmljZSBvIG9uIChlLm9pZCA9IG8ub2lkKTsKYGBgCgpBbmQsIGFnYWluLCB3aXRoIGEgcHJvamVjdGlvbjoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0IGUubmFtZSwgby5udW0KICBmcm9tIGVtcGwgZSBpbm5lciBqb2luIG9mZmljZSBvIG9uIChlLm9pZCA9IG8ub2lkKTsKYGBgCgpUaGUgZXhhbXBsZSBiZWxvdyByZW1vdmVzIHRoZSAqaW5uZXIqIGtleXdvcmQuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBlLm5hbWUsIG8ubnVtCiAgZnJvbSBlbXBsIGUgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCk7CmBgYAoKVGhlIGFmb3JlbWVudGlvbmVkIGlubmVyIGpvaW4gc3ludGF4IHNlcGFyYXRlcyB0aGUgam9pbiBjcml0ZXJpYSBmcm9tIGFueSBvdGhlciBzZWxlY3Rpb24gY3JpdGVyaWEgaW4gdGhlICp3aGVyZSogY2xhdXNlLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgZS5uYW1lLCBvLm51bQogIGZyb20gZW1wbCBlIGpvaW4gb2ZmaWNlIG8gb24gKGUub2lkID0gby5vaWQpCiB3aGVyZSBvLm51bSBsaWtlICdOSSUnOwpgYGAKCk9mIGNvdXJzZSwgaXQgY2FuIGFsc28gYmUgY29tYmluZWQgd2l0aCBhbiBPUkRFUiBCWSBjbGF1c2U6CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBlLm5hbWUsIG8ubnVtCiAgZnJvbSBlbXBsIGUgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCkKICBvcmRlciBieSBlLm5hbWUgYXNjLCBvLm9pZCBkZXNjOwpgYGAKCiMjIE5hdHVyYWwgSm9pbgoKQSBuYXR1cmFsIGpvaW4gaXMgYW4gaW5uZXIgam9pbiB3aGVyZSB0aGUgZGF0YWJhc2UgbWF0Y2hlcyB0d28gdGFibGVzIGJhc2VkIG9uIGNvbW1vbiBjb2x1bW4gbmFtZXMuIFRoZSBpbnRlbnQgaXMgdGhhdCBQSy9GSyBjb2x1bW5zIHdvdWxkIGJlIHRoZSBvbmx5IG9uZXMgdGhhdCBhcmUgbmFtZWQgdGhlIHNhbWUuIE9mIGNvdXJzZSwgdGhpcyBpcyBub3QgYWx3YXlzIHRydWUgYW5kIGNvdWxkIHJlc3VsdCBpcyBzb21lIHByZXR0eSB3cm9uZyBxdWVyaWVzIHdoZW4gdHdvIGpvaW5lZCB0YWJsZXMgaGFwcGVuIHRvIGhhdmUgdG8gY29sdW1ucyB3aXRoIHRoZSBzYW1lIG5hbWUgYnV0IHRoYXQgYXJlIG5vdCBQSyBvciBGSyBjb2x1bW5zIGludGVuZGVkIHRvIGxpbmsgdGhlIHRhYmxlLiBJbWFnaW5lIGlmIHRoZSAqbnVtKiBjb2x1bW4gb24gdGhlICpvZmZpY2UqIHRhYmxlIHdlcmUgbmFtZWQgKm5hbWUqIGluc3RlYWQuIFRoZW4gdGhlIG5hdHVyYWwgam9pbiBiZWxvdyB3b3VsZCBvbmx5IHNlbGVjdCByb3dzIHdoZXJlIHRoZSAqb2lkKiBjb2x1bW5zIG1hdGNoICoqYW5kKiogd2hlcmUgdGhlICpuYW1lKiBjb2x1bW5zIG1hdGNoLiBPZiBjb3Vyc2UsIHRoZSBuYW1lIG9mIGFuIG9mZmljZSBhbmQgdGhlIG5hbWUgb2YgYW4gZW1wbG95ZWUgYXJlIHR3byBjb21wbGV0ZWx5IGRpZmZlcmVudCBhdHRyaWJ1dGVzIGFuZCB3aWxsIG5ldmVyIG1hdGNoLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgKgogIGZyb20gZW1wbCBuYXR1cmFsIGpvaW4gb2ZmaWNlOwpgYGAKCkNvbnNpZGVyIHRoZSBmb2xsb3dpbmcgdXBkYXRlIHRvIHRoZSBkYXRhYmFzZSB3aGVyZSBzb21lIGZ1dHVyZSBkYXRhYmFzZSBhcmNoaXRlY3QgYWRkcyBhICpuYW1lKiBjb2x1bW4gdG8gdGhlICpvZmZpY2UqIHRhYmxlIHNvIHRoYXQgJ25pY2tuYW1lcycgY2FuIGJlIGFkZGVkIHRvIG9mZmljZXMgYW5kIG1lZXRpbmcgcm9vbXMuIFNpbmNlIG5vdCBhbGwgb2ZmaWNlcyBoYXZlIG5pY2tuYW1lcywgdGhlIGNvbHVtbiBhbGxvd3MgKm51bGwqIHZhbHVlcy4KCkFzIGFuIGFzaWRlLCBvbmUgd2F5IHRvIGtlZXAgc2NoZW1hIGluZGVwZW5kZW5jZSBhbmQgY29uZmluZSBjaGFuZ2VzIHRvIFNRTCBzdGF0ZW1lbnRzIHRvIHZlcnkgZmV3IHF1ZXJ5IHVwZGF0ZXMgaXMgdG8gdXNlIHZpZXdzLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQphbHRlciB0YWJsZSBvZmZpY2UKICBhZGQgY29sdW1uIG5hbWUgdGV4dDsKYGBgCgpMZXQncyBhZGQgYSBjb3VwbGUgb2Ygbmlja25hbWVzIHRvIHNvbWUgb2ZmaWNlcy4KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KdXBkYXRlIG9mZmljZQogICBzZXQgbmFtZSA9ICdGaXNoYm93bCcKd2hlcmUgb2lkID0gNDA7CmBgYAoKTm93IGxldCdzIHJ1biB0aGUgKm5hdHVyYWwgam9pbiogcXVlcnkgYWdhaW4uIFJlY2FsbCB0aGF0IGEgbmF0dXJhbCBqb2luIG1hdGNoZXMgb24gY29tbW9uIGNvbHVtbiBuYW1lcy4gV2h5IGFyZSB0aGUgc3VkZGVubHkgbm8gcmVzdWx0cz8gU2ltcGxlOiBhIG5hdHVyYWwgam9pbiBtYXRjaGVzIG9uIGNvbW1vbiB2YWx1ZXMgYmV0d2VlbiB0d28gdGFibGUgYmFzZWQgb24gY29tbW9uIGNvbHVtbiBuYW1lcy4KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbiwgZXZhbD1GfQpzZWxlY3QgKgogIGZyb20gZW1wbCBuYXR1cmFsIGpvaW4gb2ZmaWNlOwpgYGAKCkJlY2F1c2UgdGhlIHR3byB0YWJsZXMgKmVtcGwqIGFuZCAqb2ZmaWNlKiBoYXZlICpvaWQqIGFuZCAobm93KSAqbmFtZSogaW4gY29tbW9uLCB0aGUgYWJvdmUgcXVlcnkgaXMgYWN0dWFsbHkgZXF1aXZhbGVudCB0bzoKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbiwgZXZhbD1GfQpzZWxlY3QgKgogIGZyb20gZW1wbCBlLCBvZmZpY2Ugbwogd2hlcmUgZS5vaWQgPSBvLm9pZAogICBhbmQgZS5uYW1lID0gby5uYW1lOwpgYGAKClNvLCB5b3UgYXJlIGxvb2tpbmcgZm9yIGFsbCBvZmZpY2VzIHdoZXJlIHRoZSBvZmZpY2UgaWQgaW4gKmVtcGwqIGlzIGVxdWFsIHRvIHRoZSBvZmZpY2UgaWQgaW4gKm9mZmljZSogd2hpY2ggbWFrZXMgc2Vuc2UuIEJ1dCwgeW91IGFsc28gb25seSB3YW50IHRoZSBvZmZpY2VzIHdob3NlIG5hbWUgaXMgdGhlIHNhbWUgYXMgdGhlIG5hbWUgb2YgdGhlIGVtcGxveWVlIC0tIHdoaWNoIG1ha2VzIG5vIHNlbnNlLCBvZiBjb3Vyc2UuCgpTbywgYmUgY2FyZWZ1bCB3aXRoIG5hdHVyYWwgam9pbnMuIFRoZXkgbWlnaHQgYmUgY29udmVuaWVudCBidXQgY2FuIGxlYWQgdG8gdW5leHBlY3RlZCAoYW5kIG5vbnNlbnNpY2FsKSBiZWhhdmlvciBhbmQgcXVlcnkgcmVzdWx0cy4KCiMjIEpvaW5pbmcgTXVsdGlwbGUgVGFibGVzCgpKb2luaW5nIG11bHRpcGxlIHRhYmxlcyBpcyBhY2NvbXBsaXNoZWQgYnkgam9pbmluZyBwYWlycyBvZiB0YWJsZXMuIEluIGEgam9pbiB0aGUgdGFibGVzIG11c3QgYmUgc29tZWhvdyBjb25uZWN0LiBUaGV5IGRvIG5vdCBhbGwgaGF2ZSB0byBiZSBjb25uZWN0ZWQgdG8gZWFjaCBvdGhlciwgYnV0IHRoZXJlIGhhcyB0byBiZSBhIHBhdGggZnJvbSBldmVyeSB0YWJsZSB0byBldmVyeSBvdGhlciB0YWJsZSwgcGVyaGFwcyB0aHJvdWdoIGFub3RoZXIgdGFibGUuCgpMZXQncyBzYXkgdGhhdCB3ZSBleHBhbmQgdGhlIGRhdGFiYXNlIHdpdGggYW5vdGhlciB0YWJsZS4gT25lIHRoYXQgdHJhY2tzIHRoZSBjYW1wdXMgYW5kIGFkZHJlc3MgZm9yIGVhY2ggb2ZmaWNlLiBTbywgd2UnbGwgbmVlZCBhIG5ldyB0YWJsZSAqY2FtcHVzKiBpbiBvcmRlciB0byBrZWVwIHRoZSBkYXRhYmFzZSBub3JtYWxpemVkLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpjcmVhdGUgdGFibGUgY2FtcHVzICgKIGNpZCB0ZXh0IHByaW1hcnkga2V5LAogbmFtZSB0ZXh0IG5vdCBudWxsLAogY2l0eSB0ZXh0IG5vdCBudWxsLAogc3RhdGUgdGV4dCBub3QgbnVsbCwKIGNvdW50cnkgdGV4dCBub3QgbnVsbAopOwpgYGAKCldlIHdpbGwgYWxzbyBuZWVkIHRvIGFkZCBhbm90aGVyIGNvbHVtbiB0byB0aGUgKm9mZmljZSogdGFibGUgdG8gdHJhY2sgdGhlIGNhbXB1cyBvbiB3aGljaCBhbiBvZmZpY2UgaXMgbG9jYXRlZC4KCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KYWx0ZXIgdGFibGUgb2ZmaWNlCiAgYWRkIGNvbHVtbiBjaWQgdGV4dDsKYGBgCgpOb3csIGxldCdzIGFkZCBhIGZldyBjYW1wdXMgbG9jYXRpb25zIGFuZCB0aGVuIHVwZGF0ZSB0aGUgb2ZmaWNlcyBmb3IgdGhlaXIgY2FtcHVzIGxvY2F0aW9uLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQppbnNlcnQgaW50byBjYW1wdXMgdmFsdWVzIAogKCdCT1MnLCAnTWFpbiBDYW1wdXMgQm9zdG9uJywgJ0Jvc3RvbicsICdNQScsICdVU0EnKSwKICgnU1YnLCAnU2lsaWNvbiBWYWxsZXknLCAnU2FuIEpvc2UnLCAnQ0EnLCAnVVNBJyksCiAoJ1RPUicsICdUb3JvbnRvJywgJ1Rvcm9udG8nLCAnT04nLCAnQ2FuYWRhJyksCiAoJ1ZBTicsICdWYW5jb3V2ZXInLCAnVmFuY291dmVyJywgJ0JDJywgJ0NhbmFkYScpLAogKCdOQ0gnLCAnTmV3IENvbGxlZ2Ugb2YgSHVtYW5pdGllcycsICdMb25kb24nLCAnJywgJ1VLJyksCiAoJ0NMVCcsICdDaGFybG90dGUnLCAnQ2hhcmxvdHRlJywgJ05DJywgJ1VTQScpLAogKCdTRicsICdTYW4gRnJhbmNpc2NvJywgJ1NhbiBGcmFuY2lzY28nLCAnQ0EnLCAnVVNBJyksCiAoJ09MJywgJ09ubGluZScsICdPbmxpbmUnLCAnJywgJycpLAogKCdCVVInLCAnQ29zdGEgUmVzZWFyY2ggQ2VudGVyJywgJ0J1cmxpbmd0b24nLCAnTUEnLCAnVVNBJyksCiAoJ05BSCcsICdNYXJpbmUgUmVzZWFyY2ggQ2VudGVyJywgJ05haGFudCcsICdNQScsICdVU0EnKSwKICgnUlhJJywgJ1JvdXggSW5zdGl0dXRlJywgJ1BvcnRsYW5kJywgJ01FJywgJ1VTQScpLAogKCdEQycsICdDeWJlciBTZWN1cml0eSBJbnN0aXR1dGUnLCAnV2FzaGluZ3RvbicsICdWQScsICdVU0EnKSwKICgnU0VBJywgJ1NlYXR0bGUnLCAnU2VhdHRsZScsICdXQScsICdVU0EnKTsKYGBgCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnVwZGF0ZSBvZmZpY2UKICAgc2V0IGNpZCA9ICdCT1MnCndoZXJlIG9pZCBJTiAoMTAsMjAsMzAsNDApOwpgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KdXBkYXRlIG9mZmljZQogICBzZXQgY2lkID0gJ1NFQScKd2hlcmUgb2lkIElOICg1MCk7CmBgYAoKU28sIG5vdyB0aGF0IHdlIGhhdmUgdGhyZWUgdGFibGVzLCBsZXQncyBmaW5kIGFsbCBlbXBsb3llZXMsIHRoZWlyIG9mZmljZSwgYW5kIHRoZWlyIGNhbXB1cyBuYW1lLiBPbmNlIGFnYWluLCB3ZSBjYW4gYWNjb21wbGlzaCB0aGlzIHRhc2sgd2l0aCBlaXRoZXIgYW4gZXhwbGljaXQgam9pbiBjbGF1c2Ugb3IgdXNpbmcgcGFpci13aXNlICpqb2luKiBzdGF0ZW1lbnRzLiBCb3RoIGFwcHJvYWNoZXMgYXJlIHNob3duIGJlbG93LgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgZS5uYW1lLCBvLm51bSwgYy5uYW1lCiAgZnJvbSBlbXBsIGUsIG9mZmljZSBvLCBjYW1wdXMgYwogd2hlcmUgZS5vaWQgPSBvLm9pZCBhbmQgby5jaWQgPSBjLmNpZDsKYGBgCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBlLm5hbWUsIG8ubnVtLCBjLm5hbWUKICBmcm9tIGVtcGwgZSBqb2luIG9mZmljZSBvIG9uIChlLm9pZCA9IG8ub2lkKSBqb2luIGNhbXB1cyBjIG9uIChvLmNpZCA9IGMuY2lkKTsKYGBgCgpOb3RlIHRoYXQgdGhlIG9yZGVyIGluIHdoaWNoIHRoZSBwYWlyLXdpc2Ugam9pbnMgYXJlIGRvbmUgZG9lc24ndCBtYXR0ZXIgYXMgdGhlIGRhdGFiYXNlIHF1ZXJ5IHBsYW4gZG9lcyBhIGZ1bGwgQ2FydGVzaWFuIHByb2R1Y3QgZmlyc3QgYW5kIHRoZW4gc2VsZWN0cyBiYXNlZCBvbiB0aGUgam9pbiBjbGF1c2VzLgoKIyMgT3V0ZXIgSm9pbnMKClRoZXJlIGFyZSB0aHJlZSBmbGF2b3JzIG9mIG91dGVyIGpvaW5zOiAqbGVmdCosICpyaWdodCosIGFuZCAqZnVsbCouIFdoaWxlIGluIGFuIGlubmVyIGpvaW4sIHlvdSBnZXQgb25seSBtYXRjaGluZyByb3dzLCB0aGUgdmFyaW91cyBvdXRlciBqb2lucyBhbHNvIGFkZCB1bm1hdGNoZWQgcm93cy4gVGhlIHZpc3VhbCBiZWxvdyBzaG93cyB0aGUgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgam9pbnMuCgohW0pvaW5zIEV4cGxhaW5lZCBWaXN1YWxseV0oSm9pbnNWaXN1YWwuanBnKQoKIyMjIExlZnQgT3V0ZXIgSm9pbgoKQSBsZWZ0IG91dGVyIGpvaW4gc2VsZWN0IGFsbCByb3dzIGluIGNvbW1vbiBiZXR3ZWVuIHR3byB0YWJsZXMsICppLmUuKiwgdGhvc2UgbGlua2VkIGJ5IGEgUEsvRksgcmVsYXRpb25zaGlwLCBwbHVzIGFsbCB1bm1hdGNoZWQgcm93cyBmcm9tIHRoZSBsZWZ0IHRhYmxlIGluIHRoZSBqb2luIHNwZWNpZmljYXRpb24uCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCAqCiAgZnJvbSBlbXBsIGUgbGVmdCBqb2luIG9mZmljZSBvIG9uIChlLm9pZCA9IG8ub2lkKQpgYGAKClRoZSBleGFtcGxlIGJlbG93IHJlbW92ZXMgdGhlIG1hdGNoZWQgcm93cyB0byBvbmx5IHNob3cgdW5tYXRjaGVkIHJvd3MuIFRoaXMgY2FuIGJlIHVzZWZ1bCB0byBmaW5kIHRob3NlIHJvd3Mgd2hlcmUgdGhlcmUgaXMgYSByZWZlcmVudGlhbCBpbnRlZ3JpdHkgaXNzdWUgdGhhdCBtYXkgaGF2ZSBnb25lIHVuZGV0ZWN0ZWQsICppLmUuKiwgdGhlcmUncyBhbiBGSyB0aGF0IGRvZXNuJ3QgaGF2ZSBhIG1hdGNoaW5nIFBLIGluIHRoZSByZWxhdGVkIHRhYmxlLiBXaGlsZSB0aGlzIHNob3VsZCBub3QgaGFwcGVuLCBpdCBjb3VsZCBoYXBwZW4gd2hlbiBsb3ctcXVhbGl0eSBkYXRhIGlzIGltcG9ydGVkLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgKgogIGZyb20gZW1wbCBlIGxlZnQgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCkKZXhjZXB0CnNlbGVjdCAqCiAgZnJvbSBlbXBsIGUgaW5uZXIgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCk7CmBgYAoKVGhlIHF1ZXJ5IGJlbG93IHNob3dzIG9ubHkgdGhvc2Ugcm93cyB3aGVyZSB0aGVyZSdzIGEgbWlzc2luZyBGSzsgaXQgaXMgc2ltaWxhciB0byBhYm92ZSwgYWx0aG91Z2ggaXQgZmluZHMgb25seSB0aG9zZSBlbXBsb3llZXMgd2hvIGRvIG5vdCBoYXZlIGFuIG9mZmljZSwgd2hpbGUgYSBsZWZ0IG91dGVyIGpvaW4gd291bGQgYWxzbyBzaG93IGVtcGxveWVlcyB3aG8gaGF2ZSBiZWVuIGFzc2lnbmVkIGFuIG9mZmljZSB0aGF0IGRvZXMgbm90IGV4aXN0LgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgKgogIGZyb20gZW1wbAogd2hlcmUgZW1wbC5vaWQgaXMgbnVsbDsKYGBgCgojIyMjIEZpbmQgVW5tYXRjaGVkIEZvcmVpZ24gS2V5cwoKVG8gZmluZCBhbGwgb2YgdGhlIHVubWF0Y2hlZCBmb3JlaWduIGtleXMsICppLmUuKiwgd2hlcmUgYSBmb3JlaWduIGtleSBoYXMgYSB2YWx1ZSB0aGF0IGRvZXMgbm90IGNvcnJlc3BvbmQgdG8gYSBwcmltYXJ5IGtleSwgdXNlIHRoZSBxdWVyeSBiZWxvdy4gVGhpcyBjYW4gYmUgdXNlZnVsIHRvIGRldGVjdCBGSy9QSyBtaXNtYXRjaGVzIHRoYXQgbWF5IGhhdmUgb2NjdXJyZWQgZHVyaW5nIGEgYnVsayBsb2FkaW5nIG9mIHRoZSBkYXRhYmFzZSB3aGVuIHJlZmVyZW50aWFsIGludGVncml0eSBjaGVja2luZyBtYXkgaGF2ZSBiZWVuIHRlbXBvcmFyaWx5IHN1c3BlbmRlZCBmb3IgcGVyZm9ybWFuY2UgcmVhc29ucy4gTmF0dXJhbGx5LCBpdCBpcyBpbXBvcnRhbnQgdG8gZmluZCBzdWNoIG1pc21hdGNoZXMgYXMgdGhpcyBtaWdodCBvdGhlcndpc2UgaW5mbHVlbmNlIHF1ZXJpZXMgYW5kIGFuYWx5dGljcyByZXN1bHRzLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpzZWxlY3QgZS5uYW1lLCBlLm9pZAogIGZyb20gZW1wbCBlIGxlZnQgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCkKZXhjZXB0CnNlbGVjdCBlLm5hbWUsIGUub2lkCiAgZnJvbSBlbXBsIGUgaW5uZXIgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCkKZXhjZXB0CnNlbGVjdCBlLm5hbWUsIGUub2lkCiAgZnJvbSBlbXBsIGUKIHdoZXJlIGUub2lkIGlzIG51bGw7CmBgYAoKIyMjIFJpZ2h0IE91dGVyIEpvaW4KCkEgcmlnaHQgb3V0ZXIgam9pbiBpcyB0aGUgc2FtZSBhcyBhIGxlZnQgb3V0ZXIgam9pbiBleGNlcHQgdGhhdCB0aGUgdW5tYXRjaGVkIHJvd3MgY29tZSBmcm9tIHRoZSB0YWJsZSBvbiB0aGUgcmlnaHQgaW4gdGhlIGpvaW4gc3BlY2lmaWNhdGlvbi4gU1FMaXRlIGRvZXMgbm90IHN1cHBvcnQgYW4gZXhwbGljaXQgcmlnaHQgb3V0ZXIgam9pbjsgaXQgb25seSBzdXBwb3J0IGxlZnQgb3V0ZXIgam9pbi4gQnV0LCBvbmUgbmVlZHMgdG8gc2ltcGx5IHJldmVyc2UgdGhlIHRhYmxlcyBmcm9tIGEgbGVmdCBvdXRlciBqb2luIHRvIGdldCBhIHJpZ2h0IG91dGVyIGpvaW4uCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCAqCiAgZnJvbSBvZmZpY2UgbyBsZWZ0IGpvaW4gZW1wbCBlIG9uIChlLm9pZCA9IG8ub2lkKQpgYGAKCiMjIyBGdWxsIE91dGVyIEpvaW4KCkEgZnVsbCBvdXRlciBqb2luIGlzIHNpbXBseSB0aGUgdW5pb24gb2YgYSBsZWZ0IG91dGVyIGpvaW4gYW5kIGEgcmlnaHQgb3V0ZXIgam9pbi4gSXQgc2hvd3MgYWxsIG1hdGNoaW5nIHJvd3MgKCppLmUuKiwgYW4gaW5uZXIgam9pbiksIHBsdXMgdW5tYXRjaGVkIHJvd3MgZnJvbSB0aGUgcmlnaHQgYSBsZWZ0IHRhYmxlLiBTUUxpdGUgZG9lcyBub3Qgc3VwcG9ydCBhIGZ1bGwgb3V0ZXIgam9pbiBkaXJlY3RseS4gSG93ZXZlciwgaXQgaXMgc2ltcGx5IHRoZSB1bmlvbiBvZiBhIGxlZnQgYW5kIHJpZ2h0IG91dGVyIGpvaW4uCgpCdXQgZm9yIHRoaXMgdG8gd29yayB5b3UgbmVlZCB0byBzcGVjaWZ5IHRoZSBvcmRlciBvZiB0aGUgY29sdW1uczsgdXNpbmcgKlwqKiBkb2VzIG5vdCB3b3JrIGFzIHRoZSB0d28gcXVlcmllcyByZXR1cm4gdGhlIGNvbHVtbnMgaW4gYSBkaWZmZXJlbnQgb3JkZXIgYW5kIHRoZSBkYXRhIGlzICptYW5nbGVkKiAtLSBpdCBkb2VzIG5vdCByZXR1cm4gYW4gZXJyb3IsIGl0IGp1c3QgZG9lc24ndCByZXR1cm4gYSBtZWFuaW5nZnVsIHJlc3VsdC4gTm90ZSBob3cgdGhlIGZpcnN0IGNvbHVtbiBsaXN0cyBib3RoIG9mZmljZSBuYW1lcyBhbmQgZW1wbG95ZWUgbmFtZXMgLS0gd2hpY2ggaXMgbm9uc2Vuc2UuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCAqCiAgZnJvbSBvZmZpY2UgbyBsZWZ0IGpvaW4gZW1wbCBlIG9uIChlLm9pZCA9IG8ub2lkKQp1bmlvbgpzZWxlY3QgKgogIGZyb20gZW1wbCBlIGxlZnQgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCkKYGBgCgpIZXJlIGlzIHRoZSBjb3JyZWN0ZWQgdmVyc2lvbiB0aGF0IGV4cGxpY2l0bHkgc3BlY2lmaWVzIHRoZSBjb2x1bW4gb3JkZXI6CgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CnNlbGVjdCBlLm5hbWUsIG8ubnVtCiAgZnJvbSBvZmZpY2UgbyBsZWZ0IGpvaW4gZW1wbCBlIG9uIChlLm9pZCA9IG8ub2lkKQp1bmlvbgpzZWxlY3QgZS5uYW1lLCBvLm51bQogIGZyb20gZW1wbCBlIGxlZnQgam9pbiBvZmZpY2UgbyBvbiAoZS5vaWQgPSBvLm9pZCkKYGBgCgojIyMgQW50aS1Kb2luCgpJbiBhbiBhbnRpLWpvaW4gd2Ugd2FudCB0byBmaW5kIGFsbCByb3dzIHRoYXQgYXJlIGluIG9uZSB0YWJsZSBidXQgbm90IHRoZSBvdGhlci4gRm9yIGV4YW1wbGUsIHdlIG1pZ2h0IHdhbnQgdG8gZmluZCBhbGwgb2ZmaWNlIHdoaWNoIGFyZSBub3Qgb2NjdXBpZWQsICppLmUuKiwgaW4gb3VyIHNjaGVtYSBmcm9tIHRoaXMgdHV0b3JpYWwsIGFsbCBvZmZpY2VzIGluIHRoZSAqb2ZmaWNlKiB0YWJsZSB3aGljaCBhcmUgbm90IGxpbmtlZCB0byBmcm9tIHRoZSAqZW1wbCogdGFibGUuCgpUbyBmaW5kIGFsbCB0aGUgcm93cyB3aGljaCBhcmUgaW4gKm9mZmljZSogdGhhdCBhcmUgbm90IGluICplbXBsKiB3ZSBmaXJzdCBkbyBhIGxlZnQgam9pbiBvbiB0aGUgdGFibGVzIGFuZCB0aGVuIGZpbHRlciBvdXQgdGhvc2Ugd2hlcmUgdGhlIEZLIGlzIE5VTEwuCgpgYGB7c3FsIGFudGktam9pbiwgY29ubmVjdGlvbj1kYmNvbn0Kc2VsZWN0IG8ub2lkLCBvLm5hbWUsIG8uY2lkIAogIGZyb20gb2ZmaWNlIGFzIG8KICBsZWZ0IGpvaW4gZW1wbCBhcyBlIG9uIChvLm9pZCA9IGUub2lkKQogd2hlcmUgZS5vaWQgaXMgbnVsbDsKYGBgCgojIyBTZWxmIEpvaW5zCgpTZWxmIGpvaW5zIChvciwgcmVmbGV4aXZlIGpvaW5zKSBpbiBTUUwgYXJlIGEgdW5pcXVlIHR5cGUgb2Ygam9pbiB3aGVyZSBhIHRhYmxlIGlzIGpvaW5lZCB3aXRoIGl0c2VsZi4gVGhpcyBtaWdodCBzb3VuZCBhIGJpdCB1bnVzdWFsIGF0IGZpcnN0LCBidXQgc2VsZiBqb2lucyBhcmUgcXVpdGUgdXNlZnVsIGluIGNlcnRhaW4gc2NlbmFyaW9zLgoKMS4gICoqU2FtZSBUYWJsZSwgRGlmZmVyZW50IEFsaWFzZXMqKjogSW4gYSBzZWxmIGpvaW4sIHRoZSBzYW1lIHRhYmxlIGFwcGVhcnMgdHdpY2UgaW4gdGhlIHF1ZXJ5LCBidXQgaXQncyByZXByZXNlbnRlZCBieSBkaWZmZXJlbnQgYWxpYXNlcy4gVGhpcyBhbGxvd3MgeW91IHRvIGNvbXBhcmUgcm93cyB3aXRoaW4gdGhlIHNhbWUgdGFibGUuCgoyLiAgKipTeW50YXgqKjogSXQgZm9sbG93cyB0aGUgc2FtZSBzeW50YXggYXMgYSByZWd1bGFyIGpvaW4uIFRoZSBkaWZmZXJlbmNlIGxpZXMgaW4gdGhhdCBib3RoIHRoZSBsZWZ0IGFuZCByaWdodCBzaWRlIG9mIHRoZSBqb2luIGFyZSB0aGUgc2FtZSB0YWJsZSwganVzdCB3aXRoIGRpZmZlcmVudCBhbGlhc2VzLgoKICAgIGBgYCBzcWwKICAgIFNFTEVDVCBBLmNvbHVtbl9uYW1lLCBCLmNvbHVtbl9uYW1lCiAgICBGUk9NIHRhYmxlX25hbWUgQVMgQQogICAgSk9JTiB0YWJsZV9uYW1lIEFTIEIKICAgIE9OIEEuY29tbW9uX2ZpZWxkID0gQi5jb21tb25fZmllbGQ7CiAgICBgYGAKCjMuICAqKlR5cGVzIG9mIEpvaW5zKio6IFlvdSBjYW4gdXNlIGFueSB0eXBlIG9mIGpvaW4gKElOTkVSLCBMRUZULCBSSUdIVCwgRlVMTCkgYXMgYSBzZWxmIGpvaW4sIGRlcGVuZGluZyBvbiB0aGUgcmVxdWlyZW1lbnQuCgojIyMgVXNlIENhc2VzIGZvciBTZWxmIEpvaW5zCgoxLiAgKipIaWVyYXJjaGljYWwgRGF0YSoqOiBVc2VmdWwgaW4gc2NlbmFyaW9zIHdoZXJlIHRoZSB0YWJsZSBoYXMgYSBoaWVyYXJjaGljYWwgc3RydWN0dXJlLiBGb3IgZXhhbXBsZSwgYW4gZW1wbG95ZWUgdGFibGUgd2hlcmUgZWFjaCBlbXBsb3llZSBoYXMgYSBtYW5hZ2VyLCBhbmQgYm90aCBlbXBsb3llZXMgYW5kIG1hbmFnZXJzIGFyZSBpbiB0aGUgc2FtZSB0YWJsZS4KCjIuICAqKkNvbXBhcmlzb25zIHdpdGhpbiBhIFRhYmxlKio6IFRvIGNvbXBhcmUgcm93cyB3aXRoaW4gdGhlIHNhbWUgdGFibGUuIEZvciBpbnN0YW5jZSwgZmluZGluZyBwYWlycyBvZiBjdXN0b21lcnMgd2hvIGxpdmUgaW4gdGhlIHNhbWUgY2l0eS4KCjMuICAqKlRpbWUtYmFzZWQgQW5hbHlzaXMqKjogSW4gdGFibGVzIHdoZXJlIHlvdSBoYXZlIHRpbWUtc2VyaWVzIGRhdGEsIHlvdSBtaWdodCB3YW50IHRvIGNvbXBhcmUgYSByb3cgd2l0aCBpdHMgcHJlY2VkaW5nIG9yIHN1Y2NlZWRpbmcgcm93LgoKNC4gICoqRGV0ZWN0aW5nIER1cGxpY2F0ZXMqKjogVG8gZmluZCBkdXBsaWNhdGUgZW50cmllcyBiYXNlZCBvbiBjZXJ0YWluIGNyaXRlcmlhIHdpdGhvdXQgdXNpbmcgR1JPVVAgQlkuCgo1LiAgKipQYXRoIEZpbmRpbmcqKjogSW4gbmV0d29yayBvciBncmFwaC1iYXNlZCBkYXRhLCB0byBmaW5kIHBhdGhzIG9yIGNvbm5lY3Rpb25zIGJldHdlZW4gbm9kZXMgc3RvcmVkIGluIGEgc2luZ2xlIHRhYmxlLgoKIyMjIEV4YW1wbGUKCkltYWdpbmUgYW4gYEVtcGxveWVlc2AgdGFibGUgd2l0aCBjb2x1bW5zIGBFbXBsb3llZUlEYCwgYE5hbWVgLCBhbmQgYE1hbmFnZXJJRGAsIHdoZXJlIGBNYW5hZ2VySURgIGlzIGFsc28gYW4gYEVtcGxveWVlSURgIGluIHRoZSBzYW1lIHRhYmxlLiBUbyBsaXN0IGVhY2ggZW1wbG95ZWUgd2l0aCB0aGVpciBtYW5hZ2VyJ3MgbmFtZSwgeW91J2QgdXNlIGEgc2VsZiBqb2luOgoKYGBgIHNxbApTRUxFQ1QgRTEuTmFtZSBBUyBFbXBsb3llZSwgRTIuTmFtZSBBUyBNYW5hZ2VyCkZST00gRW1wbG95ZWVzIEFTIEUxCkpPSU4gRW1wbG95ZWVzIEFTIEUyIE9OIEUxLk1hbmFnZXJJRCA9IEUyLkVtcGxveWVlSUQ7CmBgYAoKSW4gdGhpcyBxdWVyeSwgYEUxYCBhbmQgYEUyYCBhcmUgYWxpYXNlcyBmb3IgdGhlIHNhbWUgYEVtcGxveWVlc2AgdGFibGUuIGBFMWAgcmVwcmVzZW50cyB0aGUgZW1wbG95ZWVzLCBhbmQgYEUyYCByZXByZXNlbnRzIHRoZWlyIG1hbmFnZXJzLgoKIyMjIERlbW9uc3RyYXRpb24KCkluIHRoaXMgYWR2YW5jZWQgY29kZSB3YWxrIGFuZCBkZW1vbnN0cmF0aW9uLCBLaG91cnkgQm9zdG9uJ3MgRHIuIERhbiBGZWluYmVyZyBwcm92aWRlcyB1c2UgY2FzZXMgZm9yIHNlbGYgam9pbnMgYW5kIHNob3dzIGhvdyB0byBidWlsZCB0aGVtIGluIE15U1FMLCBhbHRob3VnaCB0aGUgc2FtZSBzeW50YXggYW5kIHByaW5jaXBsZXMgYXBwbHkgdG8gbW9zdCBvdGhlciBkYXRhYmFzZXMsIGluY2x1ZGluZyBTUUxpdGUuCgo8aWZyYW1lIHN0eWxlPSJib3JkZXI6IDFweCBzb2xpZCAjNDY0NjQ2OyIgc3JjPSJodHRwczovL25vcnRoZWFzdGVybi5ob3N0ZWQucGFub3B0by5jb20vUGFub3B0by9QYWdlcy9FbWJlZC5hc3B4P2lkPWFhN2QwYzZiLTc3YjUtNDM2MC1hNzMwLWFjNTIwMTA1ZWQxOSZhbXA7YXV0b3BsYXk9ZmFsc2UmYW1wO29mZmVydmlld2VyPXRydWUmYW1wO3Nob3d0aXRsZT1mYWxzZSZhbXA7c2hvd2JyYW5kPWZhbHNlJmFtcDtzdGFydD0wJmFtcDtpbnRlcmFjdGl2aXR5PWFsbCIgd2lkdGg9IjQ4MCIgaGVpZ2h0PSIyNzAiIGFsbG93ZnVsbHNjcmVlbj0iYWxsb3dmdWxsc2NyZWVuIiBhbGxvdz0iYXV0b3BsYXkiIGRhdGEtZXh0ZXJuYWw9IjEiPgoKPC9pZnJhbWU+CgojIyMgQ29uY2x1c2lvbgoKU2VsZiBqb2lucyBhcmUgYSBwb3dlcmZ1bCB0b29sIGluIFNRTCwgZXNwZWNpYWxseSBmb3IgaGFuZGxpbmcgaGllcmFyY2hpY2FsIGRhdGEsIGNvbXBsZXggY29tcGFyaXNvbnMsIGFuZCBkYXRhIGFuYWx5c2lzIHdpdGhpbiB0aGUgc2FtZSB0YWJsZS4gVW5kZXJzdGFuZGluZyB3aGVuIGFuZCBob3cgdG8gdXNlIHRoZW0gY2FuIGdyZWF0bHkgZW5oYW5jZSB0aGUgZWZmaWNpZW5jeSBhbmQgY2FwYWJpbGl0eSBvZiB5b3VyIFNRTCBxdWVyaWVzLgoKIyMgU3VtbWFyeQoKVGhpcyB0dXRvcmlhbCBleHBsYWluZWQgdGhlIHNldmVyYWwgY29tbW9uIHR5cGVzIG9mIGpvaW5zOiBpbm5lciwgbmF0dXJhbCwgb3V0ZXIsIGFudGksIGFuZCByZWZsZXhpdmUuIEl0IGRpZCBvbWl0IHR3byB0eXBlcyBvZiAodW5jb21tb24pIGpvaW5zIHRob3VnaDogdGhlIGNyb3NzIGpvaW4gYW5kIHRoZSB0aGV0YSBqb2luLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKTm8gcmVmZXJlbmNlcy4KCiMjIEVycmF0YQoKW0xldCB1cyBrbm93XShodHRwczovL2Zvcm0uam90Zm9ybS5jb20vMjEyMTg3MDcyNzg0MTU3KXt0YXJnZXQ9Il9ibGFuayJ9LgoK