1 Introducion

To run SQL clauses in R, we need to use several R libraries (installed and loaded in the above R setup code chunk). There are different ways to run SQL queries in R. We only introduce one of them that runs the basic SQL code.

1.1 Connect R to Existing Database

If there is an existing database, the following code connects R to the database.

con <- DBI::dbConnect(drv = odbc::odbc(),
                      Driver = "driver_name",
                      Server = "server_url",
                      Database = "database_name",
                      user = "user", #optional
                      password = "password") #optional

1.2 Create A Database to Run SQL Queries in R

This short note shows the three basic steps to run SQL in R using R Markdown starting with a set of relational tables.

  1. Load relational data tables as usual to R.

  2. Create a SQLite (relational) database that contains these relational tables.

  3. Create an R code chunk and connect to the created database using Chunk options.

2 Create SQLite Database with R

If modeling requires a data set that contains information from multiple relational data tables, we need to perform data management to aggregate the required information from different data tables. We can load the different data sets in different formats using appropriate R functions.

As an example, We use three ecological survey data sets to create a database.

#Load the sample data
plots <- read.csv("https://pengdsci.github.io/datasets/AnimalSurvey/plots.csv")
species <- read.csv("https://pengdsci.github.io/datasets/AnimalSurvey/species.csv")
surveys <- read.csv("https://pengdsci.github.io/datasets/AnimalSurvey/surveys.csv")

We next explore the relationship between the tables.

summary(plots)
    plot_id       plot_type        
 Min.   : 1.00   Length:24         
 1st Qu.: 6.75   Class :character  
 Median :12.50   Mode  :character  
 Mean   :12.50                     
 3rd Qu.:18.25                     
 Max.   :24.00                     
summary(species)
  species_id           genus             species              taxa          
 Length:54          Length:54          Length:54          Length:54         
 Class :character   Class :character   Class :character   Class :character  
 Mode  :character   Mode  :character   Mode  :character   Mode  :character  
summary(surveys)
       X           record_id         month             day       
 Min.   :    1   Min.   :    1   Min.   : 1.000   Min.   : 1.00  
 1st Qu.: 8888   1st Qu.: 8888   1st Qu.: 4.000   1st Qu.: 9.00  
 Median :17775   Median :17775   Median : 6.000   Median :16.00  
 Mean   :17775   Mean   :17775   Mean   : 6.478   Mean   :15.99  
 3rd Qu.:26662   3rd Qu.:26662   3rd Qu.:10.000   3rd Qu.:23.00  
 Max.   :35549   Max.   :35549   Max.   :12.000   Max.   :31.00  
                                                                 
      year         plot_id      species_id            sex           
 Min.   :1977   Min.   : 1.0   Length:35549       Length:35549      
 1st Qu.:1984   1st Qu.: 5.0   Class :character   Class :character  
 Median :1990   Median :11.0   Mode  :character   Mode  :character  
 Mean   :1990   Mean   :11.4                                        
 3rd Qu.:1997   3rd Qu.:17.0                                        
 Max.   :2002   Max.   :24.0                                        
                                                                    
 hindfoot_length     weight      
 Min.   : 2.00   Min.   :  4.00  
 1st Qu.:21.00   1st Qu.: 20.00  
 Median :32.00   Median : 37.00  
 Mean   :29.29   Mean   : 42.67  
 3rd Qu.:36.00   3rd Qu.: 48.00  
 Max.   :70.00   Max.   :280.00  
 NA's   :4111    NA's   :3266    

The relational table surveys has attributes species_id and plot_id that connect relational tables plots and species. The primary and foreign keys are depicted in the following figure.

include_graphics("img/RelationshipBetweenTables.png")
The primary and foreign keys of the three relational tables.

The primary and foreign keys of the three relational tables.


Next, we create a SQLit database using R libraries and then remove the data frames from the working directory.

#Create database
con <- dbConnect(drv = SQLite(),
                 dbname = ":memory:")

#store sample data in the database
dbWriteTable(conn = con, 
             name = "plots",
             value = plots)

dbWriteTable(conn = con, 
             name = "species",
             value = species)

dbWriteTable(conn = con, 
             name = "surveys",
             value = surveys)
 
#remove the local data from the environment
rm(plots, species, surveys)

We can use the table view function tbl() to view the relational data tables in the database.

tbl(src = con,         #  the source if the database connection profile
    c("surveys"))      #  the name of the table to preview
# Source:   table<surveys> [?? x 10]
# Database: sqlite 3.45.2 [:memory:]
       X record_id month   day  year plot_id species_id sex   hindfoot_length
   <int>     <int> <int> <int> <int>   <int> <chr>      <chr>           <int>
 1     1         1     7    16  1977       2 NL         M                  32
 2     2         2     7    16  1977       3 NL         M                  33
 3     3         3     7    16  1977       2 DM         F                  37
 4     4         4     7    16  1977       7 DM         M                  36
 5     5         5     7    16  1977       3 DM         M                  35
 6     6         6     7    16  1977       1 PF         M                  14
 7     7         7     7    16  1977       2 PE         F                  NA
 8     8         8     7    16  1977       1 DM         M                  37
 9     9         9     7    16  1977       1 DM         F                  34
10    10        10     7    16  1977       6 PF         F                  20
# ℹ more rows
# ℹ 1 more variable: weight <int>
tbl(src = con, "species")
# Source:   table<species> [?? x 4]
# Database: sqlite 3.45.2 [:memory:]
   species_id genus            species         taxa   
   <chr>      <chr>            <chr>           <chr>  
 1 AB         Amphispiza       bilineata       Bird   
 2 AH         Ammospermophilus harrisi         Rodent 
 3 AS         Ammodramus       savannarum      Bird   
 4 BA         Baiomys          taylori         Rodent 
 5 CB         Campylorhynchus  brunneicapillus Bird   
 6 CM         Calamospiza      melanocorys     Bird   
 7 CQ         Callipepla       squamata        Bird   
 8 CS         Crotalus         scutalatus      Reptile
 9 CT         Cnemidophorus    tigris          Reptile
10 CU         Cnemidophorus    uniparens       Reptile
# ℹ more rows
tbl(src = con, "plots")
# Source:   table<plots> [?? x 2]
# Database: sqlite 3.45.2 [:memory:]
   plot_id plot_type                
     <int> <chr>                    
 1       1 Spectab exclosure        
 2       2 Control                  
 3       3 Long-term Krat Exclosure 
 4       4 Control                  
 5       5 Rodent Exclosure         
 6       6 Short-term Krat Exclosure
 7       7 Rodent Exclosure         
 8       8 Control                  
 9       9 Spectab exclosure        
10      10 Rodent Exclosure         
# ℹ more rows

3 Running SQL Queries in R

To use SQL in RMarkdown, we need the following chunk options:

  • sql
  • connection = “database-name”
  • output.var = “output-dataset-name”

If we create a data view only, we simply ignore option output.var =

Following are few examples of SQL queries based on the animal survey data tables in the database.

3.1 Basic SQL Operators

While working with databases, we use SQL queries to manipulate the data and retrieve the desired result. This manipulation of data is achieved through various SQL operators. An operator is a keyword in SQL that helps us access the data and returns the result based on the operator’s functionality. SQL provides us with many such operators to ease the process of data manipulation. In this subsection, we will look at three major types of operators in SQL.

Arithmetic Operators

Arithmetic operators are used to perform arithmetic operations such as addition, subtraction, division, and multiplication. These operators usually accept numeric operands.

Operator Operation Description
+ Addition Adds operands on either side of the operator
- Subtraction Subtracts the right-hand operand from the left-hand operand
* Multiplication Multiplies the values on each side
/ Division Divides left-hand operand by right-hand operand
% Modulus Divides left-hand operand by right-hand operand and returns the remainder


Comparison Operators

Comparison operators in SQL are used to check the equality of two expressions. It checks whether one expression is identical to another. Comparison operators are generally used in the WHERE clause of a SQL query. The result of a comparison operation may be TRUE, FALSE, or UNKNOWN. When one or both the expression is NULL, then the operator returns UNKNOWN.

Operator Operation Description
= Equal to Checks if both operands have equal value, if yes, then returns TRUE
> Greater than Checks if the value of the left-hand operand is greater than the right-hand operand or not
< Less than Returns TRUE if the value of the left-hand operand is less than the value of the right-hand operand
>= Greater than or equal to It checks if the value of the left-hand operand is greater than or equal to the value of the right-hand operand, if yes, then returns TRUE
<= Less than or equal to Examines if the value of the left-hand operator is less than or equal to the right-hand operand
<> or != Not equal to Checks if values on either side of the operator are equal or not. Returns TRUE if values are not equal


Logical Operators

Logical operators are those operators that take two expressions as operands and return TRUE or False as output.

Operator Description
ALL Compares a value to all other values in a set
AND Returns the records if all the conditions separated by AND are TRUE
ANY Compares a specific value to any other values in a set
SOME Compares a value to each value in a set. It is similar to ANY operator
LIKE It returns the rows for which the operand matches a specific pattern
IN Used to compare a value to a specified value in a list
BETWEEN Returns the rows for which the value lies between the mentioned range
NOT Used to reverse the output of any logical operator
EXISTS Used to search a row in a specified table in the database
OR Returns the records for which any of the conditions separated by OR is true
NULL Returns the rows where the operand is NULL

The logical and comparison operators are usually used with conditional queries in which we can specify a conditional expression in a SELECT statement WHERE clause which specifies that only those rows for which the conditional expression is true are to be retrieved. The syntax for the SELECT statement containing the WHERE clause is as follows.

select_statement:
        SELECT … FROM table_name
            WHERE conditional_expression

The following is a simple example. We want to create a subset such that species_id equals “DM” and weight > 0.

SELECT *
FROM surveys 
WHERE species_id LIKE "DM" AND weight > 0;

3.2 Subsetting and Duplicating Data

  1. Extract year, month, and day from surveys table
SELECT 
  surveys.year, surveys.month, surveys.Day
FROM 
 surveys   -- pointer is not needed since it is in the database 
WHERE
  surveys.species_id IN ('NL', 'DM') AND
  surveys.sex = 'M'

The code chunk defined the output of the query as an R data frame (Caution: not a relational table stored in the SQLite database) with the name YMD. We check the first few records from the data frame

head(YMD)
  year month day
1 1977     7  16
2 1977     7  16
3 1977     7  16
4 1977     7  16
5 1977     7  16
6 1977     7  16

If we simply create a data view without saving it as an R data frame, we simply ignore the option output.var="YMD" in the SQL code chunk.

SELECT 
  surveys.year, surveys.month, surveys.Day
FROM 
 surveys  -- pointer is not needed since it is in the database 
WHERE
  surveys.species_id IN ('NL', 'DM') AND
  surveys.sex = 'M'
