Objectives

Upon completion of this lesson, you will be able to:

  • define a stored procedure in MySQL
  • invoke a stored procedure from within an R program
  • remove a stored procedure

Introduction

Stored procedures are in important database application development mechanism in which complex data computations are defined and executed within the database – and on the database server rather than the client. This allows large and complex data manipulations to be done by a more powerful server and thus enables the use of less powerful (and cheaper) clients. The drawback is, of course, that the database may become a bottleneck. Nevertheless, server-based computations within stored procedures are an important architectural design mechanism and result in two-tier client/server applications.

Aside from leveraging a shared computational resource, stored procedure have other benefits. For one, they centralize business logic that can be executed from clients written in different languages. Updates to the business logic can be made once in the centrally located code within the database. An additional benefit is that large amounts of data do not have to be transmitted over a network – which can be slow and also a potential security risk.Furthermore, data is not stored on potentially unsecured clients where it could be saved or otherwise compromised. So, stored procedures are an important aspect in building secure applications. Stored procedures are also very fast as the database often caches results and data is local within the database server. From a development perspective, the source code for the stored procedure is stored as an object within the database and can be easily inspected (of course, inspection can also be prohibited through security settings via the REVOKE SQL statement).

This lesson focuses on the syntax for defining stored procedures for MySQL. While many databases support stored procedures, each has their own programming language syntax and restrictions. The use of stored procedures thus results in applications that are vendor-dependent and not easily portable, although many vendors do conform to the SQL:203 standard which standardizes some aspects of stored procedures and triggers.

To follow along, go to Files & Resources and download the R Notebook and copy (or better yet, type) the R and SQL code chunks into an R Notebook of your own – and experiment along the way.

Sample Database

To demonstrate the process for creating, editing, invoking, and deleting stored procedures, we will define the following database on an existing MySQL database hosted on db4free.net. To run this code for yourself, create and connect to your own database and change the setting accordingly.

Connect to MySQL

Of course, be sure to disconnect from the database once you are done.

Sample Tables

The code below creates two sample tables: Faculty and Committee.

CREATE TABLE IF NOT EXISTS Faculty (
  fid INTEGER NOT NULL PRIMARY KEY,
  fname TEXT NOT NULL,
  lname TEXT NOT NULL,
  facrank TEXT
);

We will now create the second table and link that table with a foreign key to the Faculty table.

CREATE TABLE IF NOT EXISTS Committee (
  name VARCHAR(32) NOT NULL PRIMARY KEY,
  level TEXT,
  chair INTEGER NOT NULL,
  FOREIGN KEY (chair) REFERENCES Faculty(fid)
)

Sample Data

Next, we will add some sample data to the database.

INSERT INTO Faculty (fid,fname,lname,facrank) VALUES 
  (100,"Jose","Annunziato","Assistant"),
  (200,"Dan","Feinberg","Adjunct"),
  (300,"Martin","Schedlbauer","Full"),
  (400,"Kathleen","Durant","Associate");
INSERT INTO Committee VALUES 
  ("Hiring","College",300),
  ("Merit","College", 300),
  ("Teaching","Department",400)

Defining Stored Procedures

Stored procedures must be created (defined) before they can be called. They are a function written in a vendor-specific programming language. Stored procedures should be defined as part of the database configuration code which is often in a separate configuration program or a script, alongside the SQL code to create tables, indexes, and triggers. Ad-hoc stored procedures can also be created using interactive means, such as via the MySQL Workbench.

To create a stored procedure from within an R Notebook, define either a {sql} chunk and place the code for the stored procedure within that chunk (and, remember, one procedure definition per chunk) or call the function dbSendStatement(). Of course, the code in a {sql} chunk is eventually translated to a call to dbSendStatement() during knitting, so it makes no difference which is used. If you write an R program (script), then you must use dbSendStatement().

If a stored procedure already exists, you will get an error if you try to define it again. So, drop the procedure first if it exists.

Before being able to create a stored procedure we must select a database if it wasn’t selected as part of the connection. To see the list of databases available on the server use SHOW DATABASES from either MySQL Workbench, the MySQL interactive console, or from within a {sql} chunk.

SHOW DATABASES;

To connect to a specific database, use the USE command.

USE cs5200db1;

Define with {sql} Chunk

In R, the simplest and quickest way to define a stored is not use a {sql} chunk. Be sure to only create the stored procedure if it does not already exist or drop it first before redefining it.

CREATE PROCEDURE getDepartmentCommittees ()
BEGIN
  SELECT * 
    FROM Committee
   WHERE level = 'Department';
END
CALL getDepartmentCommittees ();
Table 1: 1 records
name level chair
Teaching Department 400
[1] TRUE

Define with R Function

Alternatively to defining a stored procedure within a {sql} chunk, we can also define it using the R functions dbExecute() or dbSendStatement(). Of course, defining a function in a {sql} chunk will be simply be translated to an R function call anyway during the knitting of a markdown document.

Naturally, R functions are required when building R programs (scripts) where code chunks are not possible as they only work within markdown documents (such as R Notebooks).

Note the use of the function paste0() to build the SQL across multiple lines and with indentation.

sql <- paste0("CREATE PROCEDURE getDepartmentCommittees ()",
              " BEGIN",
              "   SELECT * ",
              "     FROM Committee",
              "    WHERE level = 'Department';",
              " END")

rs <- dbExecute(dbcon, sql)

Security Restrictions

CREATE PROCEDURE and CREATE FUNCTION require the CREATE ROUTINE privilege. They might also require the SUPER privilege, depending on the DEFINER value. By default, MySQL automatically grants the ALTER ROUTINE and EXECUTE privileges to the routine creator. This behavior can be changed by disabling the automatic_sp_privileges system variable.

DELIMITER

When defining multiple stored procedures or when a stored procedure definition is included within a SQL script, then the default SQL statement delimiter of semi-colon (;) has to be changed to some other character so that the delimiter ; can be used within stored procedures. However, within an R {sql} one can only define a single SQL statement, so no statement terminator is needed and the delimiter does not have to be changed. Also, including DELIMITER within a {sql} chunk will not work as a {sql} chunk can only contain a single SQL statement.

So, changing the delimiter with DELIMITER is only needed if you use SQL scripts to configure the database; they are not used within host programming languages such as R or Java.

Executing a Stored Procedure

Unlike a trigger, a stored procedure must be explicitly invoked by a client program. We have already seen that a stored procedure can be called from within a {sql} chunk. We will now look at how to call a procedure from an R function and how to overcome the problem of error message when calling another stored procedure before disconnecting from the database.

Calling a procedure actually returns two results set of which only the first is useful but the second can be ignored. Here is a solution to fetching all result sets and only using the first one:

sql = "CALL getDepartmentCommittees ()"

rs = dbSendQuery(dbcon, sql)
data = fetch(rs,n=-1)
# data now has ALL the rows from the first result set

# loop through any additional result sets and discard
while(dbMoreResults(dbcon) == TRUE) {
  dbNextResult(dbcon)
}

# the actual data is in the object 'data'
print(data)
##       name      level chair
## 1 Teaching Department   400

The above code should be used as the template for calling stored procedures from within R.

The call to dbMoreResults(dbcon) returns TRUE while there are more result set. Since we only need the first of the result sets, we can ignore the others. By fetching any additional results sets from the database connection, we will avoid any warnings from R that additional result sets are pending.

Return Values

The return value of a call to a stored procedure is always a table, so it is always a data frame in R.

Parameters

A stored procedure can take parameters as input arguments. The example below shows an argument and its use within the stored procedure.

CREATE PROCEDURE getCommittees (IN kind TEXT)
BEGIN
  SELECT * 
    FROM Committee
   WHERE level = kind;
END

An IN parameter is an argument that is passed by value and is not modified within the procedure. It is a call-by-value.

MySQL also recognizes INOUT and OUT parameters. An INOUT parameter is initialized by the caller, can be modified by the procedure, and any change made by the procedure is visible to the caller when the procedure returns. It is similar to a call-by-reference in programming. An OUT parameter passes a value from the procedure back to the caller. Its initial value is NULL within the procedure, and its value is visible to the caller when the procedure returns. It is another way to pass a value back from the procedure and is also an example of a call-by-value. Its programming value is somewhat dubious.

Debugging Stored Procedures

Stored procedure code runs on the database server and thus you cannot step through procedure code with a local debugger. They are best developed in small steps and through MySQL Workbench.

Removing a Stored Procedure

To remove a stored procedure use DROP PROCEDURE. You can optionally add the IF EXISTS or IF NOT EXISTS conditions. Dropping a procedure that does not exist results in an error.

Alternatively to dropping a procedure to rebuild it, one can also use an ALTER PROCEDURE statement to modify a procedure.

Developing Stored Procedures

This section takes a closer look at the programming language constructs available to build stored procedures in MySQL. It is a procedural programming language supporting most common programming constructs such as local variables, if and case statement, and loops.

Definition Syntax

The complete syntax for defining stored procedures is below:

CREATE [DEFINER = { user | CURRENT_USER }]          
PROCEDURE sp_name ([proc_parameter[,...]])          
[characteristic ...] routine_body    
proc_parameter: [ IN | OUT | INOUT ] param_name type    
type:          
Any valid MySQL data type    
characteristic:          
COMMENT 'string'     
| LANGUAGE SQL      
| [NOT] DETERMINISTIC      
| { CONTAINS SQL | NO SQL | READS SQL DATA 
| MODIFIES SQL DATA }      
| SQL SECURITY { DEFINER | INVOKER }    
routine_body:      
Valid SQL routine statement

Programming Constructs

See MySQL Stored Procedures.

Conclusion

Stored procedures are an important database application mechanism. They allow shared business logic to be stored and executed in the database which can result in faster, safer, and more maintainable applications. However, they come with the drawback of making the database server a potential bottleneck.


Files & Resources

All Files for Lesson 70.907

See Also

TBD

Errata

Let us know.