Displaying records 1 - 10
year month day
1977 7 16
1977 7 16
1977 7 16
1977 7 16
1977 7 16
1977 7 16
1977 7 16
1977 7 17
1977 7 17
1977 7 17
  1. Duplicate data and rename it
SELECT 
  surveys.*
FROM 
 surveys

Note that surveys is a relation data table in the SQLite database and duplicated data is not in the SQLite database but an R data frame in the working directory.

Caution: in the SQL code chunk, query statements only work with SQL database. They don’t work for R data frames in the working directory. The following code doesn’t work because the data set SurveyCopy is not in the SQLite database.

SELECT 
  SurveyCopy.*
FROM 
 SurveyCopy

In other words, if we want to query the data table (data frame) SurveyCopy, we need to add it to the database con defined earlier.

# Store sample data in the database
dbWriteTable(conn = con, 
             name = "SurveyCopy",
             value = SurveyCopy)
## Remove SurveyCopy in the working directory
rm(SurveyCopy)

We can use tbl() to view the newly added table in the SQLite database con.

tbl(src = con, c("SurveyCopy"))
# Source:   table<SurveyCopy> [?? x 10]
# Database: sqlite 3.45.2 [:memory:]
       X record_id month   day  year plot_id species_id sex   hindfoot_length
   <int>     <int> <int> <int> <int>   <int> <chr>      <chr>           <int>
 1     1         1     7    16  1977       2 NL         M                  32
 2     2         2     7    16  1977       3 NL         M                  33
 3     3         3     7    16  1977       2 DM         F                  37
 4     4         4     7    16  1977       7 DM         M                  36
 5     5         5     7    16  1977       3 DM         M                  35
 6     6         6     7    16  1977       1 PF         M                  14
 7     7         7     7    16  1977       2 PE         F                  NA
 8     8         8     7    16  1977       1 DM         M                  37
 9     9         9     7    16  1977       1 DM         F                  34
10    10        10     7    16  1977       6 PF         F                  20
# ℹ more rows
# ℹ 1 more variable: weight <int>

In R, we can use dbListTables() to view relational tables in the database.

dbListTables(con)
[1] "SurveyCopy" "plots"      "species"    "surveys"   

We now can query the relational table SurveyCopy in the database con using SQL clause as usual.

  1. Create a table view (i.e., no data set will be created and saved)
SELECT 
  surveys.year, surveys.month, surveys.Day
FROM 
 surveys
WHERE
  surveys.species_id = 'NL' AND
  surveys.sex = 'M'

3.3 Define A New Variable

  1. Define a new variable with simple arithmetic operations
SELECT 
    surveys.plot_id, 
    surveys.species_id, 
    surveys.sex, 
    surveys.weight, 
    surveys.weight/100 AS wt_kilo  -- should not the pointer in front of 
                                   -- the name of the new variable 
FROM 
   surveys
  1. Define new variables using string functions in SQL
SELECT surveys.*, 
       surveys.species_id||'-'||surveys.sex AS newKey
FROM surveys
  1. Define new variables with aggregated information
SELECT surveys.species_id, 
       COUNT(surveys.species_id) AS species_ctr
FROM surveys
GROUP BY surveys.species_id
HAVING species_ctr > 10

3.4 Sorting Variables

  1. Sort data based on the summarized statistics of a variable

Summary functions are restricted to the SELECT and HAVING clauses only;

SELECT surveys.species_id
FROM surveys
GROUP BY surveys.species_id
ORDER BY COUNT(surveys.species_id);
  1. Sort data based on a new variable defined using summarized statistics of a variable.
/* create a table view*/
SELECT surveys.species_id AS subtotal, 
       COUNT(*) 
FROM surveys
GROUP BY surveys.species_id
ORDER BY subtotal;

4 SQL Joins

SQL JOIN clause is used to query and access data from multiple tables by establishing logical relationships between them. It can access data from multiple tables simultaneously using common key values shared across different tables. This section briefly introduces commonly used join operations to merge tables using the common key(s). In each of the major join operations, we use a visual illustration followed by an example.

  1. Inner Join

Inner joins combine records from two tables whenever there are matching values in a field common to both tables.

include_graphics("img/inner-join.png")
Illustration of inner join.

Illustration of inner join.

SELECT *
FROM surveys AS A
INNER JOIN species AS B   -- by default, JOIN means INNER JOIN
ON A.species_id = B.species_id;
  1. Left Join

Left Join or Left Outer Join in SQL combines two or more tables, where the first table is returned wholly; but, only the matching record(s) are retrieved from the consequent tables. If zero (0) records are matched in the consequent tables, the join will still return a row in the result, but with NULL in each column from the right table.

include_graphics("img/left-join.png")
Illustration of left join.

Illustration of left join.

SELECT *
FROM surveys AS A
LEFT JOIN species AS B
ON A.species_id = B.species_id;
  1. Right Join

The Right Join query in SQL returns all rows from the right table, even if there are no matches in the left table. In short, a right join returns all the values from the right table, plus matched values from the left table or NULL in case of no matching join predicate.

include_graphics("img/right-join.png")
Illustration of right join.

Illustration of right join.

SELECT *
FROM surveys AS A
RIGHT JOIN species AS B
ON A.species_id = B.species_id;
  1. Full Join

SQL Full Join creates a new table by joining two tables as a whole. The joined table contains all records from both tables and fills NULL values for missing matches on either side. In short, full join is a type of outer join that combines the resulting sets of both left and right joins.

include_graphics("img/full-join.png")
Illustration of full join.

Illustration of full join.

SELECT *
FROM surveys AS A
FULL JOIN species AS B
ON A.species_id = B.species_id;

5 SQL Aggregation Functions

SQL is a good starting point for high-level data analysis. Many data analysis packages and languages have their own interface to read data from different SQL-based database systems. In fact, any real-life data analysis starts from an RDBMS, and the basic analysis and report generation is done on the SQL platform of that RDBMS itself. A good preview of data within the RDBMS platform itself helps analysts to get a fast high-level analysis in a different platform. The most commonly used aggregation functions include MAX(), MIN(), AVG(),SUM() and COUNT(). The following are basic examples that involve some of these aggregation functions.

  1. Average
SELECT A.species_id, 
       A.sex, 
       AVG(A.weight) as mean_wgt  
FROM surveys AS A
JOIN species AS B
ON A.species_id = B.species_id 
WHERE taxa = 'Rodent' AND A.sex IS NOT NULL 
GROUP BY A.species_id, A.sex;   -- sorted by two variables 
  1. Sample size
SELECT COUNT(*) 
FROM surveys

6 Subqueries

Subqueries (also known as inner queries or nested queries) are a tool for performing operations in multiple steps. For example, if you wanted to take the sums of several columns, and then average all of those values, you’d need to do each aggregation in a distinct step. Subqueries can be used in several places within a query.

6.1 Subquery in SELECT Clause

In the following example, we want to define a new attribute, the relative percentage of taxonomic groups in the data set in the relational table surveys. The feature taxa is not in the surveys table. We need to join tables surveys and species to obtain the distribution of taxa in the surveys table. To this end, we need to know the frequency of each taxonomic group and the size of the relational table surveys.

We could use the following queries to find the sample size and the sizes of each taxon group.

  1. Total Sample Size
SELECT COUNT(*) FROM surveys
  1. Calculating Taxon Group Frequencies

The group totals are calculated in the following code.

SELECT B.taxa, 
       COUNT(*)
FROM surveys AS A  -- A is an alias of `surveys` table
INNER JOIN species AS B -- inner join
ON A.species_id = B.species_id 
-- this finds the group frequencies
GROUP BY taxa; 

This is not efficient. We can use the following nested query to efficiently find the relative frequency table.

SELECT B.taxa, 
       100.0*COUNT(*)/(SELECT COUNT(*) FROM surveys)  AS Percentage
FROM surveys AS A
JOIN species AS B
ON A.species_id = B.species_id 
GROUP BY taxa;

The above query produces the following relative frequency table.

kable(subSQLinSELECT)
taxa Percentage
Bird 1.2658584
Rabbit 0.2109764
Reptile 0.0393823
Rodent 96.3374497

6.2 Subquery in FROM Clause

As an example, we create a subset with features record_id, year, plot_id, species_id, sex, hindfoot_length, weight with the condition that species_id = "DM" and sex = "M". The following query that contains a sub-query can do the trick.

SELECT sub_survey.record_id,
       sub_survey.year,
       sub_survey.plot_id,
       sub_survey.species_id,
       sub_survey.sex,
       sub_survey.hindfoot_length,
       sub_survey.weight
  FROM (
        SELECT *
         FROM surveys
         WHERE species_id = 'DM'
       ) sub_survey  -- the name of a relational table defined by the subquery
 WHERE sub_survey.sex = 'M'

The above subset can also be generated using the following simple query.

SELECT record_id,
       year,
       plot_id,
       species_id,
       sex,
       hindfoot_length,
       weight
  FROM surveys
 WHERE species_id = 'DM' AND sex = 'M';

6.3 Subquery in WHERE Clause

This example shows the way to create a subset of surveys record_id, plot_id, species_id, year, sex, hindfoot_length, weight for the earliest year.

SELECT record_id,
       plot_id,
       species_id,
       year,
       sex,
       hindfoot_length,
       weight
  FROM surveys
 WHERE year = (SELECT MIN(year)
                 FROM surveys
              )

6.4 Subquery in JOIN Clause

In the following example, we use the subquery to find the frequency count in each species group from the surveys table and add the frequency to the species table.

SELECT *
  FROM species
  JOIN ( SELECT COUNT(species_id) AS speciesfreq,
                species_id
           FROM surveys
           GROUP BY species_id
       ) subq
    ON species.species_id = subq.species_id
 ORDER BY subq.species_id DESC 

The resulting table is shown in the following.

kable(subSQLinJOIN)
species_id genus species taxa speciesfreq species_id
ZL Zonotrichia leucophrys Bird 2 ZL
US Sparrow sp. Bird 4 US
UR Rodent sp. Rodent 10 UR
UP Pipilo sp. Bird 8 UP
UL Lizard sp. Reptile 4 UL
SU Sceloporus undulatus Reptile 5 SU
ST Spermophilus tereticaudus Rodent 1 ST
SS Spermophilus spilosoma Rodent 248 SS
SO Sigmodon ochrognathus Rodent 43 SO
SH Sigmodon hispidus Rodent 147 SH
SF Sigmodon fulviventer Rodent 43 SF
SC Sceloporus clarki Reptile 1 SC
SA Sylvilagus audubonii Rabbit 75 SA
RX Reithrodontomys sp. Rodent 2 RX
RO Reithrodontomys montanus Rodent 8 RO
RM Reithrodontomys megalotis Rodent 2609 RM
RF Reithrodontomys fulvescens Rodent 75 RF
PX Chaetodipus sp. Rodent 6 PX
PU Pipilo fuscus Bird 5 PU
PP Chaetodipus penicillatus Rodent 3123 PP
PM Peromyscus maniculatus Rodent 899 PM
PL Peromyscus leucopus Rodent 36 PL
PI Chaetodipus intermedius Rodent 9 PI
PH Perognathus hispidus Rodent 32 PH
PG Pooecetes gramineus Bird 8 PG
PF Perognathus flavus Rodent 1597 PF
PE Peromyscus eremicus Rodent 1299 PE
PC Pipilo chlorurus Bird 39 PC
PB Chaetodipus baileyi Rodent 2891 PB
OX Onychomys sp. Rodent 12 OX
OT Onychomys torridus Rodent 2249 OT
OL Onychomys leucogaster Rodent 1006 OL
NL Neotoma albigula Rodent 1252 NL
DX Dipodomys sp. Rodent 40 DX
DS Dipodomys spectabilis Rodent 2504 DS
DO Dipodomys ordii Rodent 3027 DO
DM Dipodomys merriami Rodent 10596 DM
CV Crotalus viridis Reptile 1 CV
CU Cnemidophorus uniparens Reptile 1 CU
CT Cnemidophorus tigris Reptile 1 CT
CS Crotalus scutalatus Reptile 1 CS
CQ Callipepla squamata Bird 16 CQ
CM Calamospiza melanocorys Bird 13 CM
CB Campylorhynchus brunneicapillus Bird 50 CB
BA Baiomys taylori Rodent 46 BA
AS Ammodramus savannarum Bird 2 AS
AH Ammospermophilus harrisi Rodent 437 AH
AB Amphispiza bilineata Bird 303 AB

7 Concluding Remarks

We have introduced the basic SQL clauses, the basic join operations, and the sub-queries in this note. But it is by no means considered a complete tutorial for SQL programming. This note intends to help you get started with the basic SQL coding outside a DBMS and expand your technical Vocabulary so you can talk confidently with data professionals in the future.

Since we don’t have DBMS to practice SQL, some of the advanced SQL tasks such as control flow, user-defined SQL functions, etc. can not be performed using PROC SQL in SAS and R libraries. You can learn these advanced techniques quickly once you are comfortable with the basic concepts covered in this note.