LS0tCnRpdGxlOiAiU3RvcmVkIFByb2NlZHVyZXMgaW4gTXlTUUwiCnBhcmFtczoKICBjYXRlZ29yeTogNzAKICBudW1iZXI6IDkwNwogIHRpbWU6IDMwCiAgbGV2ZWw6IGJlZ2lubmVyCiAgdGFnczogIm15c3FsLFIsc3RvcmVkIHByb2NlZHVyZXMsY2xpZW50L3NlcnZlcixkYXRhYmFzZSIKICBkZXNjcmlwdGlvbjogIlRoaXMgbGVzc29uIGV4cGxhaW5zIGhvdyB0byBkZWZpbmUgc3RvcmVkIHByb2NlZHVyZXMKICAgICAgICAgICAgICAgIGluIE15U1FMIGFuZCBleGVjdXRlIHRoZW0gZnJvbSB3aXRoaW4gYSBob3N0IHByb2dyYW0uCiAgICAgICAgICAgICAgICBJdCBzaG93cyB0aGUgaW52b2NhdGlvbiBwcm9jZXNzIHVzaW5nIGV4YW1wbGVzIGZyb20gUgogICAgICAgICAgICAgICAgYW5kIEphdmEuIEV4cGxhaW5zIHRoZSBzeW50YXggb2YgdGhlIHN0b3JlZCBwcm9jZWR1cmUKICAgICAgICAgICAgICAgIHByb2dyYW1taW5nIGxhbmd1YWdlIGZvciBNeVNRTC4iCmRhdGU6ICI8c21hbGw+YHIgU3lzLkRhdGUoKWA8L3NtYWxsPiIKYXV0aG9yOiAiPHNtYWxsPk1hcnRpbiBTY2hlZGxiYXVlcjwvc21hbGw+IgplbWFpbDogIm0uc2NoZWRsYmF1ZXJAbmV1LmVkdSIKYWZmaWxpdGF0aW9uOiAiTm9ydGhlYXN0ZXJuIFVuaXZlcnNpdHkiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvbGxhcHNlZDogZmFsc2UKICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgaGlnaGxpZ2h0OiB0YW5nbwotLS0KCi0tLQp0aXRsZTogIjxzbWFsbD5gciBwYXJhbXMkY2F0ZWdvcnlgLmByIHBhcmFtcyRudW1iZXJgPC9zbWFsbD48YnIvPjxzcGFuIHN0eWxlPSdjb2xvcjogIzJFNDA1MzsgZm9udC1zaXplOiAwLjllbSc+YHIgcm1hcmtkb3duOjptZXRhZGF0YSR0aXRsZWA8L3NwYW4+IgotLS0KCmBgYHtyIGNvZGU9eGZ1bjo6cmVhZF91dGY4KHBhc3RlMChoZXJlOjpoZXJlKCksJy9SL19pbnNlcnQyREIuUicpKSwgaW5jbHVkZSA9IEZBTFNFfQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgT2JqZWN0aXZlcwoKVXBvbiBjb21wbGV0aW9uIG9mIHRoaXMgbGVzc29uLCB5b3Ugd2lsbCBiZSBhYmxlIHRvOgoKLSAgIGRlZmluZSBhIHN0b3JlZCBwcm9jZWR1cmUgaW4gTXlTUUwKLSAgIGludm9rZSBhIHN0b3JlZCBwcm9jZWR1cmUgZnJvbSB3aXRoaW4gYW4gUiBwcm9ncmFtCi0gICByZW1vdmUgYSBzdG9yZWQgcHJvY2VkdXJlCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIEludHJvZHVjdGlvbgoKU3RvcmVkIHByb2NlZHVyZXMgYXJlIGluIGltcG9ydGFudCBkYXRhYmFzZSBhcHBsaWNhdGlvbiBkZXZlbG9wbWVudCBtZWNoYW5pc20gaW4gd2hpY2ggY29tcGxleCBkYXRhIGNvbXB1dGF0aW9ucyBhcmUgZGVmaW5lZCBhbmQgZXhlY3V0ZWQgd2l0aGluIHRoZSBkYXRhYmFzZSAtLSBhbmQgb24gdGhlIGRhdGFiYXNlIHNlcnZlciByYXRoZXIgdGhhbiB0aGUgY2xpZW50LiBUaGlzIGFsbG93cyBsYXJnZSBhbmQgY29tcGxleCBkYXRhIG1hbmlwdWxhdGlvbnMgdG8gYmUgZG9uZSBieSBhIG1vcmUgcG93ZXJmdWwgc2VydmVyIGFuZCB0aHVzIGVuYWJsZXMgdGhlIHVzZSBvZiBsZXNzIHBvd2VyZnVsIChhbmQgY2hlYXBlcikgY2xpZW50cy4gVGhlIGRyYXdiYWNrIGlzLCBvZiBjb3Vyc2UsIHRoYXQgdGhlIGRhdGFiYXNlIG1heSBiZWNvbWUgYSBib3R0bGVuZWNrLiBOZXZlcnRoZWxlc3MsIHNlcnZlci1iYXNlZCBjb21wdXRhdGlvbnMgd2l0aGluIHN0b3JlZCBwcm9jZWR1cmVzIGFyZSBhbiBpbXBvcnRhbnQgYXJjaGl0ZWN0dXJhbCBkZXNpZ24gbWVjaGFuaXNtIGFuZCByZXN1bHQgaW4gdHdvLXRpZXIgY2xpZW50L3NlcnZlciBhcHBsaWNhdGlvbnMuCgpBc2lkZSBmcm9tIGxldmVyYWdpbmcgYSBzaGFyZWQgY29tcHV0YXRpb25hbCByZXNvdXJjZSwgc3RvcmVkIHByb2NlZHVyZSBoYXZlIG90aGVyIGJlbmVmaXRzLiBGb3Igb25lLCB0aGV5IGNlbnRyYWxpemUgYnVzaW5lc3MgbG9naWMgdGhhdCBjYW4gYmUgZXhlY3V0ZWQgZnJvbSBjbGllbnRzIHdyaXR0ZW4gaW4gZGlmZmVyZW50IGxhbmd1YWdlcy4gVXBkYXRlcyB0byB0aGUgYnVzaW5lc3MgbG9naWMgY2FuIGJlIG1hZGUgb25jZSBpbiB0aGUgY2VudHJhbGx5IGxvY2F0ZWQgY29kZSB3aXRoaW4gdGhlIGRhdGFiYXNlLiBBbiBhZGRpdGlvbmFsIGJlbmVmaXQgaXMgdGhhdCBsYXJnZSBhbW91bnRzIG9mIGRhdGEgZG8gbm90IGhhdmUgdG8gYmUgdHJhbnNtaXR0ZWQgb3ZlciBhIG5ldHdvcmsgLS0gd2hpY2ggY2FuIGJlIHNsb3cgYW5kIGFsc28gYSBwb3RlbnRpYWwgc2VjdXJpdHkgcmlzay5GdXJ0aGVybW9yZSwgZGF0YSBpcyBub3Qgc3RvcmVkIG9uIHBvdGVudGlhbGx5IHVuc2VjdXJlZCBjbGllbnRzIHdoZXJlIGl0IGNvdWxkIGJlIHNhdmVkIG9yIG90aGVyd2lzZSBjb21wcm9taXNlZC4gU28sIHN0b3JlZCBwcm9jZWR1cmVzIGFyZSBhbiBpbXBvcnRhbnQgYXNwZWN0IGluIGJ1aWxkaW5nIHNlY3VyZSBhcHBsaWNhdGlvbnMuIFN0b3JlZCBwcm9jZWR1cmVzIGFyZSBhbHNvIHZlcnkgZmFzdCBhcyB0aGUgZGF0YWJhc2Ugb2Z0ZW4gY2FjaGVzIHJlc3VsdHMgYW5kIGRhdGEgaXMgbG9jYWwgd2l0aGluIHRoZSBkYXRhYmFzZSBzZXJ2ZXIuIEZyb20gYSBkZXZlbG9wbWVudCBwZXJzcGVjdGl2ZSwgdGhlIHNvdXJjZSBjb2RlIGZvciB0aGUgc3RvcmVkIHByb2NlZHVyZSBpcyBzdG9yZWQgYXMgYW4gb2JqZWN0IHdpdGhpbiB0aGUgZGF0YWJhc2UgYW5kIGNhbiBiZSBlYXNpbHkgaW5zcGVjdGVkIChvZiBjb3Vyc2UsIGluc3BlY3Rpb24gY2FuIGFsc28gYmUgcHJvaGliaXRlZCB0aHJvdWdoIHNlY3VyaXR5IHNldHRpbmdzIHZpYSB0aGUgUkVWT0tFIFNRTCBzdGF0ZW1lbnQpLgoKVGhpcyBsZXNzb24gZm9jdXNlcyBvbiB0aGUgc3ludGF4IGZvciBkZWZpbmluZyBzdG9yZWQgcHJvY2VkdXJlcyBmb3IgTXlTUUwuIFdoaWxlIG1hbnkgZGF0YWJhc2VzIHN1cHBvcnQgc3RvcmVkIHByb2NlZHVyZXMsIGVhY2ggaGFzIHRoZWlyIG93biBwcm9ncmFtbWluZyBsYW5ndWFnZSBzeW50YXggYW5kIHJlc3RyaWN0aW9ucy4gVGhlIHVzZSBvZiBzdG9yZWQgcHJvY2VkdXJlcyB0aHVzIHJlc3VsdHMgaW4gYXBwbGljYXRpb25zIHRoYXQgYXJlIHZlbmRvci1kZXBlbmRlbnQgYW5kIG5vdCBlYXNpbHkgcG9ydGFibGUsIGFsdGhvdWdoIG1hbnkgdmVuZG9ycyBkbyBjb25mb3JtIHRvIHRoZSBTUUw6MjAzIHN0YW5kYXJkIHdoaWNoIHN0YW5kYXJkaXplcyBzb21lIGFzcGVjdHMgb2Ygc3RvcmVkIHByb2NlZHVyZXMgYW5kIHRyaWdnZXJzLgoKVG8gZm9sbG93IGFsb25nLCBnbyB0byBbRmlsZXMgJiBSZXNvdXJjZXNdKCNmaWxlcy1yZXNvdXJjZXMpIGFuZCBkb3dubG9hZCB0aGUgUiBOb3RlYm9vayBhbmQgY29weSAob3IgYmV0dGVyIHlldCwgdHlwZSkgdGhlIFIgYW5kIFNRTCBjb2RlIGNodW5rcyBpbnRvIGFuIFIgTm90ZWJvb2sgb2YgeW91ciBvd24gLS0gYW5kIGV4cGVyaW1lbnQgYWxvbmcgdGhlIHdheS4KCiMjIFNhbXBsZSBEYXRhYmFzZQoKVG8gZGVtb25zdHJhdGUgdGhlIHByb2Nlc3MgZm9yIGNyZWF0aW5nLCBlZGl0aW5nLCBpbnZva2luZywgYW5kIGRlbGV0aW5nIHN0b3JlZCBwcm9jZWR1cmVzLCB3ZSB3aWxsIGRlZmluZSB0aGUgZm9sbG93aW5nIGRhdGFiYXNlIG9uIGFuIGV4aXN0aW5nIE15U1FMIGRhdGFiYXNlIGhvc3RlZCBvbiBbZGI0ZnJlZS5uZXRdKGh0dHA6Ly9kYjRmcmVlLm5ldCkuIFRvIHJ1biB0aGlzIGNvZGUgZm9yIHlvdXJzZWxmLCBjcmVhdGUgYW5kIGNvbm5lY3QgdG8geW91ciBvd24gZGF0YWJhc2UgYW5kIGNoYW5nZSB0aGUgc2V0dGluZyBhY2NvcmRpbmdseS4KCiMjIyBDb25uZWN0IHRvIE15U1FMCgpgYGB7ciBpbmNsdWRlPUZ9CnBhY2thZ2VzIDwtIGMoIlJNeVNRTCIpCm5ldy5wYWNrYWdlcyA8LSBwYWNrYWdlc1shKHBhY2thZ2VzICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKClbLCJQYWNrYWdlIl0pXQppZihsZW5ndGgobmV3LnBhY2thZ2VzKSkgaW5zdGFsbC5wYWNrYWdlcyhuZXcucGFja2FnZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBvcyA9ICJodHRwczovL2NyYW4udW5pLW11ZW5zdGVyLmRlLyIpCmBgYAoKYGBge3Igc2V0REJQYXJhbXMsIGluY2x1ZGU9RkFMU0V9CiMgc2V0dGluZ3MKZGJfdXNlciA8LSAnY3M1MjAwJyAKZGJfcGFzc3dvcmQgPC0gJ2tob3VyIzIwMjEnCmRiX25hbWUgPC0gJ2NzNTIwMGRiMScKZGJfdGFibGUgPC0gJ2NvdXJzZXMnCmRiX2hvc3QgPC0gJ2RiNGZyZWUubmV0JyAjIEhvc3QgODUuMTAuMjA1LjE3MwpkYl9wb3J0IDwtIDMzMDYKYGBgCgpgYGB7ciBjb25uZWN0MmRiNGZyZWUsIHdhcm5pbmc9RkFMU0UsIGVjaG89VCwgY29tbWVudD0iIiwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShSTXlTUUwpCgojIGNvbm5lY3QgdG8gZGF0YWJhc2UgaG9zdGVkIG9uIGRiNGZyZWUgc2VydmVyCmRiY29uIDwtICBkYkNvbm5lY3QoUk15U1FMOjpNeVNRTCgpLCAKICAgICAgICAgICAgICAgICAgIHVzZXIgPSBkYl91c2VyLCAKICAgICAgICAgICAgICAgICAgIHBhc3N3b3JkID0gZGJfcGFzc3dvcmQsCiAgICAgICAgICAgICAgICAgICBkYm5hbWUgPSBkYl9uYW1lLCAKICAgICAgICAgICAgICAgICAgIGhvc3QgPSBkYl9ob3N0LCAKICAgICAgICAgICAgICAgICAgIHBvcnQgPSBkYl9wb3J0KQpgYGAKCk9mIGNvdXJzZSwgYmUgc3VyZSB0byBkaXNjb25uZWN0IGZyb20gdGhlIGRhdGFiYXNlIG9uY2UgeW91IGFyZSBkb25lLgoKIyMjIFNhbXBsZSBUYWJsZXMKClRoZSBjb2RlIGJlbG93IGNyZWF0ZXMgdHdvIHNhbXBsZSB0YWJsZXM6ICpGYWN1bHR5KiBhbmQgKkNvbW1pdHRlZS4qCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb24sIGVjaG89Rn0KRFJPUCBUQUJMRSBJRiBFWElTVFMgQ29tbWl0dGVlCmBgYAoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29uLCBlY2hvPUZ9CkRST1AgVEFCTEUgSUYgRVhJU1RTIEZhY3VsdHkKYGBgCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CkNSRUFURSBUQUJMRSBJRiBOT1QgRVhJU1RTIEZhY3VsdHkgKAogIGZpZCBJTlRFR0VSIE5PVCBOVUxMIFBSSU1BUlkgS0VZLAogIGZuYW1lIFRFWFQgTk9UIE5VTEwsCiAgbG5hbWUgVEVYVCBOT1QgTlVMTCwKICBmYWNyYW5rIFRFWFQKKTsKYGBgCgpXZSB3aWxsIG5vdyBjcmVhdGUgdGhlIHNlY29uZCB0YWJsZSBhbmQgbGluayB0aGF0IHRhYmxlIHdpdGggYSBmb3JlaWduIGtleSB0byB0aGUgKkZhY3VsdHkqIHRhYmxlLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29ufQpDUkVBVEUgVEFCTEUgSUYgTk9UIEVYSVNUUyBDb21taXR0ZWUgKAogIG5hbWUgVkFSQ0hBUigzMikgTk9UIE5VTEwgUFJJTUFSWSBLRVksCiAgbGV2ZWwgVEVYVCwKICBjaGFpciBJTlRFR0VSIE5PVCBOVUxMLAogIEZPUkVJR04gS0VZIChjaGFpcikgUkVGRVJFTkNFUyBGYWN1bHR5KGZpZCkKKQpgYGAKCiMjIyBTYW1wbGUgRGF0YQoKTmV4dCwgd2Ugd2lsbCBhZGQgc29tZSBzYW1wbGUgZGF0YSB0byB0aGUgZGF0YWJhc2UuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb259CklOU0VSVCBJTlRPIEZhY3VsdHkgKGZpZCxmbmFtZSxsbmFtZSxmYWNyYW5rKSBWQUxVRVMgCiAgKDEwMCwiSm9zZSIsIkFubnVuemlhdG8iLCJBc3Npc3RhbnQiKSwKICAoMjAwLCJEYW4iLCJGZWluYmVyZyIsIkFkanVuY3QiKSwKICAoMzAwLCJNYXJ0aW4iLCJTY2hlZGxiYXVlciIsIkZ1bGwiKSwKICAoNDAwLCJLYXRobGVlbiIsIkR1cmFudCIsIkFzc29jaWF0ZSIpOwpgYGAKCmBgYHtzcWwgY29ubmVjdGlvbj1kYmNvbn0KSU5TRVJUIElOVE8gQ29tbWl0dGVlIFZBTFVFUyAKICAoIkhpcmluZyIsIkNvbGxlZ2UiLDMwMCksCiAgKCJNZXJpdCIsIkNvbGxlZ2UiLCAzMDApLAogICgiVGVhY2hpbmciLCJEZXBhcnRtZW50Iiw0MDApCmBgYAoKIyMgRGVmaW5pbmcgU3RvcmVkIFByb2NlZHVyZXMKClN0b3JlZCBwcm9jZWR1cmVzIG11c3QgYmUgY3JlYXRlZCAoZGVmaW5lZCkgYmVmb3JlIHRoZXkgY2FuIGJlIGNhbGxlZC4gVGhleSBhcmUgYSBmdW5jdGlvbiB3cml0dGVuIGluIGEgdmVuZG9yLXNwZWNpZmljIHByb2dyYW1taW5nIGxhbmd1YWdlLiBTdG9yZWQgcHJvY2VkdXJlcyBzaG91bGQgYmUgZGVmaW5lZCBhcyBwYXJ0IG9mIHRoZSBkYXRhYmFzZSBjb25maWd1cmF0aW9uIGNvZGUgd2hpY2ggaXMgb2Z0ZW4gaW4gYSBzZXBhcmF0ZSBjb25maWd1cmF0aW9uIHByb2dyYW0gb3IgYSBzY3JpcHQsIGFsb25nc2lkZSB0aGUgU1FMIGNvZGUgdG8gY3JlYXRlIHRhYmxlcywgaW5kZXhlcywgYW5kIHRyaWdnZXJzLiBBZC1ob2Mgc3RvcmVkIHByb2NlZHVyZXMgY2FuIGFsc28gYmUgY3JlYXRlZCB1c2luZyBpbnRlcmFjdGl2ZSBtZWFucywgc3VjaCBhcyB2aWEgdGhlIE15U1FMIFdvcmtiZW5jaC4KClRvIGNyZWF0ZSBhIHN0b3JlZCBwcm9jZWR1cmUgZnJvbSB3aXRoaW4gYW4gUiBOb3RlYm9vaywgZGVmaW5lIGVpdGhlciBhICp7c3FsfSogY2h1bmsgYW5kIHBsYWNlIHRoZSBjb2RlIGZvciB0aGUgc3RvcmVkIHByb2NlZHVyZSB3aXRoaW4gdGhhdCBjaHVuayAoYW5kLCByZW1lbWJlciwgb25lIHByb2NlZHVyZSBkZWZpbml0aW9uIHBlciBjaHVuaykgb3IgY2FsbCB0aGUgZnVuY3Rpb24gYGRiU2VuZFN0YXRlbWVudCgpYC4gT2YgY291cnNlLCB0aGUgY29kZSBpbiBhICp7c3FsfSogY2h1bmsgaXMgZXZlbnR1YWxseSB0cmFuc2xhdGVkIHRvIGEgY2FsbCB0byBgZGJTZW5kU3RhdGVtZW50KClgIGR1cmluZyBrbml0dGluZywgc28gaXQgbWFrZXMgbm8gZGlmZmVyZW5jZSB3aGljaCBpcyB1c2VkLiBJZiB5b3Ugd3JpdGUgYW4gUiBwcm9ncmFtIChzY3JpcHQpLCB0aGVuIHlvdSBtdXN0IHVzZSBgZGJTZW5kU3RhdGVtZW50KClgLgoKSWYgYSBzdG9yZWQgcHJvY2VkdXJlIGFscmVhZHkgZXhpc3RzLCB5b3Ugd2lsbCBnZXQgYW4gZXJyb3IgaWYgeW91IHRyeSB0byBkZWZpbmUgaXQgYWdhaW4uIFNvLCBkcm9wIHRoZSBwcm9jZWR1cmUgZmlyc3QgaWYgaXQgZXhpc3RzLgoKQmVmb3JlIGJlaW5nIGFibGUgdG8gY3JlYXRlIGEgc3RvcmVkIHByb2NlZHVyZSB3ZSBtdXN0IHNlbGVjdCBhIGRhdGFiYXNlIGlmIGl0IHdhc24ndCBzZWxlY3RlZCBhcyBwYXJ0IG9mIHRoZSBjb25uZWN0aW9uLiBUbyBzZWUgdGhlIGxpc3Qgb2YgZGF0YWJhc2VzIGF2YWlsYWJsZSBvbiB0aGUgc2VydmVyIHVzZSBTSE9XIERBVEFCQVNFUyBmcm9tIGVpdGhlciBNeVNRTCBXb3JrYmVuY2gsIHRoZSBNeVNRTCBpbnRlcmFjdGl2ZSBjb25zb2xlLCBvciBmcm9tIHdpdGhpbiBhICp7c3FsfSogY2h1bmsuCgpgYGB7c3FsIGNvbm5lY3Rpb249ZGJjb24sIGV2YWw9Rn0KU0hPVyBEQVRBQkFTRVM7CmBgYAoKVG8gY29ubmVjdCB0byBhIHNwZWNpZmljIGRhdGFiYXNlLCB1c2UgdGhlIFVTRSBjb21tYW5kLgoKYGBge3NxbCBjb25uZWN0aW9uPWRiY29uLCBldmFsPUZ9ClVTRSBjczUyMDBkYjE7CmBgYAoKIyMjIERlZmluZSB3aXRoIHtzcWx9IENodW5rCgpJbiBSLCB0aGUgc2ltcGxlc3QgYW5kIHF1aWNrZXN0IHdheSB0byBkZWZpbmUgYSBzdG9yZWQgaXMgbm90IHVzZSBhICp7c3FsfSogY2h1bmsuIEJlIHN1cmUgdG8gb25seSBjcmVhdGUgdGhlIHN0b3JlZCBwcm9jZWR1cmUgaWYgaXQgZG9lcyBub3QgYWxyZWFkeSBleGlzdCBvciBkcm9wIGl0IGZpcnN0IGJlZm9yZSByZWRlZmluaW5nIGl0LgoKYGBge3NxbCBkcm9wU1AxLCBjb25uZWN0aW9uPWRiY29uLCBlY2hvPUZ9CkRST1AgUFJPQ0VEVVJFIElGIEVYSVNUUyBnZXREZXBhcnRtZW50Q29tbWl0dGVlczsKYGBgCgpgYGB7c3FsIGRlZlNQdmlhQ2h1bmssIGNvbm5lY3Rpb249ZGJjb259CkNSRUFURSBQUk9DRURVUkUgZ2V0RGVwYXJ0bWVudENvbW1pdHRlZXMgKCkKQkVHSU4KICBTRUxFQ1QgKiAKICAgIEZST00gQ29tbWl0dGVlCiAgIFdIRVJFIGxldmVsID0gJ0RlcGFydG1lbnQnOwpFTkQKYGBgCgpgYGB7c3FsIGNhbGxQcm9jRnJvbVNRTCwgY29ubmVjdGlvbj1kYmNvbn0KQ0FMTCBnZXREZXBhcnRtZW50Q29tbWl0dGVlcyAoKTsKYGBgCgpgYGB7ciBkaXNjTlJlLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVQsIGNvbW1lbnQ9IiIsIG1lc3NhZ2U9RkFMU0V9CmRiRGlzY29ubmVjdChkYmNvbikKCiMgcmVjb25uZWN0IGFnYWluIHRvIGF2b2lkIHByb2NlZHVyZSBwcm9ibGVtCmRiY29uIDwtICBkYkNvbm5lY3QoUk15U1FMOjpNeVNRTCgpLCAKICAgICAgICAgICAgICAgICAgIHVzZXIgPSBkYl91c2VyLCAKICAgICAgICAgICAgICAgICAgIHBhc3N3b3JkID0gZGJfcGFzc3dvcmQsCiAgICAgICAgICAgICAgICAgICBkYm5hbWUgPSBkYl9uYW1lLCAKICAgICAgICAgICAgICAgICAgIGhvc3QgPSBkYl9ob3N0LCAKICAgICAgICAgICAgICAgICAgIHBvcnQgPSBkYl9wb3J0KQpgYGAKCiMjIyBEZWZpbmUgd2l0aCBSIEZ1bmN0aW9uCgpBbHRlcm5hdGl2ZWx5IHRvIGRlZmluaW5nIGEgc3RvcmVkIHByb2NlZHVyZSB3aXRoaW4gYSAqe3NxbH0qIGNodW5rLCB3ZSBjYW4gYWxzbyBkZWZpbmUgaXQgdXNpbmcgdGhlIFIgZnVuY3Rpb25zIGBkYkV4ZWN1dGUoKWAgb3IgYGRiU2VuZFN0YXRlbWVudCgpYC4gT2YgY291cnNlLCBkZWZpbmluZyBhIGZ1bmN0aW9uIGluIGEgKntzcWx9KiBjaHVuayB3aWxsIGJlIHNpbXBseSBiZSB0cmFuc2xhdGVkIHRvIGFuIFIgZnVuY3Rpb24gY2FsbCBhbnl3YXkgZHVyaW5nIHRoZSBrbml0dGluZyBvZiBhIG1hcmtkb3duIGRvY3VtZW50LgoKTmF0dXJhbGx5LCBSIGZ1bmN0aW9ucyBhcmUgcmVxdWlyZWQgd2hlbiBidWlsZGluZyBSIHByb2dyYW1zIChzY3JpcHRzKSB3aGVyZSBjb2RlIGNodW5rcyBhcmUgbm90IHBvc3NpYmxlIGFzIHRoZXkgb25seSB3b3JrIHdpdGhpbiBtYXJrZG93biBkb2N1bWVudHMgKHN1Y2ggYXMgUiBOb3RlYm9va3MpLgoKTm90ZSB0aGUgdXNlIG9mIHRoZSBmdW5jdGlvbiBgcGFzdGUwKClgIHRvIGJ1aWxkIHRoZSBTUUwgYWNyb3NzIG11bHRpcGxlIGxpbmVzIGFuZCB3aXRoIGluZGVudGF0aW9uLgoKYGBge3NxbCBkcm9wU1AyLCBjb25uZWN0aW9uPWRiY29uLCBlY2hvPUZ9CkRST1AgUFJPQ0VEVVJFIGdldERlcGFydG1lbnRDb21taXR0ZWVzOwpgYGAKCmBgYHtyIGRlZlNQdmlhUn0Kc3FsIDwtIHBhc3RlMCgiQ1JFQVRFIFBST0NFRFVSRSBnZXREZXBhcnRtZW50Q29tbWl0dGVlcyAoKSIsCiAgICAgICAgICAgICAgIiBCRUdJTiIsCiAgICAgICAgICAgICAgIiAgIFNFTEVDVCAqICIsCiAgICAgICAgICAgICAgIiAgICAgRlJPTSBDb21taXR0ZWUiLAogICAgICAgICAgICAgICIgICAgV0hFUkUgbGV2ZWwgPSAnRGVwYXJ0bWVudCc7IiwKICAgICAgICAgICAgICAiIEVORCIpCgpycyA8LSBkYkV4ZWN1dGUoZGJjb24sIHNxbCkKYGBgCgojIyMgU2VjdXJpdHkgUmVzdHJpY3Rpb25zCgpDUkVBVEUgUFJPQ0VEVVJFIGFuZCBDUkVBVEUgRlVOQ1RJT04gcmVxdWlyZSB0aGUgQ1JFQVRFIFJPVVRJTkUgcHJpdmlsZWdlLiBUaGV5IG1pZ2h0IGFsc28gcmVxdWlyZSB0aGUgU1VQRVIgcHJpdmlsZWdlLCBkZXBlbmRpbmcgb24gdGhlIERFRklORVIgdmFsdWUuIEJ5IGRlZmF1bHQsIE15U1FMIGF1dG9tYXRpY2FsbHkgZ3JhbnRzIHRoZSBBTFRFUiBST1VUSU5FIGFuZCBFWEVDVVRFIHByaXZpbGVnZXMgdG8gdGhlIHJvdXRpbmUgY3JlYXRvci4gVGhpcyBiZWhhdmlvciBjYW4gYmUgY2hhbmdlZCBieSBkaXNhYmxpbmcgdGhlICphdXRvbWF0aWNfc3BfcHJpdmlsZWdlcyogc3lzdGVtIHZhcmlhYmxlLgoKIyMjIERFTElNSVRFUgoKV2hlbiBkZWZpbmluZyBtdWx0aXBsZSBzdG9yZWQgcHJvY2VkdXJlcyBvciB3aGVuIGEgc3RvcmVkIHByb2NlZHVyZSBkZWZpbml0aW9uIGlzIGluY2x1ZGVkIHdpdGhpbiBhIFNRTCBzY3JpcHQsIHRoZW4gdGhlIGRlZmF1bHQgU1FMIHN0YXRlbWVudCBkZWxpbWl0ZXIgb2Ygc2VtaS1jb2xvbiAoOykgaGFzIHRvIGJlIGNoYW5nZWQgdG8gc29tZSBvdGhlciBjaGFyYWN0ZXIgc28gdGhhdCB0aGUgZGVsaW1pdGVyIDsgY2FuIGJlIHVzZWQgd2l0aGluIHN0b3JlZCBwcm9jZWR1cmVzLiBIb3dldmVyLCB3aXRoaW4gYW4gUiAqe3NxbH0qIG9uZSBjYW4gb25seSBkZWZpbmUgYSBzaW5nbGUgU1FMIHN0YXRlbWVudCwgc28gbm8gc3RhdGVtZW50IHRlcm1pbmF0b3IgaXMgbmVlZGVkIGFuZCB0aGUgZGVsaW1pdGVyIGRvZXMgbm90IGhhdmUgdG8gYmUgY2hhbmdlZC4gQWxzbywgaW5jbHVkaW5nIERFTElNSVRFUiB3aXRoaW4gYSAqe3NxbH0qIGNodW5rIHdpbGwgbm90IHdvcmsgYXMgYSAqe3NxbH0qIGNodW5rIGNhbiBvbmx5IGNvbnRhaW4gYSBzaW5nbGUgU1FMIHN0YXRlbWVudC4KClNvLCBjaGFuZ2luZyB0aGUgZGVsaW1pdGVyIHdpdGggREVMSU1JVEVSIGlzIG9ubHkgbmVlZGVkIGlmIHlvdSB1c2UgU1FMIHNjcmlwdHMgdG8gY29uZmlndXJlIHRoZSBkYXRhYmFzZTsgdGhleSBhcmUgbm90IHVzZWQgd2l0aGluIGhvc3QgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2VzIHN1Y2ggYXMgUiBvciBKYXZhLgoKIyMgRXhlY3V0aW5nIGEgU3RvcmVkIFByb2NlZHVyZQoKVW5saWtlIGEgdHJpZ2dlciwgYSBzdG9yZWQgcHJvY2VkdXJlIG11c3QgYmUgZXhwbGljaXRseSBpbnZva2VkIGJ5IGEgY2xpZW50IHByb2dyYW0uIFdlIGhhdmUgYWxyZWFkeSBzZWVuIHRoYXQgYSBzdG9yZWQgcHJvY2VkdXJlIGNhbiBiZSBjYWxsZWQgZnJvbSB3aXRoaW4gYSAqe3NxbH0qIGNodW5rLiBXZSB3aWxsIG5vdyBsb29rIGF0IGhvdyB0byBjYWxsIGEgcHJvY2VkdXJlIGZyb20gYW4gUiBmdW5jdGlvbiBhbmQgaG93IHRvIG92ZXJjb21lIHRoZSBwcm9ibGVtIG9mIGVycm9yIG1lc3NhZ2Ugd2hlbiBjYWxsaW5nIGFub3RoZXIgc3RvcmVkIHByb2NlZHVyZSBiZWZvcmUgZGlzY29ubmVjdGluZyBmcm9tIHRoZSBkYXRhYmFzZS4KCkNhbGxpbmcgYSBwcm9jZWR1cmUgYWN0dWFsbHkgcmV0dXJucyB0d28gcmVzdWx0cyBzZXQgb2Ygd2hpY2ggb25seSB0aGUgZmlyc3QgaXMgdXNlZnVsIGJ1dCB0aGUgc2Vjb25kIGNhbiBiZSBpZ25vcmVkLiBIZXJlIGlzIGEgc29sdXRpb24gdG8gZmV0Y2hpbmcgYWxsIHJlc3VsdCBzZXRzIGFuZCBvbmx5IHVzaW5nIHRoZSBmaXJzdCBvbmU6CgpgYGB7ciBtdWx0UlN9CnNxbCA9ICJDQUxMIGdldERlcGFydG1lbnRDb21taXR0ZWVzICgpIgoKcnMgPSBkYlNlbmRRdWVyeShkYmNvbiwgc3FsKQpkYXRhID0gZmV0Y2gocnMsbj0tMSkKIyBkYXRhIG5vdyBoYXMgQUxMIHRoZSByb3dzIGZyb20gdGhlIGZpcnN0IHJlc3VsdCBzZXQKCiMgbG9vcCB0aHJvdWdoIGFueSBhZGRpdGlvbmFsIHJlc3VsdCBzZXRzIGFuZCBkaXNjYXJkCndoaWxlKGRiTW9yZVJlc3VsdHMoZGJjb24pID09IFRSVUUpIHsKICBkYk5leHRSZXN1bHQoZGJjb24pCn0KCiMgdGhlIGFjdHVhbCBkYXRhIGlzIGluIHRoZSBvYmplY3QgJ2RhdGEnCnByaW50KGRhdGEpCmBgYAoKVGhlIGFib3ZlIGNvZGUgc2hvdWxkIGJlIHVzZWQgYXMgdGhlIHRlbXBsYXRlIGZvciBjYWxsaW5nIHN0b3JlZCBwcm9jZWR1cmVzIGZyb20gd2l0aGluIFIuCgpUaGUgY2FsbCB0byBgZGJNb3JlUmVzdWx0cyhkYmNvbilgIHJldHVybnMgKlRSVUUqIHdoaWxlIHRoZXJlIGFyZSBtb3JlIHJlc3VsdCBzZXQuIFNpbmNlIHdlIG9ubHkgbmVlZCB0aGUgZmlyc3Qgb2YgdGhlIHJlc3VsdCBzZXRzLCB3ZSBjYW4gaWdub3JlIHRoZSBvdGhlcnMuIEJ5IGZldGNoaW5nIGFueSBhZGRpdGlvbmFsIHJlc3VsdHMgc2V0cyBmcm9tIHRoZSBkYXRhYmFzZSBjb25uZWN0aW9uLCB3ZSB3aWxsIGF2b2lkIGFueSB3YXJuaW5ncyBmcm9tIFIgdGhhdCBhZGRpdGlvbmFsIHJlc3VsdCBzZXRzIGFyZSBwZW5kaW5nLgoKIyMjIFJldHVybiBWYWx1ZXMKClRoZSByZXR1cm4gdmFsdWUgb2YgYSBjYWxsIHRvIGEgc3RvcmVkIHByb2NlZHVyZSBpcyBhbHdheXMgYSB0YWJsZSwgc28gaXQgaXMgYWx3YXlzIGEgZGF0YSBmcmFtZSBpbiBSLgoKIyMjIFBhcmFtZXRlcnMKCkEgc3RvcmVkIHByb2NlZHVyZSBjYW4gdGFrZSBwYXJhbWV0ZXJzIGFzIGlucHV0IGFyZ3VtZW50cy4gVGhlIGV4YW1wbGUgYmVsb3cgc2hvd3MgYW4gYXJndW1lbnQgYW5kIGl0cyB1c2Ugd2l0aGluIHRoZSBzdG9yZWQgcHJvY2VkdXJlLgoKYGBge3NxbCBkcm9wU1AzLCBjb25uZWN0aW9uPWRiY29uLCBlY2hvPUZ9CkRST1AgUFJPQ0VEVVJFIElGIEVYSVNUUyBnZXRDb21taXR0ZWVzOwpgYGAKCmBgYHtzcWwgZGVmR2V0Q29tbWl0dGVlcywgY29ubmVjdGlvbj1kYmNvbn0KQ1JFQVRFIFBST0NFRFVSRSBnZXRDb21taXR0ZWVzIChJTiBraW5kIFRFWFQpCkJFR0lOCiAgU0VMRUNUICogCiAgICBGUk9NIENvbW1pdHRlZQogICBXSEVSRSBsZXZlbCA9IGtpbmQ7CkVORApgYGAKCkFuICpJTiogcGFyYW1ldGVyIGlzIGFuIGFyZ3VtZW50IHRoYXQgaXMgcGFzc2VkIGJ5IHZhbHVlIGFuZCBpcyBub3QgbW9kaWZpZWQgd2l0aGluIHRoZSBwcm9jZWR1cmUuIEl0IGlzIGEgY2FsbC1ieS12YWx1ZS4KCk15U1FMIGFsc28gcmVjb2duaXplcyAqSU5PVVQqIGFuZCAqT1VUKiBwYXJhbWV0ZXJzLiBBbiAqSU5PVVQqIHBhcmFtZXRlciBpcyBpbml0aWFsaXplZCBieSB0aGUgY2FsbGVyLCBjYW4gYmUgbW9kaWZpZWQgYnkgdGhlIHByb2NlZHVyZSwgYW5kIGFueSBjaGFuZ2UgbWFkZSBieSB0aGUgcHJvY2VkdXJlIGlzIHZpc2libGUgdG8gdGhlIGNhbGxlciB3aGVuIHRoZSBwcm9jZWR1cmUgcmV0dXJucy4gSXQgaXMgc2ltaWxhciB0byBhIGNhbGwtYnktcmVmZXJlbmNlIGluIHByb2dyYW1taW5nLiBBbiBPVVQgcGFyYW1ldGVyIHBhc3NlcyBhIHZhbHVlIGZyb20gdGhlIHByb2NlZHVyZSBiYWNrIHRvIHRoZSBjYWxsZXIuIEl0cyBpbml0aWFsIHZhbHVlIGlzIE5VTEwgd2l0aGluIHRoZSBwcm9jZWR1cmUsIGFuZCBpdHMgdmFsdWUgaXMgdmlzaWJsZSB0byB0aGUgY2FsbGVyIHdoZW4gdGhlIHByb2NlZHVyZSByZXR1cm5zLiBJdCBpcyBhbm90aGVyIHdheSB0byBwYXNzIGEgdmFsdWUgYmFjayBmcm9tIHRoZSBwcm9jZWR1cmUgYW5kIGlzIGFsc28gYW4gZXhhbXBsZSBvZiBhIGNhbGwtYnktdmFsdWUuIEl0cyBwcm9ncmFtbWluZyB2YWx1ZSBpcyBzb21ld2hhdCBkdWJpb3VzLgoKIyMgRGVidWdnaW5nIFN0b3JlZCBQcm9jZWR1cmVzCgpTdG9yZWQgcHJvY2VkdXJlIGNvZGUgcnVucyBvbiB0aGUgZGF0YWJhc2Ugc2VydmVyIGFuZCB0aHVzIHlvdSBjYW5ub3Qgc3RlcCB0aHJvdWdoIHByb2NlZHVyZSBjb2RlIHdpdGggYSBsb2NhbCBkZWJ1Z2dlci4gVGhleSBhcmUgYmVzdCBkZXZlbG9wZWQgaW4gc21hbGwgc3RlcHMgYW5kIHRocm91Z2ggTXlTUUwgV29ya2JlbmNoLgoKIyMgUmVtb3ZpbmcgYSBTdG9yZWQgUHJvY2VkdXJlCgpUbyByZW1vdmUgYSBzdG9yZWQgcHJvY2VkdXJlIHVzZSAqRFJPUCBQUk9DRURVUkUqLiBZb3UgY2FuIG9wdGlvbmFsbHkgYWRkIHRoZSAqSUYgRVhJU1RTKiBvciAqSUYgTk9UIEVYSVNUUyogY29uZGl0aW9ucy4gRHJvcHBpbmcgYSBwcm9jZWR1cmUgdGhhdCBkb2VzIG5vdCBleGlzdCByZXN1bHRzIGluIGFuIGVycm9yLgoKYGBge3NxbCBkcm9wU1A0LCBjb25uZWN0aW9uPWRiY29uLCBlY2hvPUYsIGV2YWw9Rn0KRFJPUCBQUk9DRURVUkUgSUYgRVhJU1RTIGdldENvbW1pdHRlZXM7CmBgYAoKQWx0ZXJuYXRpdmVseSB0byBkcm9wcGluZyBhIHByb2NlZHVyZSB0byByZWJ1aWxkIGl0LCBvbmUgY2FuIGFsc28gdXNlIGFuIEFMVEVSIFBST0NFRFVSRSBzdGF0ZW1lbnQgdG8gbW9kaWZ5IGEgcHJvY2VkdXJlLgoKIyMgRGV2ZWxvcGluZyBTdG9yZWQgUHJvY2VkdXJlcwoKVGhpcyBzZWN0aW9uIHRha2VzIGEgY2xvc2VyIGxvb2sgYXQgdGhlIHByb2dyYW1taW5nIGxhbmd1YWdlIGNvbnN0cnVjdHMgYXZhaWxhYmxlIHRvIGJ1aWxkIHN0b3JlZCBwcm9jZWR1cmVzIGluIE15U1FMLiBJdCBpcyBhIHByb2NlZHVyYWwgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2Ugc3VwcG9ydGluZyBtb3N0IGNvbW1vbiBwcm9ncmFtbWluZyBjb25zdHJ1Y3RzIHN1Y2ggYXMgbG9jYWwgdmFyaWFibGVzLCBpZiBhbmQgY2FzZSBzdGF0ZW1lbnQsIGFuZCBsb29wcy4KCiMjIyBEZWZpbml0aW9uIFN5bnRheAoKVGhlIGNvbXBsZXRlIHN5bnRheCBmb3IgZGVmaW5pbmcgc3RvcmVkIHByb2NlZHVyZXMgaXMgYmVsb3c6CgpgYGAgICAgICAgICAKQ1JFQVRFIFtERUZJTkVSID0geyB1c2VyIHwgQ1VSUkVOVF9VU0VSIH1dICAgICAgICAgIApQUk9DRURVUkUgc3BfbmFtZSAoW3Byb2NfcGFyYW1ldGVyWywuLi5dXSkgICAgICAgICAgCltjaGFyYWN0ZXJpc3RpYyAuLi5dIHJvdXRpbmVfYm9keSAgICAKcHJvY19wYXJhbWV0ZXI6IFsgSU4gfCBPVVQgfCBJTk9VVCBdIHBhcmFtX25hbWUgdHlwZSAgICAKdHlwZTogICAgICAgICAgCkFueSB2YWxpZCBNeVNRTCBkYXRhIHR5cGUgICAgCmNoYXJhY3RlcmlzdGljOiAgICAgICAgICAKQ09NTUVOVCAnc3RyaW5nJyAgICAgCnwgTEFOR1VBR0UgU1FMICAgICAgCnwgW05PVF0gREVURVJNSU5JU1RJQyAgICAgIAp8IHsgQ09OVEFJTlMgU1FMIHwgTk8gU1FMIHwgUkVBRFMgU1FMIERBVEEgCnwgTU9ESUZJRVMgU1FMIERBVEEgfSAgICAgIAp8IFNRTCBTRUNVUklUWSB7IERFRklORVIgfCBJTlZPS0VSIH0gICAgCnJvdXRpbmVfYm9keTogICAgICAKVmFsaWQgU1FMIHJvdXRpbmUgc3RhdGVtZW50CmBgYAoKIyMjIFByb2dyYW1taW5nIENvbnN0cnVjdHMKClNlZSBbTXlTUUwgU3RvcmVkIFByb2NlZHVyZXNdKGh0dHBzOi8vd3d3LnczcmVzb3VyY2UuY29tL215c3FsL215c3FsLXByb2NlZHVyZS5waHApLgoKIyMgQ29uY2x1c2lvbgoKU3RvcmVkIHByb2NlZHVyZXMgYXJlIGFuIGltcG9ydGFudCBkYXRhYmFzZSBhcHBsaWNhdGlvbiBtZWNoYW5pc20uIFRoZXkgYWxsb3cgc2hhcmVkIGJ1c2luZXNzIGxvZ2ljIHRvIGJlIHN0b3JlZCBhbmQgZXhlY3V0ZWQgaW4gdGhlIGRhdGFiYXNlIHdoaWNoIGNhbiByZXN1bHQgaW4gZmFzdGVyLCBzYWZlciwgYW5kIG1vcmUgbWFpbnRhaW5hYmxlIGFwcGxpY2F0aW9ucy4gSG93ZXZlciwgdGhleSBjb21lIHdpdGggdGhlIGRyYXdiYWNrIG9mIG1ha2luZyB0aGUgZGF0YWJhc2Ugc2VydmVyIGEgcG90ZW50aWFsIGJvdHRsZW5lY2suCgpgYGB7ciBkaXNjb25uZWN0REIsIGVjaG89VH0KcyA8LSBkYkRpc2Nvbm5lY3QoZGJjb24pCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcyB7I2ZpbGVzLXJlc291cmNlc30KCmBgYHtyIHppcEZpbGVzLCBlY2hvPUZBTFNFfQp6aXBOYW1lID0gc3ByaW50ZigiTGVzc29uRmlsZXMtJXMtJXMuemlwIiwgCiAgICAgICAgICAgICAgICAgcGFyYW1zJGNhdGVnb3J5LAogICAgICAgICAgICAgICAgIHBhcmFtcyRudW1iZXIpCgp0ZXh0QUxpbmsgPSBwYXN0ZTAoIkFsbCBGaWxlcyBmb3IgTGVzc29uICIsIAogICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksIi4iLHBhcmFtcyRudW1iZXIpCgojIGRvd25sb2FkRmlsZXNMaW5rKCkgaXMgaW5jbHVkZWQgZnJvbSBfaW5zZXJ0MkRCLlIKa25pdHI6OnJhd19odG1sKGRvd25sb2FkRmlsZXNMaW5rKCIuIiwgemlwTmFtZSwgdGV4dEFMaW5rKSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIFNlZSBBbHNvCgpUQkQKCiMjIFJlZmVyZW5jZXMKCi0gICBbTXlTUUwgU3RvcmVkIFByb2NlZHVyZXNdKGh0dHBzOi8vd3d3Lm15c3FsdHV0b3JpYWwub3JnL215c3FsLXN0b3JlZC1wcm9jZWR1cmUtdHV0b3JpYWwuYXNweCkKLSAgIFtNeVNRTCBTdG9yZWQgUHJvY2VkdXJlc10oaHR0cHM6Ly93d3cudzNyZXNvdXJjZS5jb20vbXlzcWwvbXlzcWwtcHJvY2VkdXJlLnBocCkKCiMjIEVycmF0YQoKW0xldCB1cyBrbm93XShodHRwczovL2Zvcm0uam90Zm9ybS5jb20vMjEyMTg3MDcyNzg0MTU3KXt0YXJnZXQ9Il9ibGFuayJ9Lgo=