LS0tDQp0aXRsZTogIlJ1bm5pbmcgU1FMIGluIFIiDQphdXRob3I6ICJDaGVuZyBQZW5nIg0KZGF0ZTogIiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jX2NvbGxhcHNlZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgc21vb3RoX3Njcm9sbDogeWVzDQogICAgdGhlbWU6IGx1bWVuDQogIHdvcmRfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIGtlZXBfbWQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgZmlnX2NhcHRpb246IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyNHB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDIwcHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogIGZvbnQtZmFtaWx5OiBzeXN0ZW0tdWk7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIwcHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxNnB4Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE0cHg7DQogICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBEZXRlY3QsIGluc3RhbGwgYW5kIGxvYWQgcGFja2FnZXMgaWYgbmVlZGVkLg0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJvZGJjIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIm9kYmMiKQ0KICAgbGlicmFyeShvZGJjKQ0KfQ0KaWYgKCFyZXF1aXJlKCJEQkkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiREJJIikNCiAgIGxpYnJhcnkoREJJKQ0KfQ0KaWYgKCFyZXF1aXJlKCJSU1FMaXRlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoIlJTUUxpdGUiKQ0KICAgbGlicmFyeShSU1FMaXRlKQ0KfQ0KDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQojDQojIHNwZWNpZmljYXRpb25zIG9mIG91dHB1dHMgb2YgY29kZSBpbiBjb2RlIGNodW5rcw0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCAgICAgICMgaW5jbHVkZSBjb2RlIGNodW5rIGluIHRoZSBvdXRwdXQgZmlsZQ0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmdzID0gRkFMU0UsICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3UgY2FuIGNob29zZSB0byBpbmNsdWRlIHRoZSB3YXJuaW5nIG1lc3NhZ2VzIGluDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIG91dHB1dCBmaWxlLiANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlcyA9IEZBTFNFLCAgIw0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSBUUlVFLCAgICAgIyB5b3UgY2FuIGFsc28gZGVjaWRlIHdoZXRoZXIgdG8gaW5jbHVkZSB0aGUgb3V0cHV0DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW4gdGhlIG91dHB1dCBmaWxlLg0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQQ0KICAgICAgICAgICAgICAgICAgICAgICkgICANCg0KI2RiIDwtIGRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgZGJuYW1lID0gInNxbC5zcWxpdGUiKQ0KI2tuaXRyOjpvcHRzX2NodW5rJHNldChjb25uZWN0aW9uID0gImRiIikNCg0KYGBgDQoNCiMgSW50cm9kdWNpb24NCg0KDQpUbyBydW4gU1FMIGNsYXVzZXMgaW4gUiwgd2UgbmVlZCB0byB1c2Ugc2V2ZXJhbCBSIGxpYnJhcmllcyAoaW5zdGFsbGVkIGFuZCBsb2FkZWQgaW4gdGhlIGFib3ZlIFIgc2V0dXAgY29kZSBjaHVuaykuIFRoZXJlIGFyZSBkaWZmZXJlbnQgd2F5cyB0byBydW4gU1FMIHF1ZXJpZXMgaW4gUi4gV2Ugb25seSBpbnRyb2R1Y2Ugb25lIG9mIHRoZW0gdGhhdCBydW5zIHRoZSBiYXNpYyBTUUwgY29kZS4gIA0KDQojIyBDb25uZWN0IFIgdG8gRXhpc3RpbmcgRGF0YWJhc2UNCg0KSWYgdGhlcmUgaXMgYW4gZXhpc3RpbmcgZGF0YWJhc2UsIHRoZSBmb2xsb3dpbmcgY29kZSBjb25uZWN0cyBSIHRvIHRoZSBkYXRhYmFzZS4NCg0KYGBge30NCmNvbiA8LSBEQkk6OmRiQ29ubmVjdChkcnYgPSBvZGJjOjpvZGJjKCksDQogICAgICAgICAgICAgICAgICAgICAgRHJpdmVyID0gImRyaXZlcl9uYW1lIiwNCiAgICAgICAgICAgICAgICAgICAgICBTZXJ2ZXIgPSAic2VydmVyX3VybCIsDQogICAgICAgICAgICAgICAgICAgICAgRGF0YWJhc2UgPSAiZGF0YWJhc2VfbmFtZSIsDQogICAgICAgICAgICAgICAgICAgICAgdXNlciA9ICJ1c2VyIiwgI29wdGlvbmFsDQogICAgICAgICAgICAgICAgICAgICAgcGFzc3dvcmQgPSAicGFzc3dvcmQiKSAjb3B0aW9uYWwNCmBgYA0KDQojIyBDcmVhdGUgQSBEYXRhYmFzZSB0byBSdW4gU1FMIFF1ZXJpZXMgaW4gUg0KDQoNClRoaXMgc2hvcnQgbm90ZSBzaG93cyB0aGUgdGhyZWUgYmFzaWMgc3RlcHMgdG8gcnVuIFNRTCBpbiBSIHVzaW5nIFIgTWFya2Rvd24gc3RhcnRpbmcgd2l0aCBhIHNldCBvZiByZWxhdGlvbmFsIHRhYmxlcy4NCg0KMS4gTG9hZCByZWxhdGlvbmFsIGRhdGEgdGFibGVzIGFzIHVzdWFsIHRvIFIuDQoNCjIuIENyZWF0ZSBhIFNRTGl0ZSAocmVsYXRpb25hbCkgZGF0YWJhc2UgdGhhdCBjb250YWlucyB0aGVzZSByZWxhdGlvbmFsIHRhYmxlcy4NCg0KMy4gQ3JlYXRlIGFuIFIgY29kZSBjaHVuayBhbmQgY29ubmVjdCB0byB0aGUgY3JlYXRlZCBkYXRhYmFzZSB1c2luZyBDaHVuayBvcHRpb25zLg0KDQoNCg0KIyBDcmVhdGUgU1FMaXRlIERhdGFiYXNlIHdpdGggUg0KDQpJZiBtb2RlbGluZyByZXF1aXJlcyBhIGRhdGEgc2V0IHRoYXQgY29udGFpbnMgaW5mb3JtYXRpb24gZnJvbSBtdWx0aXBsZSByZWxhdGlvbmFsIGRhdGEgdGFibGVzLCB3ZSBuZWVkIHRvIHBlcmZvcm0gZGF0YSBtYW5hZ2VtZW50IHRvIGFnZ3JlZ2F0ZSB0aGUgcmVxdWlyZWQgaW5mb3JtYXRpb24gZnJvbSBkaWZmZXJlbnQgZGF0YSB0YWJsZXMuIFdlIGNhbiBsb2FkIHRoZSBkaWZmZXJlbnQgZGF0YSBzZXRzIGluIGRpZmZlcmVudCBmb3JtYXRzIHVzaW5nIGFwcHJvcHJpYXRlIFIgZnVuY3Rpb25zLiANCg0KQXMgYW4gZXhhbXBsZSwgV2UgdXNlIHRocmVlIGVjb2xvZ2ljYWwgc3VydmV5IGRhdGEgc2V0cyB0byBjcmVhdGUgYSBkYXRhYmFzZS4NCg0KYGBge3J9DQojTG9hZCB0aGUgc2FtcGxlIGRhdGENCnBsb3RzIDwtIHJlYWQuY3N2KCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9kYXRhc2V0cy9BbmltYWxTdXJ2ZXkvcGxvdHMuY3N2IikNCnNwZWNpZXMgPC0gcmVhZC5jc3YoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL2RhdGFzZXRzL0FuaW1hbFN1cnZleS9zcGVjaWVzLmNzdiIpDQpzdXJ2ZXlzIDwtIHJlYWQuY3N2KCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9kYXRhc2V0cy9BbmltYWxTdXJ2ZXkvc3VydmV5cy5jc3YiKQ0KYGBgDQoNCldlIG5leHQgZXhwbG9yZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHRhYmxlcy4NCg0KYGBge3J9DQpzdW1tYXJ5KHBsb3RzKQ0Kc3VtbWFyeShzcGVjaWVzKQ0Kc3VtbWFyeShzdXJ2ZXlzKQ0KYGBgDQoNClRoZSByZWxhdGlvbmFsIHRhYmxlIGBzdXJ2ZXlzYCBoYXMgYXR0cmlidXRlcyAqKnNwZWNpZXNfaWQqKiBhbmQgKipwbG90X2lkKiogdGhhdCBjb25uZWN0IHJlbGF0aW9uYWwgdGFibGVzIGBwbG90c2AgYW5kIGBzcGVjaWVzYC4gVGhlIHByaW1hcnkgYW5kIGZvcmVpZ24ga2V5cyBhcmUgZGVwaWN0ZWQgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgb3V0LndpZHRoPSI5OSUiLCBmaWcuY2FwPSJUaGUgcHJpbWFyeSBhbmQgZm9yZWlnbiBrZXlzIG9mIHRoZSB0aHJlZSByZWxhdGlvbmFsIHRhYmxlcy4ifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL1JlbGF0aW9uc2hpcEJldHdlZW5UYWJsZXMucG5nIikNCmBgYA0KDQpcDQoNCg0KTmV4dCwgd2UgY3JlYXRlIGEgU1FMaXQgZGF0YWJhc2UgdXNpbmcgUiBsaWJyYXJpZXMgYW5kIHRoZW4gcmVtb3ZlIHRoZSBkYXRhIGZyYW1lcyBmcm9tIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4NCg0KYGBge3J9DQojQ3JlYXRlIGRhdGFiYXNlDQpjb24gPC0gZGJDb25uZWN0KGRydiA9IFNRTGl0ZSgpLA0KICAgICAgICAgICAgICAgICBkYm5hbWUgPSAiOm1lbW9yeToiKQ0KDQojc3RvcmUgc2FtcGxlIGRhdGEgaW4gdGhlIGRhdGFiYXNlDQpkYldyaXRlVGFibGUoY29ubiA9IGNvbiwgDQogICAgICAgICAgICAgbmFtZSA9ICJwbG90cyIsDQogICAgICAgICAgICAgdmFsdWUgPSBwbG90cykNCg0KZGJXcml0ZVRhYmxlKGNvbm4gPSBjb24sIA0KICAgICAgICAgICAgIG5hbWUgPSAic3BlY2llcyIsDQogICAgICAgICAgICAgdmFsdWUgPSBzcGVjaWVzKQ0KDQpkYldyaXRlVGFibGUoY29ubiA9IGNvbiwgDQogICAgICAgICAgICAgbmFtZSA9ICJzdXJ2ZXlzIiwNCiAgICAgICAgICAgICB2YWx1ZSA9IHN1cnZleXMpDQogDQojcmVtb3ZlIHRoZSBsb2NhbCBkYXRhIGZyb20gdGhlIGVudmlyb25tZW50DQpybShwbG90cywgc3BlY2llcywgc3VydmV5cykNCmBgYA0KDQpXZSBjYW4gdXNlIHRoZSB0YWJsZSB2aWV3IGZ1bmN0aW9uIGB0YmwoKWAgdG8gdmlldyB0aGUgcmVsYXRpb25hbCBkYXRhIHRhYmxlcyBpbiB0aGUgZGF0YWJhc2UuICANCg0KYGBge3J9DQp0Ymwoc3JjID0gY29uLCAgICAgICAgICMgIHRoZSBzb3VyY2UgaWYgdGhlIGRhdGFiYXNlIGNvbm5lY3Rpb24gcHJvZmlsZQ0KICAgIGMoInN1cnZleXMiKSkgICAgICAjICB0aGUgbmFtZSBvZiB0aGUgdGFibGUgdG8gcHJldmlldw0KdGJsKHNyYyA9IGNvbiwgInNwZWNpZXMiKQ0KdGJsKHNyYyA9IGNvbiwgInBsb3RzIikNCmBgYA0KDQojIFJ1bm5pbmcgU1FMIFF1ZXJpZXMgaW4gUiANCg0KVG8gdXNlIFNRTCBpbiBSTWFya2Rvd24sIHdlIG5lZWQgdGhlIGZvbGxvd2luZyBjaHVuayBvcHRpb25zOg0KDQoqIHNxbA0KKiBjb25uZWN0aW9uID0gImRhdGFiYXNlLW5hbWUiDQoqIG91dHB1dC52YXIgPSAib3V0cHV0LWRhdGFzZXQtbmFtZSINCg0KSWYgd2UgY3JlYXRlIGEgZGF0YSB2aWV3IG9ubHksIHdlIHNpbXBseSBpZ25vcmUgb3B0aW9uIGBvdXRwdXQudmFyID0gYA0KDQpGb2xsb3dpbmcgYXJlIGZldyBleGFtcGxlcyBvZiBTUUwgcXVlcmllcyBiYXNlZCBvbiB0aGUgYW5pbWFsIHN1cnZleSBkYXRhIHRhYmxlcyBpbiB0aGUgZGF0YWJhc2UuDQoNCiMjIEJhc2ljIFNRTCBPcGVyYXRvcnMNCg0KV2hpbGUgd29ya2luZyB3aXRoIGRhdGFiYXNlcywgd2UgdXNlIFNRTCBxdWVyaWVzIHRvIG1hbmlwdWxhdGUgdGhlIGRhdGEgYW5kIHJldHJpZXZlIHRoZSBkZXNpcmVkIHJlc3VsdC4gVGhpcyBtYW5pcHVsYXRpb24gb2YgZGF0YSBpcyBhY2hpZXZlZCB0aHJvdWdoIHZhcmlvdXMgKipTUUwgb3BlcmF0b3JzKiouIEFuIG9wZXJhdG9yIGlzIGEga2V5d29yZCBpbiBTUUwgdGhhdCBoZWxwcyB1cyBhY2Nlc3MgdGhlIGRhdGEgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBiYXNlZCBvbiB0aGUgb3BlcmF0b3LigJlzIGZ1bmN0aW9uYWxpdHkuIFNRTCBwcm92aWRlcyB1cyB3aXRoIG1hbnkgc3VjaCBvcGVyYXRvcnMgdG8gZWFzZSB0aGUgcHJvY2VzcyBvZiBkYXRhIG1hbmlwdWxhdGlvbi4gSW4gdGhpcyBzdWJzZWN0aW9uLCB3ZSB3aWxsIGxvb2sgYXQgdGhyZWUgbWFqb3IgdHlwZXMgb2Ygb3BlcmF0b3JzIGluIFNRTC4NCg0KKipBcml0aG1ldGljIE9wZXJhdG9ycyoqDQoNCkFyaXRobWV0aWMgb3BlcmF0b3JzIGFyZSB1c2VkIHRvIHBlcmZvcm0gYXJpdGhtZXRpYyBvcGVyYXRpb25zIHN1Y2ggYXMgYWRkaXRpb24sIHN1YnRyYWN0aW9uLCBkaXZpc2lvbiwgYW5kIG11bHRpcGxpY2F0aW9uLiBUaGVzZSBvcGVyYXRvcnMgdXN1YWxseSBhY2NlcHQgbnVtZXJpYyBvcGVyYW5kcy4gDQoNCg0KfCBPcGVyYXRvciAgfCBPcGVyYXRpb24gfCBEZXNjcmlwdGlvbiB8DQp8Oi0tLS0tLS0tLS18Oi0tLS0tLS0tLS18Oi0tLS0tLS0tLS0tLXwNCnwgKyB8IEFkZGl0aW9uIHwgQWRkcyBvcGVyYW5kcyBvbiBlaXRoZXIgc2lkZSBvZiB0aGUgb3BlcmF0b3IgfA0KfCAtIHwgU3VidHJhY3Rpb24gfCBTdWJ0cmFjdHMgdGhlIHJpZ2h0LWhhbmQgb3BlcmFuZCBmcm9tIHRoZSBsZWZ0LWhhbmQgb3BlcmFuZHwNCnwgKiB8TXVsdGlwbGljYXRpb24gfCBNdWx0aXBsaWVzIHRoZSB2YWx1ZXMgb24gZWFjaCBzaWRlfA0KfCAvIHxEaXZpc2lvbiB8IERpdmlkZXMgbGVmdC1oYW5kIG9wZXJhbmQgYnkgcmlnaHQtaGFuZCBvcGVyYW5kIHwNCnwgJSB8IE1vZHVsdXMgfERpdmlkZXMgbGVmdC1oYW5kIG9wZXJhbmQgYnkgcmlnaHQtaGFuZCBvcGVyYW5kIGFuZCByZXR1cm5zIHRoZSByZW1haW5kZXJ8DQoNClwNCg0KKipDb21wYXJpc29uIE9wZXJhdG9ycyoqDQoNCkNvbXBhcmlzb24gb3BlcmF0b3JzIGluIFNRTCBhcmUgdXNlZCB0byBjaGVjayB0aGUgZXF1YWxpdHkgb2YgdHdvIGV4cHJlc3Npb25zLiBJdCBjaGVja3Mgd2hldGhlciBvbmUgZXhwcmVzc2lvbiBpcyBpZGVudGljYWwgdG8gYW5vdGhlci4gQ29tcGFyaXNvbiBvcGVyYXRvcnMgYXJlIGdlbmVyYWxseSB1c2VkIGluIHRoZSAqKldIRVJFIGNsYXVzZSoqIG9mIGEgU1FMIHF1ZXJ5LiBUaGUgcmVzdWx0IG9mIGEgY29tcGFyaXNvbiBvcGVyYXRpb24gbWF5IGJlIFRSVUUsIEZBTFNFLCBvciBVTktOT1dOLiBXaGVuIG9uZSBvciBib3RoIHRoZSBleHByZXNzaW9uIGlzIE5VTEwsIHRoZW4gdGhlIG9wZXJhdG9yIHJldHVybnMgVU5LTk9XTi4gDQoNCg0KDQp8IE9wZXJhdG9yICB8IE9wZXJhdGlvbiB8IERlc2NyaXB0aW9uIHwNCnw6LS0tLS0tLS0tLXw6LS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tfA0KfCA9IHxFcXVhbCB0byB8Q2hlY2tzIGlmIGJvdGggb3BlcmFuZHMgaGF2ZSBlcXVhbCB2YWx1ZSwgaWYgeWVzLCB0aGVuIHJldHVybnMgVFJVRXwNCnwgPiB8R3JlYXRlciB0aGFuIHxDaGVja3MgaWYgdGhlIHZhbHVlIG9mIHRoZSBsZWZ0LWhhbmQgb3BlcmFuZCBpcyBncmVhdGVyIHRoYW4gdGhlIHJpZ2h0LWhhbmQgb3BlcmFuZCBvciBub3R8DQp8IDwgfExlc3MgdGhhbiB8UmV0dXJucyBUUlVFIGlmIHRoZSB2YWx1ZSBvZiB0aGUgbGVmdC1oYW5kIG9wZXJhbmQgaXMgbGVzcyB0aGFuIHRoZSB2YWx1ZSBvZiB0aGUgcmlnaHQtaGFuZCBvcGVyYW5kfA0KfD49IHxHcmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gfCBJdCBjaGVja3MgaWYgdGhlIHZhbHVlIG9mIHRoZSBsZWZ0LWhhbmQgb3BlcmFuZCBpcyBncmVhdGVyIHRoYW4gb3IgZXF1YWwgdG8gdGhlIHZhbHVlIG9mIHRoZSByaWdodC1oYW5kIG9wZXJhbmQsIGlmIHllcywgdGhlbiByZXR1cm5zIFRSVUUgfA0KfDw9IHxMZXNzIHRoYW4gb3IgZXF1YWwgdG8gfEV4YW1pbmVzIGlmIHRoZSB2YWx1ZSBvZiB0aGUgbGVmdC1oYW5kIG9wZXJhdG9yIGlzIGxlc3MgdGhhbiBvciBlcXVhbCB0byB0aGUgcmlnaHQtaGFuZCBvcGVyYW5kIHwNCnw8PiBvciAhPSB8Tm90IGVxdWFsIHRvIHxDaGVja3MgaWYgdmFsdWVzIG9uIGVpdGhlciBzaWRlIG9mIHRoZSBvcGVyYXRvciBhcmUgZXF1YWwgb3Igbm90LiBSZXR1cm5zIFRSVUUgaWYgdmFsdWVzIGFyZSBub3QgZXF1YWx8DQoNClwNCg0KKipMb2dpY2FsIE9wZXJhdG9ycyoqDQoNCkxvZ2ljYWwgb3BlcmF0b3JzIGFyZSB0aG9zZSBvcGVyYXRvcnMgdGhhdCB0YWtlIHR3byBleHByZXNzaW9ucyBhcyBvcGVyYW5kcyBhbmQgcmV0dXJuIFRSVUUgb3IgRmFsc2UgYXMgb3V0cHV0LiANCg0KDQp8IE9wZXJhdG9yICB8IERlc2NyaXB0aW9uIHwNCnw6LS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tfA0KfEFMTHxDb21wYXJlcyBhIHZhbHVlIHRvIGFsbCBvdGhlciB2YWx1ZXMgaW4gYSBzZXR8DQp8QU5EIHxSZXR1cm5zIHRoZSByZWNvcmRzIGlmIGFsbCB0aGUgY29uZGl0aW9ucyBzZXBhcmF0ZWQgYnkgQU5EIGFyZSBUUlVFIHwNCnxBTlkgfENvbXBhcmVzIGEgc3BlY2lmaWMgdmFsdWUgdG8gYW55IG90aGVyIHZhbHVlcyBpbiBhIHNldCB8DQp8IFNPTUUgfENvbXBhcmVzIGEgdmFsdWUgdG8gZWFjaCB2YWx1ZSBpbiBhIHNldC4gSXQgaXMgc2ltaWxhciB0byBBTlkgb3BlcmF0b3IgfA0KfExJS0UgfCBJdCByZXR1cm5zIHRoZSByb3dzIGZvciB3aGljaCB0aGUgb3BlcmFuZCBtYXRjaGVzIGEgc3BlY2lmaWMgcGF0dGVybiB8DQp8SU4gfFVzZWQgdG8gY29tcGFyZSBhIHZhbHVlIHRvIGEgc3BlY2lmaWVkIHZhbHVlIGluIGEgbGlzdCB8DQp8QkVUV0VFTiB8IFJldHVybnMgdGhlIHJvd3MgZm9yIHdoaWNoIHRoZSB2YWx1ZSBsaWVzIGJldHdlZW4gdGhlIG1lbnRpb25lZCByYW5nZXwNCnxOT1QgfCBVc2VkIHRvIHJldmVyc2UgdGhlIG91dHB1dCBvZiBhbnkgbG9naWNhbCBvcGVyYXRvciB8DQp8RVhJU1RTIHxVc2VkIHRvIHNlYXJjaCBhIHJvdyBpbiBhIHNwZWNpZmllZCB0YWJsZSBpbiB0aGUgZGF0YWJhc2UgfA0KfCBPUiB8IFJldHVybnMgdGhlIHJlY29yZHMgZm9yIHdoaWNoIGFueSBvZiB0aGUgY29uZGl0aW9ucyBzZXBhcmF0ZWQgYnkgT1IgaXMgdHJ1ZSB8DQp8TlVMTCB8IFJldHVybnMgdGhlIHJvd3Mgd2hlcmUgdGhlIG9wZXJhbmQgaXMgTlVMTCB8DQoNCg0KVGhlIGxvZ2ljYWwgYW5kIGNvbXBhcmlzb24gb3BlcmF0b3JzIGFyZSB1c3VhbGx5IHVzZWQgd2l0aCBjb25kaXRpb25hbCBxdWVyaWVzIGluIHdoaWNoIHdlIGNhbiBzcGVjaWZ5IGEgY29uZGl0aW9uYWwgZXhwcmVzc2lvbiBpbiBhICoqU0VMRUNUIHN0YXRlbWVudCBXSEVSRSBjbGF1c2UqKiB3aGljaCBzcGVjaWZpZXMgdGhhdCBvbmx5IHRob3NlIHJvd3MgZm9yIHdoaWNoIHRoZSBjb25kaXRpb25hbCBleHByZXNzaW9uIGlzIHRydWUgYXJlIHRvIGJlIHJldHJpZXZlZC4gVGhlIHN5bnRheCBmb3IgdGhlICoqU0VMRUNUIHN0YXRlbWVudCoqIGNvbnRhaW5pbmcgdGhlICoqV0hFUkUgY2xhdXNlKiogaXMgYXMgZm9sbG93cy4NCg0KYGBgDQpzZWxlY3Rfc3RhdGVtZW50Og0KCQlTRUxFQ1Qg4oCmIEZST00gdGFibGVfbmFtZQ0KIAkJCVdIRVJFIGNvbmRpdGlvbmFsX2V4cHJlc3Npb24NCmBgYA0KDQpUaGUgZm9sbG93aW5nIGlzIGEgc2ltcGxlIGV4YW1wbGUuIFdlIHdhbnQgdG8gY3JlYXRlIGEgc3Vic2V0IHN1Y2ggdGhhdCBgc3BlY2llc19pZGAgZXF1YWxzICJETSIgYW5kIGB3ZWlnaHQgPiAwYC4NCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gIkNvbmRpdGlvbmFsUXVlcnkifSANClNFTEVDVCAqDQpGUk9NIHN1cnZleXMgDQpXSEVSRSBzcGVjaWVzX2lkIExJS0UgIkRNIiBBTkQgd2VpZ2h0ID4gMDsNCmBgYA0KDQoNCg0KDQojIyBTdWJzZXR0aW5nIGFuZCBEdXBsaWNhdGluZyBEYXRhIA0KDQoxLiBFeHRyYWN0IHllYXIsIG1vbnRoLCBhbmQgZGF5IGZyb20gYHN1cnZleXNgIHRhYmxlDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIiwgb3V0cHV0LnZhcj0iWU1EIn0NClNFTEVDVCANCiAgc3VydmV5cy55ZWFyLCBzdXJ2ZXlzLm1vbnRoLCBzdXJ2ZXlzLkRheQ0KRlJPTSANCiBzdXJ2ZXlzICAgLS0gcG9pbnRlciBpcyBub3QgbmVlZGVkIHNpbmNlIGl0IGlzIGluIHRoZSBkYXRhYmFzZSANCldIRVJFDQogIHN1cnZleXMuc3BlY2llc19pZCBJTiAoJ05MJywgJ0RNJykgQU5EDQogIHN1cnZleXMuc2V4ID0gJ00nDQpgYGANCg0KVGhlIGNvZGUgY2h1bmsgZGVmaW5lZCB0aGUgb3V0cHV0IG9mIHRoZSBxdWVyeSBhcyBhbiBSIGRhdGEgZnJhbWUgKCpDYXV0aW9uOiBub3QgYSByZWxhdGlvbmFsIHRhYmxlIHN0b3JlZCBpbiB0aGUgU1FMaXRlIGRhdGFiYXNlKikgd2l0aCB0aGUgbmFtZSAqKllNRCoqLiBXZSBjaGVjayB0aGUgZmlyc3QgZmV3IHJlY29yZHMgZnJvbSB0aGUgZGF0YSBmcmFtZQ0KDQpgYGB7cn0NCmhlYWQoWU1EKQ0KYGBgDQpJZiB3ZSBzaW1wbHkgY3JlYXRlIGEgZGF0YSB2aWV3IHdpdGhvdXQgc2F2aW5nIGl0IGFzIGFuIFIgZGF0YSBmcmFtZSwgd2Ugc2ltcGx5IGlnbm9yZSB0aGUgb3B0aW9uIGBvdXRwdXQudmFyPSJZTUQiYCBpbiB0aGUgKipTUUwgY29kZSBjaHVuayoqLg0KDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIn0NClNFTEVDVCANCiAgc3VydmV5cy55ZWFyLCBzdXJ2ZXlzLm1vbnRoLCBzdXJ2ZXlzLkRheQ0KRlJPTSANCiBzdXJ2ZXlzICAtLSBwb2ludGVyIGlzIG5vdCBuZWVkZWQgc2luY2UgaXQgaXMgaW4gdGhlIGRhdGFiYXNlIA0KV0hFUkUNCiAgc3VydmV5cy5zcGVjaWVzX2lkIElOICgnTkwnLCAnRE0nKSBBTkQNCiAgc3VydmV5cy5zZXggPSAnTScNCmBgYA0KDQoNCjIuIER1cGxpY2F0ZSBkYXRhIGFuZCByZW5hbWUgaXQNCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyPSJTdXJ2ZXlDb3B5In0NClNFTEVDVCANCiAgc3VydmV5cy4qDQpGUk9NIA0KIHN1cnZleXMNCmBgYA0KDQpOb3RlIHRoYXQgKipzdXJ2ZXlzKiogaXMgYSByZWxhdGlvbiBkYXRhIHRhYmxlIGluIHRoZSBTUUxpdGUgZGF0YWJhc2UgYW5kIGR1cGxpY2F0ZWQgZGF0YSBpcyBub3QgaW4gdGhlIFNRTGl0ZSBkYXRhYmFzZSBidXQgYW4gUiBkYXRhIGZyYW1lIGluIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4NCg0KKipDYXV0aW9uKio6IGluIHRoZSBTUUwgY29kZSBjaHVuaywgcXVlcnkgc3RhdGVtZW50cyBvbmx5IHdvcmsgd2l0aCBTUUwgZGF0YWJhc2UuIFRoZXkgZG9uJ3Qgd29yayBmb3IgUiBkYXRhIGZyYW1lcyBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkuIFRoZSBmb2xsb3dpbmcgY29kZSBkb2Vzbid0IHdvcmsgYmVjYXVzZSB0aGUgZGF0YSBzZXQgYFN1cnZleUNvcHlgIGlzIG5vdCBpbiB0aGUgU1FMaXRlIGRhdGFiYXNlLiANCg0KDQpgYGB7IG91dHB1dC52YXI9InN1cnZleS5jb3B5In0NClNFTEVDVCANCiAgU3VydmV5Q29weS4qDQpGUk9NIA0KIFN1cnZleUNvcHkNCmBgYA0KDQpJbiBvdGhlciB3b3JkcywgaWYgd2Ugd2FudCB0byBxdWVyeSB0aGUgZGF0YSB0YWJsZSAoZGF0YSBmcmFtZSkgYFN1cnZleUNvcHlgLCB3ZSBuZWVkIHRvIGFkZCBpdCB0byB0aGUgZGF0YWJhc2UgYGNvbmAgZGVmaW5lZCBlYXJsaWVyLg0KDQpgYGB7cn0NCiMgU3RvcmUgc2FtcGxlIGRhdGEgaW4gdGhlIGRhdGFiYXNlDQpkYldyaXRlVGFibGUoY29ubiA9IGNvbiwgDQogICAgICAgICAgICAgbmFtZSA9ICJTdXJ2ZXlDb3B5IiwNCiAgICAgICAgICAgICB2YWx1ZSA9IFN1cnZleUNvcHkpDQojIyBSZW1vdmUgU3VydmV5Q29weSBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkNCnJtKFN1cnZleUNvcHkpDQpgYGANCg0KV2UgY2FuIHVzZSBgdGJsKClgIHRvIHZpZXcgdGhlIG5ld2x5IGFkZGVkIHRhYmxlIGluIHRoZSBTUUxpdGUgZGF0YWJhc2UgYGNvbmAuDQoNCmBgYHtyfQ0KdGJsKHNyYyA9IGNvbiwgYygiU3VydmV5Q29weSIpKQ0KYGBgDQoNCkluIFIsIHdlIGNhbiB1c2UgYGRiTGlzdFRhYmxlcygpYCB0byB2aWV3IHJlbGF0aW9uYWwgdGFibGVzIGluIHRoZSBkYXRhYmFzZS4NCg0KYGBge3J9DQpkYkxpc3RUYWJsZXMoY29uKQ0KYGBgDQoNCg0KV2Ugbm93IGNhbiBxdWVyeSB0aGUgcmVsYXRpb25hbCB0YWJsZSBgU3VydmV5Q29weWAgaW4gdGhlIGRhdGFiYXNlIGBjb25gIHVzaW5nIFNRTCBjbGF1c2UgYXMgdXN1YWwuDQoNCg0KMy4gQ3JlYXRlIGEgdGFibGUgdmlldyAoaS5lLiwgbm8gZGF0YSBzZXQgd2lsbCBiZSBjcmVhdGVkIGFuZCBzYXZlZCkNCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gIllNRCJ9DQpTRUxFQ1QgDQogIHN1cnZleXMueWVhciwgc3VydmV5cy5tb250aCwgc3VydmV5cy5EYXkNCkZST00gDQogc3VydmV5cw0KV0hFUkUNCiAgc3VydmV5cy5zcGVjaWVzX2lkID0gJ05MJyBBTkQNCiAgc3VydmV5cy5zZXggPSAnTScNCmBgYA0KDQoNCg0KIyMgRGVmaW5lIEEgTmV3IFZhcmlhYmxlIA0KDQoxLiBEZWZpbmUgYSBuZXcgdmFyaWFibGUgd2l0aCBzaW1wbGUgYXJpdGhtZXRpYyBvcGVyYXRpb25zDQoNCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gIkNhbHVsYXRlZFRhYmxlIn0NClNFTEVDVCANCiAgICBzdXJ2ZXlzLnBsb3RfaWQsIA0KICAgIHN1cnZleXMuc3BlY2llc19pZCwgDQogICAgc3VydmV5cy5zZXgsIA0KICAgIHN1cnZleXMud2VpZ2h0LCANCiAgICBzdXJ2ZXlzLndlaWdodC8xMDAgQVMgd3Rfa2lsbyAgLS0gc2hvdWxkIG5vdCB0aGUgcG9pbnRlciBpbiBmcm9udCBvZiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLS0gdGhlIG5hbWUgb2YgdGhlIG5ldyB2YXJpYWJsZSANCkZST00gDQogICBzdXJ2ZXlzDQpgYGANCg0KDQoyLiBEZWZpbmUgbmV3IHZhcmlhYmxlcyB1c2luZyBzdHJpbmcgZnVuY3Rpb25zIGluIFNRTA0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAiTmV3U3RyaW5nVmFyIn0NClNFTEVDVCBzdXJ2ZXlzLiosIA0KICAgICAgIHN1cnZleXMuc3BlY2llc19pZHx8Jy0nfHxzdXJ2ZXlzLnNleCBBUyBuZXdLZXkNCkZST00gc3VydmV5cw0KYGBgDQoNCg0KMy4gRGVmaW5lIG5ldyB2YXJpYWJsZXMgd2l0aCBhZ2dyZWdhdGVkIGluZm9ybWF0aW9uIA0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAiTmV3U3VtbWFyeVZhciJ9DQpTRUxFQ1Qgc3VydmV5cy5zcGVjaWVzX2lkLCANCiAgICAgICBDT1VOVChzdXJ2ZXlzLnNwZWNpZXNfaWQpIEFTIHNwZWNpZXNfY3RyDQpGUk9NIHN1cnZleXMNCkdST1VQIEJZIHN1cnZleXMuc3BlY2llc19pZA0KSEFWSU5HIHNwZWNpZXNfY3RyID4gMTANCmBgYA0KDQoNCg0KIyMgU29ydGluZyBWYXJpYWJsZXMNCg0KMS4gU29ydCBkYXRhIGJhc2VkIG9uIHRoZSBzdW1tYXJpemVkIHN0YXRpc3RpY3Mgb2YgYSB2YXJpYWJsZQ0KDQpTdW1tYXJ5IGZ1bmN0aW9ucyBhcmUgcmVzdHJpY3RlZCB0byB0aGUgU0VMRUNUIGFuZCBIQVZJTkcgY2xhdXNlcyBvbmx5Ow0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAiU29ydGluZ0RhdGEifQ0KU0VMRUNUIHN1cnZleXMuc3BlY2llc19pZA0KRlJPTSBzdXJ2ZXlzDQpHUk9VUCBCWSBzdXJ2ZXlzLnNwZWNpZXNfaWQNCk9SREVSIEJZIENPVU5UKHN1cnZleXMuc3BlY2llc19pZCk7DQpgYGANCg0KDQoyLiBTb3J0IGRhdGEgYmFzZWQgb24gYSBuZXcgdmFyaWFibGUgZGVmaW5lZCB1c2luZyBzdW1tYXJpemVkIHN0YXRpc3RpY3Mgb2YgYSB2YXJpYWJsZS4NCg0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAiU29ydGluZ05ld1ZhciJ9IA0KLyogY3JlYXRlIGEgdGFibGUgdmlldyovDQpTRUxFQ1Qgc3VydmV5cy5zcGVjaWVzX2lkIEFTIHN1YnRvdGFsLCANCiAgICAgICBDT1VOVCgqKSANCkZST00gc3VydmV5cw0KR1JPVVAgQlkgc3VydmV5cy5zcGVjaWVzX2lkDQpPUkRFUiBCWSBzdWJ0b3RhbDsNCmBgYA0KDQoNCiMgU1FMIEpvaW5zDQoNClNRTCBKT0lOIGNsYXVzZSBpcyB1c2VkIHRvIHF1ZXJ5IGFuZCBhY2Nlc3MgZGF0YSBmcm9tIG11bHRpcGxlIHRhYmxlcyBieSBlc3RhYmxpc2hpbmcgbG9naWNhbCByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlbS4gSXQgY2FuIGFjY2VzcyBkYXRhIGZyb20gbXVsdGlwbGUgdGFibGVzIHNpbXVsdGFuZW91c2x5IHVzaW5nIGNvbW1vbiBrZXkgdmFsdWVzIHNoYXJlZCBhY3Jvc3MgZGlmZmVyZW50IHRhYmxlcy4gVGhpcyBzZWN0aW9uIGJyaWVmbHkgaW50cm9kdWNlcyBjb21tb25seSB1c2VkIGpvaW4gb3BlcmF0aW9ucyB0byBtZXJnZSB0YWJsZXMgdXNpbmcgdGhlIGNvbW1vbiBrZXkocykuIEluIGVhY2ggb2YgdGhlIG1ham9yIGBqb2luYCBvcGVyYXRpb25zLCB3ZSB1c2UgYSB2aXN1YWwgaWxsdXN0cmF0aW9uIGZvbGxvd2VkIGJ5IGFuIGV4YW1wbGUuDQoNCg0KMS4gSW5uZXIgSm9pbg0KDQpJbm5lciBqb2lucyBjb21iaW5lIHJlY29yZHMgZnJvbSB0d28gdGFibGVzIHdoZW5ldmVyIHRoZXJlIGFyZSBtYXRjaGluZyB2YWx1ZXMgaW4gYSBmaWVsZCBjb21tb24gdG8gYm90aCB0YWJsZXMuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAiNDUlIiwgZmlnLmNhcD0iSWxsdXN0cmF0aW9uIG9mIGlubmVyIGpvaW4uIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9pbm5lci1qb2luLnBuZyIpDQpgYGANCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gImlubmVySm9pbiJ9IA0KU0VMRUNUICoNCkZST00gc3VydmV5cyBBUyBBDQpJTk5FUiBKT0lOIHNwZWNpZXMgQVMgQiAgIC0tIGJ5IGRlZmF1bHQsIEpPSU4gbWVhbnMgSU5ORVIgSk9JTg0KT04gQS5zcGVjaWVzX2lkID0gQi5zcGVjaWVzX2lkOw0KYGBgDQoNCg0KMi4gTGVmdCBKb2luDQoNCkxlZnQgSm9pbiBvciBMZWZ0IE91dGVyIEpvaW4gaW4gU1FMIGNvbWJpbmVzIHR3byBvciBtb3JlIHRhYmxlcywgd2hlcmUgdGhlIGZpcnN0IHRhYmxlIGlzIHJldHVybmVkIHdob2xseTsgYnV0LCBvbmx5IHRoZSBtYXRjaGluZyByZWNvcmQocykgYXJlIHJldHJpZXZlZCBmcm9tIHRoZSBjb25zZXF1ZW50IHRhYmxlcy4gSWYgemVybyAoMCkgcmVjb3JkcyBhcmUgbWF0Y2hlZCBpbiB0aGUgY29uc2VxdWVudCB0YWJsZXMsIHRoZSBqb2luIHdpbGwgc3RpbGwgcmV0dXJuIGEgcm93IGluIHRoZSByZXN1bHQsIGJ1dCB3aXRoIE5VTEwgaW4gZWFjaCBjb2x1bW4gZnJvbSB0aGUgcmlnaHQgdGFibGUuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAiNjAlIiwgZmlnLmNhcD0iSWxsdXN0cmF0aW9uIG9mIGxlZnQgam9pbi4ifQ0KaW5jbHVkZV9ncmFwaGljcygiaW1nL2xlZnQtam9pbi5wbmciKQ0KYGBgDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIiwgb3V0cHV0LnZhciA9ICJsZWZ0Sm9pbiJ9IA0KU0VMRUNUICoNCkZST00gc3VydmV5cyBBUyBBDQpMRUZUIEpPSU4gc3BlY2llcyBBUyBCDQpPTiBBLnNwZWNpZXNfaWQgPSBCLnNwZWNpZXNfaWQ7DQpgYGANCg0KDQozLiBSaWdodCBKb2luDQoNClRoZSBSaWdodCBKb2luIHF1ZXJ5IGluIFNRTCByZXR1cm5zIGFsbCByb3dzIGZyb20gdGhlIHJpZ2h0IHRhYmxlLCBldmVuIGlmIHRoZXJlIGFyZSBubyBtYXRjaGVzIGluIHRoZSBsZWZ0IHRhYmxlLiBJbiBzaG9ydCwgYSByaWdodCBqb2luIHJldHVybnMgYWxsIHRoZSB2YWx1ZXMgZnJvbSB0aGUgcmlnaHQgdGFibGUsIHBsdXMgbWF0Y2hlZCB2YWx1ZXMgZnJvbSB0aGUgbGVmdCB0YWJsZSBvciBOVUxMIGluIGNhc2Ugb2Ygbm8gbWF0Y2hpbmcgam9pbiBwcmVkaWNhdGUuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGggPSAiNjAlIiwgZmlnLmNhcD0iSWxsdXN0cmF0aW9uIG9mIHJpZ2h0IGpvaW4uIn0NCmluY2x1ZGVfZ3JhcGhpY3MoImltZy9yaWdodC1qb2luLnBuZyIpDQpgYGANCg0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAicmlnaHRKb2luIn0gDQpTRUxFQ1QgKg0KRlJPTSBzdXJ2ZXlzIEFTIEENClJJR0hUIEpPSU4gc3BlY2llcyBBUyBCDQpPTiBBLnNwZWNpZXNfaWQgPSBCLnNwZWNpZXNfaWQ7DQpgYGANCg0KDQo0LiBGdWxsIEpvaW4NCg0KU1FMIEZ1bGwgSm9pbiBjcmVhdGVzIGEgbmV3IHRhYmxlIGJ5IGpvaW5pbmcgdHdvIHRhYmxlcyBhcyBhIHdob2xlLiBUaGUgam9pbmVkIHRhYmxlIGNvbnRhaW5zIGFsbCByZWNvcmRzIGZyb20gYm90aCB0YWJsZXMgYW5kIGZpbGxzIE5VTEwgdmFsdWVzIGZvciBtaXNzaW5nIG1hdGNoZXMgb24gZWl0aGVyIHNpZGUuIEluIHNob3J0LCBmdWxsIGpvaW4gaXMgYSB0eXBlIG9mIG91dGVyIGpvaW4gdGhhdCBjb21iaW5lcyB0aGUgcmVzdWx0aW5nIHNldHMgb2YgYm90aCBsZWZ0IGFuZCByaWdodCBqb2lucy4NCg0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcicsIG91dC53aWR0aCA9ICI2MCUiLCBmaWcuY2FwPSJJbGx1c3RyYXRpb24gb2YgZnVsbCBqb2luLiJ9DQppbmNsdWRlX2dyYXBoaWNzKCJpbWcvZnVsbC1qb2luLnBuZyIpDQpgYGANCg0KDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIiwgb3V0cHV0LnZhciA9ICJmdWxsSm9pbiJ9IA0KU0VMRUNUICoNCkZST00gc3VydmV5cyBBUyBBDQpGVUxMIEpPSU4gc3BlY2llcyBBUyBCDQpPTiBBLnNwZWNpZXNfaWQgPSBCLnNwZWNpZXNfaWQ7DQpgYGANCg0KDQoNCg0KIyBTUUwgQWdncmVnYXRpb24gRnVuY3Rpb25zDQoNClNRTCBpcyBhIGdvb2Qgc3RhcnRpbmcgcG9pbnQgZm9yIGhpZ2gtbGV2ZWwgZGF0YSBhbmFseXNpcy4gTWFueSBkYXRhIGFuYWx5c2lzIHBhY2thZ2VzIGFuZCBsYW5ndWFnZXMgaGF2ZSB0aGVpciBvd24gaW50ZXJmYWNlIHRvIHJlYWQgZGF0YSBmcm9tIGRpZmZlcmVudCBTUUwtYmFzZWQgZGF0YWJhc2Ugc3lzdGVtcy4gSW4gZmFjdCwgYW55IHJlYWwtbGlmZSBkYXRhIGFuYWx5c2lzIHN0YXJ0cyBmcm9tIGFuIFJEQk1TLCBhbmQgdGhlIGJhc2ljIGFuYWx5c2lzIGFuZCByZXBvcnQgZ2VuZXJhdGlvbiBpcyBkb25lIG9uIHRoZSBTUUwgcGxhdGZvcm0gb2YgdGhhdCBSREJNUyBpdHNlbGYuIEEgZ29vZCBwcmV2aWV3IG9mIGRhdGEgd2l0aGluIHRoZSBSREJNUyBwbGF0Zm9ybSBpdHNlbGYgaGVscHMgYW5hbHlzdHMgdG8gZ2V0IGEgZmFzdCBoaWdoLWxldmVsIGFuYWx5c2lzIGluIGEgZGlmZmVyZW50IHBsYXRmb3JtLiBUaGUgbW9zdCBjb21tb25seSB1c2VkIGFnZ3JlZ2F0aW9uIGZ1bmN0aW9ucyBpbmNsdWRlICoqTUFYKCksIE1JTigpLCBBVkcoKSxTVU0oKSoqIGFuZCAqKkNPVU5UKCkqKi4gVGhlIGZvbGxvd2luZyBhcmUgYmFzaWMgZXhhbXBsZXMgdGhhdCBpbnZvbHZlIHNvbWUgb2YgdGhlc2UgYWdncmVnYXRpb24gZnVuY3Rpb25zLiANCg0KDQoxLiBBdmVyYWdlDQoNCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gIkpvaW5TdWJ0YWJsZSJ9IA0KU0VMRUNUIEEuc3BlY2llc19pZCwgDQogICAgICAgQS5zZXgsIA0KICAgICAgIEFWRyhBLndlaWdodCkgYXMgbWVhbl93Z3QgIA0KRlJPTSBzdXJ2ZXlzIEFTIEENCkpPSU4gc3BlY2llcyBBUyBCDQpPTiBBLnNwZWNpZXNfaWQgPSBCLnNwZWNpZXNfaWQgDQpXSEVSRSB0YXhhID0gJ1JvZGVudCcgQU5EIEEuc2V4IElTIE5PVCBOVUxMIA0KR1JPVVAgQlkgQS5zcGVjaWVzX2lkLCBBLnNleDsgICAtLSBzb3J0ZWQgYnkgdHdvIHZhcmlhYmxlcyANCmBgYA0KDQoNCjIuIFNhbXBsZSBzaXplDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIiwgb3V0cHV0LnZhciA9ICJzbXBsZVNpemUifSANClNFTEVDVCBDT1VOVCgqKSANCkZST00gc3VydmV5cw0KYGBgDQoNCg0KIyBTdWJxdWVyaWVzDQoNClN1YnF1ZXJpZXMgKGFsc28ga25vd24gYXMgaW5uZXIgcXVlcmllcyBvciBuZXN0ZWQgcXVlcmllcykgYXJlIGEgdG9vbCBmb3IgcGVyZm9ybWluZyBvcGVyYXRpb25zIGluIG11bHRpcGxlIHN0ZXBzLiBGb3IgZXhhbXBsZSwgaWYgeW91IHdhbnRlZCB0byB0YWtlIHRoZSBzdW1zIG9mIHNldmVyYWwgY29sdW1ucywgYW5kIHRoZW4gYXZlcmFnZSBhbGwgb2YgdGhvc2UgdmFsdWVzLCB5b3UnZCBuZWVkIHRvIGRvIGVhY2ggYWdncmVnYXRpb24gaW4gYSBkaXN0aW5jdCBzdGVwLiBTdWJxdWVyaWVzIGNhbiBiZSB1c2VkIGluIHNldmVyYWwgcGxhY2VzIHdpdGhpbiBhIHF1ZXJ5LiANCg0KDQojIyBTdWJxdWVyeSBpbiBTRUxFQ1QgQ2xhdXNlDQoNCkluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSwgd2Ugd2FudCB0byBkZWZpbmUgYSBuZXcgYXR0cmlidXRlLCB0aGUgcmVsYXRpdmUgcGVyY2VudGFnZSBvZiB0YXhvbm9taWMgZ3JvdXBzIGluIHRoZSBkYXRhIHNldCBpbiB0aGUgcmVsYXRpb25hbCB0YWJsZSBgc3VydmV5c2AuIFRoZSBmZWF0dXJlIGB0YXhhYCBpcyBub3QgaW4gdGhlIGBzdXJ2ZXlzYCB0YWJsZS4gV2UgbmVlZCB0byBqb2luIHRhYmxlcyBgc3VydmV5c2AgYW5kIGBzcGVjaWVzYCB0byBvYnRhaW4gdGhlIGRpc3RyaWJ1dGlvbiBvZiBgdGF4YWAgaW4gdGhlIGBzdXJ2ZXlzYCB0YWJsZS4gVG8gdGhpcyBlbmQsIHdlIG5lZWQgdG8ga25vdyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdGF4b25vbWljIGdyb3VwIGFuZCB0aGUgc2l6ZSBvZiB0aGUgcmVsYXRpb25hbCB0YWJsZSBgc3VydmV5c2AuDQoNCldlIGNvdWxkIHVzZSB0aGUgZm9sbG93aW5nIHF1ZXJpZXMgdG8gZmluZCB0aGUgc2FtcGxlIHNpemUgYW5kIHRoZSBzaXplcyBvZiBlYWNoIHRheG9uIGdyb3VwLg0KDQoxLiAqKlRvdGFsIFNhbXBsZSBTaXplKioNCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gIlNhbXBsZVNpemUifSANClNFTEVDVCBDT1VOVCgqKSBGUk9NIHN1cnZleXMNCmBgYA0KDQoyLiAqKkNhbGN1bGF0aW5nIFRheG9uIEdyb3VwIEZyZXF1ZW5jaWVzKioNCg0KVGhlIGdyb3VwIHRvdGFscyBhcmUgY2FsY3VsYXRlZCBpbiB0aGUgZm9sbG93aW5nIGNvZGUuDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIiwgb3V0cHV0LnZhciA9ICJzdWJUb3RhbCJ9IA0KU0VMRUNUIEIudGF4YSwgDQogICAgICAgQ09VTlQoKikNCkZST00gc3VydmV5cyBBUyBBICAtLSBBIGlzIGFuIGFsaWFzIG9mIGBzdXJ2ZXlzYCB0YWJsZQ0KSU5ORVIgSk9JTiBzcGVjaWVzIEFTIEIgLS0gaW5uZXIgam9pbg0KT04gQS5zcGVjaWVzX2lkID0gQi5zcGVjaWVzX2lkIA0KLS0gdGhpcyBmaW5kcyB0aGUgZ3JvdXAgZnJlcXVlbmNpZXMNCkdST1VQIEJZIHRheGE7IA0KYGBgDQoNCg0KVGhpcyBpcyBub3QgZWZmaWNpZW50LiBXZSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgbmVzdGVkIHF1ZXJ5IHRvIGVmZmljaWVudGx5IGZpbmQgdGhlIHJlbGF0aXZlIGZyZXF1ZW5jeSB0YWJsZS4NCg0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAic3ViU1FMaW5TRUxFQ1QifSANClNFTEVDVCBCLnRheGEsIA0KICAgICAgIDEwMC4wKkNPVU5UKCopLyhTRUxFQ1QgQ09VTlQoKikgRlJPTSBzdXJ2ZXlzKSAgQVMgUGVyY2VudGFnZQ0KRlJPTSBzdXJ2ZXlzIEFTIEENCkpPSU4gc3BlY2llcyBBUyBCDQpPTiBBLnNwZWNpZXNfaWQgPSBCLnNwZWNpZXNfaWQgDQpHUk9VUCBCWSB0YXhhOw0KYGBgDQoNClRoZSBhYm92ZSBxdWVyeSBwcm9kdWNlcyB0aGUgZm9sbG93aW5nIHJlbGF0aXZlIGZyZXF1ZW5jeSB0YWJsZS4NCg0KYGBge3J9DQprYWJsZShzdWJTUUxpblNFTEVDVCkNCmBgYA0KDQojIyBTdWJxdWVyeSBpbiBGUk9NIENsYXVzZQ0KDQpBcyBhbiBleGFtcGxlLCB3ZSBjcmVhdGUgYSBzdWJzZXQgd2l0aCBmZWF0dXJlcyBgcmVjb3JkX2lkLCB5ZWFyLCBwbG90X2lkLCBzcGVjaWVzX2lkLCBzZXgsIGhpbmRmb290X2xlbmd0aCwgd2VpZ2h0YCB3aXRoIHRoZSBjb25kaXRpb24gdGhhdCBgc3BlY2llc19pZCA9ICJETSJgIGFuZCBgc2V4ID0gIk0iYC4gVGhlIGZvbGxvd2luZyBxdWVyeSB0aGF0IGNvbnRhaW5zIGEgc3ViLXF1ZXJ5IGNhbiBkbyB0aGUgdHJpY2suDQoNCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gInN1YlNRTGluRlJPTSJ9IA0KU0VMRUNUIHN1Yl9zdXJ2ZXkucmVjb3JkX2lkLA0KICAgICAgIHN1Yl9zdXJ2ZXkueWVhciwNCiAgICAgICBzdWJfc3VydmV5LnBsb3RfaWQsDQogICAgICAgc3ViX3N1cnZleS5zcGVjaWVzX2lkLA0KICAgICAgIHN1Yl9zdXJ2ZXkuc2V4LA0KICAgICAgIHN1Yl9zdXJ2ZXkuaGluZGZvb3RfbGVuZ3RoLA0KICAgICAgIHN1Yl9zdXJ2ZXkud2VpZ2h0DQogIEZST00gKA0KICAgICAgICBTRUxFQ1QgKg0KICAgICAgICAgRlJPTSBzdXJ2ZXlzDQogICAgICAgICBXSEVSRSBzcGVjaWVzX2lkID0gJ0RNJw0KICAgICAgICkgc3ViX3N1cnZleSAgLS0gdGhlIG5hbWUgb2YgYSByZWxhdGlvbmFsIHRhYmxlIGRlZmluZWQgYnkgdGhlIHN1YnF1ZXJ5DQogV0hFUkUgc3ViX3N1cnZleS5zZXggPSAnTScNCmBgYA0KDQpUaGUgYWJvdmUgc3Vic2V0IGNhbiBhbHNvIGJlIGdlbmVyYXRlZCB1c2luZyB0aGUgZm9sbG93aW5nIHNpbXBsZSBxdWVyeS4NCg0KYGBge3NxbCwgY29ubmVjdGlvbiA9ICJjb24iLCBvdXRwdXQudmFyID0gInN1YlNRTGluRlJPTV9uZXcifSANClNFTEVDVCByZWNvcmRfaWQsDQogICAgICAgeWVhciwNCiAgICAgICBwbG90X2lkLA0KICAgICAgIHNwZWNpZXNfaWQsDQogICAgICAgc2V4LA0KICAgICAgIGhpbmRmb290X2xlbmd0aCwNCiAgICAgICB3ZWlnaHQNCiAgRlJPTSBzdXJ2ZXlzDQogV0hFUkUgc3BlY2llc19pZCA9ICdETScgQU5EIHNleCA9ICdNJzsNCmBgYA0KDQoNCiMjIFN1YnF1ZXJ5IGluIFdIRVJFIENsYXVzZQ0KDQoNClRoaXMgZXhhbXBsZSBzaG93cyB0aGUgd2F5IHRvIGNyZWF0ZSBhIHN1YnNldCBvZiBzdXJ2ZXlzIGByZWNvcmRfaWQsIHBsb3RfaWQsIHNwZWNpZXNfaWQsIHllYXIsIHNleCwgaGluZGZvb3RfbGVuZ3RoLCB3ZWlnaHRgIGZvciB0aGUgZWFybGllc3QgeWVhci4NCg0KDQpgYGB7c3FsLCBjb25uZWN0aW9uID0gImNvbiIsIG91dHB1dC52YXIgPSAic3ViU1FMaW5XSEVSRSJ9IA0KU0VMRUNUIHJlY29yZF9pZCwNCiAgICAgICBwbG90X2lkLA0KICAgICAgIHNwZWNpZXNfaWQsDQogICAgICAgeWVhciwNCiAgICAgICBzZXgsDQogICAgICAgaGluZGZvb3RfbGVuZ3RoLA0KICAgICAgIHdlaWdodA0KICBGUk9NIHN1cnZleXMNCiBXSEVSRSB5ZWFyID0gKFNFTEVDVCBNSU4oeWVhcikNCiAgICAgICAgICAgICAgICAgRlJPTSBzdXJ2ZXlzDQogICAgICAgICAgICAgICkNCmBgYA0KDQoNCiMjIFN1YnF1ZXJ5IGluIEpPSU4gQ2xhdXNlDQoNCkluIHRoZSBmb2xsb3dpbmcgZXhhbXBsZSwgd2UgdXNlIHRoZSBzdWJxdWVyeSB0byBmaW5kIHRoZSBmcmVxdWVuY3kgY291bnQgaW4gZWFjaCBzcGVjaWVzIGdyb3VwIGZyb20gdGhlIGBzdXJ2ZXlzYCB0YWJsZSBhbmQgYWRkIHRoZSBmcmVxdWVuY3kgdG8gdGhlIGBzcGVjaWVzYCB0YWJsZS4gDQoNCmBgYHtzcWwsIGNvbm5lY3Rpb24gPSAiY29uIiwgb3V0cHV0LnZhciA9ICJzdWJTUUxpbkpPSU4ifSANClNFTEVDVCAqDQogIEZST00gc3BlY2llcw0KICBKT0lOICggU0VMRUNUIENPVU5UKHNwZWNpZXNfaWQpIEFTIHNwZWNpZXNmcmVxLA0KICAgICAgICAgICAgICAgIHNwZWNpZXNfaWQNCiAgICAgICAgICAgRlJPTSBzdXJ2ZXlzDQogICAgICAgICAgIEdST1VQIEJZIHNwZWNpZXNfaWQNCiAgICAgICApIHN1YnENCiAgICBPTiBzcGVjaWVzLnNwZWNpZXNfaWQgPSBzdWJxLnNwZWNpZXNfaWQNCiBPUkRFUiBCWSBzdWJxLnNwZWNpZXNfaWQgREVTQyANCmBgYA0KDQpUaGUgcmVzdWx0aW5nIHRhYmxlIGlzIHNob3duIGluIHRoZSBmb2xsb3dpbmcuDQoNCmBgYHtyfQ0Ka2FibGUoc3ViU1FMaW5KT0lOKQ0KYGBgDQoNCg0KIyBDb25jbHVkaW5nIFJlbWFya3MNCg0KV2UgaGF2ZSBpbnRyb2R1Y2VkIHRoZSBiYXNpYyBTUUwgY2xhdXNlcywgdGhlIGJhc2ljIGpvaW4gb3BlcmF0aW9ucywgYW5kIHRoZSBzdWItcXVlcmllcyBpbiB0aGlzIG5vdGUuIEJ1dCBpdCBpcyBieSBubyBtZWFucyBjb25zaWRlcmVkIGEgY29tcGxldGUgdHV0b3JpYWwgZm9yIFNRTCBwcm9ncmFtbWluZy4gVGhpcyBub3RlIGludGVuZHMgdG8gaGVscCB5b3UgZ2V0IHN0YXJ0ZWQgd2l0aCB0aGUgYmFzaWMgU1FMIGNvZGluZyBvdXRzaWRlIGEgREJNUyBhbmQgZXhwYW5kIHlvdXIgdGVjaG5pY2FsIFZvY2FidWxhcnkgc28geW91IGNhbiB0YWxrIGNvbmZpZGVudGx5IHdpdGggZGF0YSBwcm9mZXNzaW9uYWxzIGluIHRoZSBmdXR1cmUuDQoNClNpbmNlIHdlIGRvbid0IGhhdmUgREJNUyB0byBwcmFjdGljZSBTUUwsIHNvbWUgb2YgdGhlIGFkdmFuY2VkIFNRTCB0YXNrcyBzdWNoIGFzIGNvbnRyb2wgZmxvdywgdXNlci1kZWZpbmVkIFNRTCBmdW5jdGlvbnMsIGV0Yy4gY2FuIG5vdCBiZSBwZXJmb3JtZWQgdXNpbmcgUFJPQyBTUUwgaW4gU0FTIGFuZCBSIGxpYnJhcmllcy4gWW91IGNhbiBsZWFybiB0aGVzZSBhZHZhbmNlZCB0ZWNobmlxdWVzIHF1aWNrbHkgb25jZSB5b3UgYXJlIGNvbWZvcnRhYmxlIHdpdGggdGhlIGJhc2ljIGNvbmNlcHRzIGNvdmVyZWQgaW4gdGhpcyBub3RlLg0KDQoNCg0KDQoNCg==