From: “Getting and Cleaning Data”, week 2 - lesson 1 by Johns Hopkins University (Coursera)
This lecture’s about reading data from a MySQL database. MySQL’s one of the most widely used open source databases, and it’s widely used because it’s, partially because it’s free but also because it works really well. And it’s, again, widely used in Internet-based applications.
Data are structured in databases, and then the databases, within each database, there’s a series of tables. And then within the tables, there a series of fields. So, you can think of each table sort of as the data set, and each field as, say, one of the calms of that data set. So, each row in the database is called a record. If you want to know a little bit more about MySQL structure, you can look at the the Wikipedia page, or there’s quite extensive documentation at MySQL.com.
The first step in the using the MySQL package is actually installing MySQL. We’re actually going to be showing in this lecture, an example of a web-facing MySQL server that we’ll be able to access, butas a act of good citizenship to the world I would appreciate if you didn’t all go and try to access that one server because we’ll probably overwhelm them with the large number of people in the class.
A better example is to try to create MySQL database on your own system and then play around with it. So, the first thing that you need to do is install RMySQL with an R.
We’re going to be using a web-facing version of a MySQL database, so that we can just show how the R of MySQL package works, without having to install MySQL and go through the process of actually building that database.
The important point to keep in mind here is that as a data scientist what role that you will have is likely to collect data from a database, and maybe later you’re going to put some data back in it. But usually, the basic data collection has already been formed before you get there, so you usually be handed a database and trying, having to get data out of it.
This is information about the human genome that’s collected by University of California Santa Cruz. It’s one of the most famous databases for genomic. And so, they have a public facing MySQL page, sort of MySQL server. And so it gives you information here on how to connect. If you have MySQL installed on your computer, how to connect to their MySQL server. And so we’re going to use that information actually just connect with an R. And what we’re going to try to do is access the database and collect some information about a particular genome that we’re interested in.
Connecting and listing databases
Load RMySQL package in R
library(RMySQL)
You’re going to use the dbConnect() command to connect to a database, and so in this case, it’s a MySQL database. You can actually use this dbConnect() to actually connect to other kinds of databases as well.
The user that we’re going to pass it is “genome”.
The host we’re going to pass just where the MySQL server is. And in this case, “genome-mysql.cse.ucsc.edu”
So this opens a connection, and that connection is given this handle: ucscDb .
ucscDb <- dbConnect(MySQL(), user="genome", host="genome-mysql.cse.ucsc.edu")
* Now you can actually apply a query to that database. So, what you can do is you can use the dbGetQuery() command.
- So, what that does is it’ll go to the connection to ucscDb database. And it’ll run this MySQL command.
“show databases;” isn’t an R command; it’s actually a MySQL command that we’re sending to ucscDb database through the dbGetQuery() function.
- Then we use dbDisconnect(). This going to disconnect from the MySQL server. It’s very important that whenever you’re done analyzing data or collecting data from a MySQL server that you disconnect from that server.
And you should get a TRUE response. That true is coming from the dbDisconnect because it’s saying that we did, in fact, disconnect from the server.
result <- dbGetQuery(ucscDb, "show databases;"); dbDisconnect(ucscDb)
[1] TRUE
Then we look at result, what result will show is a list of all the databases that are available for that all the databases that are available within the MySQL server that is located at this particular host address: “genome-mysql.cse.ucsc.edu”.
result
Connecting to hg19 and listing tables
We’re going to actually focus on one in particular,and that’s hg19. Hg19 is a particular build of the human genome. So, it’s the 19th build of the human genome.
Let’s run dbConnect() command again, but instead of just passing it the user, we’ll also pass it the database (db). We’ll say, we’re going to, we want to access hg19 database within this “genome-mysql.cse.ucsc.edu” MySQL server.
hg19 <- dbConnect(MySQL(), user="genome", db="hg19", host="genome-mysql.cse.ucsc.edu")
And then what we might want to see what are the tables within that database. So remember, on a server, there might be multiple databases and within the database, they’ll be multiple tables. Each table corresponding to what you might think of as a data frame.
So, we can look at all the tables that exist in the hg19 database with dbListTables() command.
allTables <- dbListTables(hg19)
And if you look at the length of allTables, in this database, it’s very long. They’re a 111131 tables that are in that hg19 database.
length(allTables)
[1] 11131
So, if you look at the first, say, five tables, you get these tables here.
allTables[1:5]
[1] "HInv" "HInvGeneMrna" "acembly" "acemblyClass" "acemblyPep"
These are all different tables corresponding to various different elements that describe components of the human genome. Each table corresponds to a different kind of data set.
You can kind of think about it in the same way that the tidy data principle is. You thought about each data set corresponds to its own file. It’s the same way here; you get a, each different data type gets its own table.
Get dimensions of a specific table
Suppose we know what table that we’re interested in. So, our second argument “affyU133Plus2” is a particular kind of microarray, which is a measurement technology used to measure something about the genome.
So, you can actually say, okay, I want to look at this hg19 database within the MySQL server. And I want to know what are all the fields in this particular affyU133Plus2 table. We use them as arguments for dbListFields() command.
dbListFields(hg19, "affyU133Plus2")
[1] "bin" "matches" "misMatches" "repMatches" "nCount" "qNumInsert" "qBaseInsert"
[8] "tNumInsert" "tBaseInsert" "strand" "qName" "qSize" "qStart" "qEnd"
[15] "tName" "tSize" "tStart" "tEnd" "blockCount" "blockSizes" "qStarts"
[22] "tStarts"
Remember a table corresponds to something like a data frame. And the fields correspond to something like the column names that data frame. And so, you can see if we look at the fields here, there are things like bin, matches, misMatches, and so forth.
Now suppose we want to find out, how many different rows there are in this data set. So, we already know something about the columns, right? Because dbListFields() told us all the column names. There are 22 columns in hg19 data set or in affyU133Plus2 table.
Then, what we might want to know is how many rows it has. And so, what we’re going to do is send another query to the database. So, it’s dbGetQuery() again.
So again, we’re going to pass a, in quotes (“”) a MySQL command. This is a very special MySQL command that says, select count ALL from affyU133Plus2.
What it’s doing is it’s basically going to count all of the records in the table. And it’s going to return the number of records.
dbGetQuery(hg19, "select count(*) from affyU133Plus2")
Read from the table
Suppose you want to get one of the tables out. You can basically get the data frame out from the data set. You can do it using dbReadTable.
We pass a database that we’re interested in getting the table from (hg19), and the table name that we’re interested in getting the data from (affyU133Plus2).
What that’ll return is the data frame itself.
affyData <- dbReadTable(hg19, "affyU133Plus2")
head(affyData)
So, you can see this is the data that’s actually been extracted nowfrom that MySQL server, from that particular database (hg19) and that particular table (affyU133Plus2). You can extract the data one table at a time.
Select a specific subset
One important thing to keep in mind is that often if I use MySQL databases, there’ll be a huge amount of data stored. Any particular table might be gigantic and might be too big to read into R.
So, one thing that you might want to do is select only a subset of the data. So, you can do that with the dbSendQuery() command.
What you do is call dbSendQuery() command, and you give it the database (hg19). And then, we’re going to send it a different MySQL command.
The MySQL command here is select star (all), so it’s going to select all the different observations from this affyU133plus2 table, where the misMatches variable is between one and three.
Then we send this query to the database. And now, that’s stored remotely at the database.
query <- dbSendQuery(hg19, "select * from affyU133Plus2 where misMatches between 1 and 3")
If we want to fetch the results of that, we use the fetch() command like this. And that’ll return this affyMis, which will give us some information about only the samples that we’ve selected using this database query.
affyMis <- fetch(query)
affyMis
Remember, we selected the misMatches between one and three, so you can see that the quantiles reflect that we only had data for which the misMatches are between one and three.
quantile(affyMis$misMatches)
0% 25% 50% 75% 100%
1 1 2 2 3
Other thing that you can do is, you can actually just fetch. Remember, when you used the **SendQuery()* command, it sent it to the database, but it didn’t try to suck the data back to your personal computer yet.
Suppose you just want to see a little bit of the data and make sure that you don’t accidentally suck down a gigantic table, you can use the fetch command, and you can tell it only bring back, say, the top 10 records. And so, what you’ve got now is just a very small data set that only contains the first 10 rows of that table.
affyMisSmall <- fetch(query, n=10)
affyMisSmall
When you do that, you need to clear the query. Remember you sent a query out to the MySQL server, and now, it’s still sitting out there. And you fetch the data back, but it didn’t stop that query from still being out there, at the MySQL Server. So, you need to do dbClearResult() to re-clear that query from the remote server, and it should return TRUE again because you’ve cleared that result.
dbClearResult(query)
[1] TRUE
If you look at the small data set where we fetched only the top 10 rows or observations, you can see that that produces a data frame that actually has a dimension of 10 rows as well. What we’ve done is select a very specific subset.
dim(affyMisSmall)
[1] 10 22
An important point here is that you can basically send any MySQL query that you would like within the quotes. So, it’s a little bit beyond the scope of this class to teach you all of the different queries. The most important ones are the queries that I’ve shown you about selecting, the sort of the total count and selecting all of the observations that are subject to some particular conditions.
But if you go to the MySQL documentation, you can actually come up with a query that will allow you to very flexibly select almost any sort of subset of rows or columns from a data set that you’re actually interested in selecting.
Don’t forget to close the connection!
Remember to close the connection. So, this is one of the most common mistakes when using accessing a database from R, is that you open a connection; you select some data out; you go happily on your way. You should try to close the connection as soon as you don’t need the connection anymore. So, immediately after extracting out the data that you’re interested in, you should close the connection and move on with the rest of your script.
dbDisconnect(hg19)
[1] TRUE
Further resources
- List of commands: http://www.pantz.org/software/mysql/mysqlcommands.html
- Do not, do not, delete, add or join things from ensembl. Only select.
- In general be careful with mySQL commands
There’s very nice collection of MySQL commands here, very nicely organized that will give you a lot more information if you want to be able to use them.
One thing to keep in mind is that I have shown an example here about public spacing MySQL server. What I’ve done primarily for this analysis is select data from that server. Please, please do not acces that server to delete, add or join things. In other words, don’t push anything back into the server, only sub things or select things out of the server. So, you should only be using the select command.
Another important component is that we again, are a large class. So, if you all hit that server at the same time, it will likely cause problems for them. It’s much better to practice on your own local version of MySQL. In careful, in general, you need to be a little bit careful with MySQL commands because you can delete data that other people are working on.
A nice blog post summarizing some other commands: http://www.r-bloggers.com/mysql-and-r/
A nice blog summarizing some other commands that are used with MySQL and R is right here, and so if you want to take a look and learn a lot more, there’s a ton more that can be learned there, and R and MySQL play very nicely together if you want to get data out.
END
LS0tCnRpdGxlOiAiUmVhZGluZyBEYXRhIGZyb20gTXlTUUwiCmF1dGhvcjogSmVmZnJleSBMZWVrCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMjIyMgRnJvbTogIkdldHRpbmcgYW5kIENsZWFuaW5nIERhdGEiLCB3ZWVrIDIgLSBsZXNzb24gMSBieSBKb2hucyBIb3BraW5zIFVuaXZlcnNpdHkgKENvdXJzZXJhKQoKPGJyIC8+CgpUaGlzIGxlY3R1cmUncyBhYm91dCByZWFkaW5nIGRhdGEgZnJvbSBhIE15U1FMIGRhdGFiYXNlLiBNeVNRTCdzIG9uZSBvZiB0aGUgbW9zdCB3aWRlbHkgdXNlZCBvcGVuIHNvdXJjZSBkYXRhYmFzZXMsIGFuZCBpdCdzIHdpZGVseSB1c2VkIGJlY2F1c2UgaXQncywgcGFydGlhbGx5IGJlY2F1c2UgaXQncyBmcmVlIGJ1dCBhbHNvIGJlY2F1c2UgaXQgd29ya3MgcmVhbGx5IHdlbGwuIEFuZCBpdCdzLCBhZ2Fpbiwgd2lkZWx5IHVzZWQgaW4gSW50ZXJuZXQtYmFzZWQgYXBwbGljYXRpb25zLgoKRGF0YSBhcmUgc3RydWN0dXJlZCBpbiBkYXRhYmFzZXMsIGFuZCB0aGVuIHRoZSBkYXRhYmFzZXMsIHdpdGhpbiBlYWNoIGRhdGFiYXNlLCB0aGVyZSdzIGEgc2VyaWVzIG9mIHRhYmxlcy4gQW5kIHRoZW4gd2l0aGluIHRoZSB0YWJsZXMsIHRoZXJlIGEgc2VyaWVzIG9mIGZpZWxkcy4gU28sIHlvdSBjYW4gdGhpbmsgb2YgZWFjaCB0YWJsZSBzb3J0IG9mIGFzIHRoZSBkYXRhIHNldCwgYW5kIGVhY2ggZmllbGQgYXMsIHNheSwgb25lIG9mIHRoZSBjYWxtcyBvZiB0aGF0IGRhdGEgc2V0LiBTbywgZWFjaCByb3cgaW4gdGhlIGRhdGFiYXNlIGlzIGNhbGxlZCBhIHJlY29yZC4gSWYgeW91IHdhbnQgdG8ga25vdyBhIGxpdHRsZSBiaXQgbW9yZSBhYm91dCBNeVNRTCBzdHJ1Y3R1cmUsIHlvdSBjYW4gbG9vayBhdCB0aGUgdGhlIFdpa2lwZWRpYSBwYWdlLCBvciB0aGVyZSdzIHF1aXRlIGV4dGVuc2l2ZSBkb2N1bWVudGF0aW9uIGF0IE15U1FMLmNvbS4KClRoZSBmaXJzdCBzdGVwIGluIHRoZSB1c2luZyB0aGUgTXlTUUwgcGFja2FnZSBpcyBhY3R1YWxseSBpbnN0YWxsaW5nIE15U1FMLiBXZSdyZSBhY3R1YWxseSBnb2luZyB0byBiZSBzaG93aW5nIGluIHRoaXMgbGVjdHVyZSwgYW4gZXhhbXBsZSBvZiBhIHdlYi1mYWNpbmcgTXlTUUwgc2VydmVyIHRoYXQgd2UnbGwgYmUgYWJsZSB0byBhY2Nlc3MsIGJ1dGFzIGEgYWN0IG9mIGdvb2QgY2l0aXplbnNoaXAgdG8gdGhlIHdvcmxkIEkgd291bGQgYXBwcmVjaWF0ZSBpZiB5b3UgZGlkbid0IGFsbCBnbyBhbmQgdHJ5IHRvIGFjY2VzcyB0aGF0IG9uZSBzZXJ2ZXIgYmVjYXVzZSB3ZSdsbCBwcm9iYWJseSBvdmVyd2hlbG0gdGhlbSB3aXRoIHRoZSBsYXJnZSBudW1iZXIgb2YgcGVvcGxlIGluIHRoZSBjbGFzcy4KCkEgYmV0dGVyIGV4YW1wbGUgaXMgdG8gdHJ5IHRvIGNyZWF0ZSBNeVNRTCBkYXRhYmFzZSBvbiB5b3VyIG93biBzeXN0ZW0gYW5kIHRoZW4gcGxheSBhcm91bmQgd2l0aCBpdC4gU28sIHRoZSBmaXJzdCB0aGluZyB0aGF0IHlvdSBuZWVkIHRvIGRvIGlzIGluc3RhbGwgUk15U1FMIHdpdGggYW4gUi4KCldlJ3JlIGdvaW5nIHRvIGJlIHVzaW5nIGEgd2ViLWZhY2luZyB2ZXJzaW9uIG9mIGEgTXlTUUwgZGF0YWJhc2UsIHNvIHRoYXQgd2UgY2FuIGp1c3Qgc2hvdyBob3cgdGhlIFIgb2YgTXlTUUwgcGFja2FnZSB3b3Jrcywgd2l0aG91dCBoYXZpbmcgdG8gaW5zdGFsbCBNeVNRTCBhbmQgZ28gdGhyb3VnaCB0aGUgcHJvY2VzcyBvZiBhY3R1YWxseSBidWlsZGluZyB0aGF0IGRhdGFiYXNlLiAKClRoZSBpbXBvcnRhbnQgcG9pbnQgdG8ga2VlcCBpbiBtaW5kIGhlcmUgaXMgdGhhdCBhcyBhIGRhdGEgc2NpZW50aXN0IHdoYXQgcm9sZSB0aGF0IHlvdSB3aWxsIGhhdmUgaXMgbGlrZWx5IHRvIGNvbGxlY3QgZGF0YSBmcm9tIGEgZGF0YWJhc2UsIGFuZCBtYXliZSBsYXRlciB5b3UncmUgZ29pbmcgdG8gcHV0IHNvbWUgZGF0YSBiYWNrIGluIGl0LiBCdXQgdXN1YWxseSwgdGhlIGJhc2ljIGRhdGEgY29sbGVjdGlvbiBoYXMgYWxyZWFkeSBiZWVuIGZvcm1lZCBiZWZvcmUgeW91IGdldCB0aGVyZSwgc28geW91IHVzdWFsbHkgYmUgaGFuZGVkIGEgZGF0YWJhc2UgYW5kIHRyeWluZywgaGF2aW5nIHRvIGdldCBkYXRhIG91dCBvZiBpdC4KClRoaXMgaXMgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGh1bWFuIGdlbm9tZSB0aGF0J3MgY29sbGVjdGVkIGJ5IFVuaXZlcnNpdHkgb2YgQ2FsaWZvcm5pYSBTYW50YSBDcnV6LiBJdCdzIG9uZSBvZiB0aGUgbW9zdCBmYW1vdXMgZGF0YWJhc2VzIGZvciBnZW5vbWljLiBBbmQgc28sIHRoZXkgaGF2ZSBhIHB1YmxpYyBmYWNpbmcgTXlTUUwgcGFnZSwgc29ydCBvZiBNeVNRTCBzZXJ2ZXIuIEFuZCBzbyBpdCBnaXZlcyB5b3UgaW5mb3JtYXRpb24gaGVyZSBvbiBob3cgdG8gY29ubmVjdC4gSWYgeW91IGhhdmUgTXlTUUwgaW5zdGFsbGVkIG9uIHlvdXIgY29tcHV0ZXIsIGhvdyB0byBjb25uZWN0IHRvIHRoZWlyIE15U1FMIHNlcnZlci4gQW5kIHNvIHdlJ3JlIGdvaW5nIHRvIHVzZSB0aGF0IGluZm9ybWF0aW9uIGFjdHVhbGx5IGp1c3QgY29ubmVjdCB3aXRoIGFuIFIuIEFuZCB3aGF0IHdlJ3JlIGdvaW5nIHRvIHRyeSB0byBkbyBpcyBhY2Nlc3MgdGhlIGRhdGFiYXNlIGFuZCBjb2xsZWN0IHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgYSBwYXJ0aWN1bGFyIGdlbm9tZSB0aGF0IHdlJ3JlIGludGVyZXN0ZWQgaW4uCgo8YnIgXD4KCi0tLQoKPGJyIFw+CgojIyMgQ29ubmVjdGluZyBhbmQgbGlzdGluZyBkYXRhYmFzZXMKCjxiciBcPgpMb2FkICoqUk15U1FMKiogcGFja2FnZSBpbiBSCmBgYHtyfQpsaWJyYXJ5KFJNeVNRTCkKYGBgCgo8YnIgXD4KWW91J3JlIGdvaW5nIHRvIHVzZSB0aGUgKipkYkNvbm5lY3QoKSoqIGNvbW1hbmQgdG8gY29ubmVjdCB0byBhIGRhdGFiYXNlLCBhbmQgc28gaW4gdGhpcyBjYXNlLCBpdCdzIGEgTXlTUUwgZGF0YWJhc2UuIFlvdSBjYW4gYWN0dWFsbHkgdXNlIHRoaXMgKipkYkNvbm5lY3QoKSoqIHRvIGFjdHVhbGx5IGNvbm5lY3QgdG8gb3RoZXIga2luZHMgb2YgZGF0YWJhc2VzIGFzIHdlbGwuIAoKVGhlICoqdXNlcioqIHRoYXQgd2UncmUgZ29pbmcgdG8gcGFzcyBpdCBpcyAqImdlbm9tZSIqLgoKVGhlICoqaG9zdCoqIHdlJ3JlIGdvaW5nIHRvIHBhc3MganVzdCB3aGVyZSB0aGUgTXlTUUwgc2VydmVyIGlzLiBBbmQgaW4gdGhpcyBjYXNlLCAqImdlbm9tZS1teXNxbC5jc2UudWNzYy5lZHUiKgoKU28gdGhpcyBvcGVucyBhIGNvbm5lY3Rpb24sIGFuZCB0aGF0IGNvbm5lY3Rpb24gaXMgZ2l2ZW4gdGhpcyBoYW5kbGU6ICoqdWNzY0RiICoqLgoKCmBgYHtyfQp1Y3NjRGIgPC0gZGJDb25uZWN0KE15U1FMKCksIHVzZXI9Imdlbm9tZSIsIGhvc3Q9Imdlbm9tZS1teXNxbC5jc2UudWNzYy5lZHUiKQpgYGAKCjxiciBcPgoqIE5vdyB5b3UgY2FuIGFjdHVhbGx5IGFwcGx5IGEgcXVlcnkgdG8gdGhhdCBkYXRhYmFzZS4gU28sIHdoYXQgeW91IGNhbiBkbyBpcyB5b3UgY2FuIHVzZSB0aGUgKipkYkdldFF1ZXJ5KCkqKiBjb21tYW5kLgoKKiBTbywgd2hhdCB0aGF0IGRvZXMgaXMgaXQnbGwgZ28gdG8gdGhlIGNvbm5lY3Rpb24gdG8gKip1Y3NjRGIqKiBkYXRhYmFzZS4gQW5kIGl0J2xsIHJ1biB0aGlzIE15U1FMIGNvbW1hbmQuCiogKioic2hvdyBkYXRhYmFzZXM7IioqIGlzbid0IGFuIFIgY29tbWFuZDsgaXQncyBhY3R1YWxseSBhIE15U1FMIGNvbW1hbmQgdGhhdCB3ZSdyZSBzZW5kaW5nIHRvICoqdWNzY0RiKiogIGRhdGFiYXNlIHRocm91Z2ggdGhlICoqZGJHZXRRdWVyeSgpKiogZnVuY3Rpb24uCgoqIFRoZW4gd2UgdXNlICoqZGJEaXNjb25uZWN0KCkqKi4gVGhpcyBnb2luZyB0byBkaXNjb25uZWN0IGZyb20gdGhlIE15U1FMIHNlcnZlci4gSXQncyB2ZXJ5IGltcG9ydGFudCB0aGF0IHdoZW5ldmVyIHlvdSdyZSBkb25lIGFuYWx5emluZyBkYXRhIG9yIGNvbGxlY3RpbmcgZGF0YSBmcm9tIGEgTXlTUUwgc2VydmVyIHRoYXQgeW91IGRpc2Nvbm5lY3QgZnJvbSB0aGF0IHNlcnZlci4gCiogQW5kIHlvdSBzaG91bGQgZ2V0IGEgVFJVRSByZXNwb25zZS4gVGhhdCB0cnVlIGlzIGNvbWluZyBmcm9tIHRoZSBkYkRpc2Nvbm5lY3QgYmVjYXVzZSBpdCdzIHNheWluZyB0aGF0IHdlIGRpZCwgaW4gZmFjdCwgZGlzY29ubmVjdCBmcm9tIHRoZSBzZXJ2ZXIuCmBgYHtyfQpyZXN1bHQgPC0gZGJHZXRRdWVyeSh1Y3NjRGIsICJzaG93IGRhdGFiYXNlczsiKTsgZGJEaXNjb25uZWN0KHVjc2NEYikKYGBgCgo8YnIgXD4KVGhlbiB3ZSBsb29rIGF0ICoqcmVzdWx0KiosIHdoYXQgcmVzdWx0IHdpbGwgc2hvdyBpcyBhIGxpc3Qgb2YgYWxsIHRoZSBkYXRhYmFzZXMgdGhhdCBhcmUgYXZhaWxhYmxlIGZvciB0aGF0IGFsbCB0aGUgZGF0YWJhc2VzIHRoYXQgYXJlIGF2YWlsYWJsZSB3aXRoaW4gdGhlIE15U1FMIHNlcnZlciB0aGF0IGlzIGxvY2F0ZWQgYXQgdGhpcyBwYXJ0aWN1bGFyIGhvc3QgYWRkcmVzczogKiJnZW5vbWUtbXlzcWwuY3NlLnVjc2MuZWR1IiouCgpgYGB7cn0KcmVzdWx0CmBgYAoKPGJyIFw+CgotLS0KCjxiciBcPgoKIyMjIENvbm5lY3RpbmcgdG8gaGcxOSBhbmQgbGlzdGluZyB0YWJsZXMKCjxiciBcPgpXZSdyZSBnb2luZyB0byBhY3R1YWxseSBmb2N1cyBvbiBvbmUgaW4gcGFydGljdWxhcixhbmQgdGhhdCdzICoqaGcxOSoqLiAqKkhnMTkqKiBpcyBhIHBhcnRpY3VsYXIgYnVpbGQgb2YgdGhlIGh1bWFuIGdlbm9tZS4gU28sIGl0J3MgdGhlIDE5dGggYnVpbGQgb2YgdGhlIGh1bWFuIGdlbm9tZS4KCkxldCdzIHJ1biAqKmRiQ29ubmVjdCgpKiogY29tbWFuZCBhZ2FpbiwgYnV0IGluc3RlYWQgb2YganVzdCBwYXNzaW5nIGl0IHRoZSAqKnVzZXIqKiwgd2UnbGwgYWxzbyBwYXNzIGl0IHRoZSBkYXRhYmFzZSAoKipkYioqKS4gV2UnbGwgc2F5LCB3ZSdyZSBnb2luZyB0bywgd2Ugd2FudCB0byBhY2Nlc3MgKmhnMTkqIGRhdGFiYXNlIHdpdGhpbiB0aGlzICoiZ2Vub21lLW15c3FsLmNzZS51Y3NjLmVkdSIqIE15U1FMIHNlcnZlci4KCmBgYHtyfQpoZzE5IDwtIGRiQ29ubmVjdChNeVNRTCgpLCB1c2VyPSJnZW5vbWUiLCBkYj0iaGcxOSIsIGhvc3Q9Imdlbm9tZS1teXNxbC5jc2UudWNzYy5lZHUiKQpgYGAKCjxiciBcPgpBbmQgdGhlbiB3aGF0IHdlIG1pZ2h0IHdhbnQgdG8gc2VlIHdoYXQgYXJlIHRoZSB0YWJsZXMgd2l0aGluIHRoYXQgZGF0YWJhc2UuIFNvIHJlbWVtYmVyLCBvbiBhIHNlcnZlciwgdGhlcmUgbWlnaHQgYmUgbXVsdGlwbGUgZGF0YWJhc2VzIGFuZCB3aXRoaW4gdGhlIGRhdGFiYXNlLCB0aGV5J2xsIGJlIG11bHRpcGxlIHRhYmxlcy4gRWFjaCB0YWJsZSBjb3JyZXNwb25kaW5nIHRvIHdoYXQgeW91IG1pZ2h0IHRoaW5rIG9mIGFzIGEgKmRhdGEgZnJhbWUqLgoKU28sIHdlIGNhbiBsb29rIGF0IGFsbCB0aGUgdGFibGVzIHRoYXQgZXhpc3QgaW4gdGhlICpoZzE5KiBkYXRhYmFzZSB3aXRoICoqZGJMaXN0VGFibGVzKCkqKiBjb21tYW5kLiAKCmBgYHtyfQphbGxUYWJsZXMgPC0gZGJMaXN0VGFibGVzKGhnMTkpCmBgYAoKPGJyIFw+CkFuZCBpZiB5b3UgbG9vayBhdCB0aGUgKipsZW5ndGgqKiBvZiAqKmFsbFRhYmxlcyoqLCBpbiB0aGlzIGRhdGFiYXNlLCBpdCdzIHZlcnkgbG9uZy4gVGhleSdyZSBhIDExMTEzMSB0YWJsZXMgdGhhdCBhcmUgaW4gdGhhdCAqaGcxOSogZGF0YWJhc2UuCgpgYGB7cn0KbGVuZ3RoKGFsbFRhYmxlcykKYGBgCgo8YnIgXD4KU28sIGlmIHlvdSBsb29rIGF0IHRoZSBmaXJzdCwgc2F5LCBmaXZlIHRhYmxlcywgeW91IGdldCB0aGVzZSB0YWJsZXMgaGVyZS4KCmBgYHtyfQphbGxUYWJsZXNbMTo1XQpgYGAKCjxiciBcPgpUaGVzZSBhcmUgYWxsIGRpZmZlcmVudCB0YWJsZXMgY29ycmVzcG9uZGluZyB0byB2YXJpb3VzIGRpZmZlcmVudCBlbGVtZW50cyB0aGF0IGRlc2NyaWJlIGNvbXBvbmVudHMgb2YgdGhlIGh1bWFuIGdlbm9tZS4gRWFjaCB0YWJsZSBjb3JyZXNwb25kcyB0byBhIGRpZmZlcmVudCBraW5kIG9mIGRhdGEgc2V0LiAKCllvdSBjYW4ga2luZCBvZiB0aGluayBhYm91dCBpdCBpbiB0aGUgc2FtZSB3YXkgdGhhdCB0aGUgdGlkeSBkYXRhIHByaW5jaXBsZSBpcy4gWW91IHRob3VnaHQgYWJvdXQgZWFjaCBkYXRhIHNldCBjb3JyZXNwb25kcyB0byBpdHMgb3duIGZpbGUuIEl0J3MgdGhlIHNhbWUgd2F5IGhlcmU7IHlvdSBnZXQgYSwgZWFjaCBkaWZmZXJlbnQgZGF0YSB0eXBlIGdldHMgaXRzIG93biB0YWJsZS4KCjxiciBcPgoKLS0tCgo8YnIgXD4KCiMjIyBHZXQgZGltZW5zaW9ucyBvZiBhIHNwZWNpZmljIHRhYmxlCgo8YnIgXD4KU3VwcG9zZSB3ZSBrbm93IHdoYXQgdGFibGUgdGhhdCB3ZSdyZSBpbnRlcmVzdGVkIGluLiBTbywgb3VyIHNlY29uZCBhcmd1bWVudCAqImFmZnlVMTMzUGx1czIiKiBpcyBhIHBhcnRpY3VsYXIga2luZCBvZiBtaWNyb2FycmF5LCB3aGljaCBpcyBhIG1lYXN1cmVtZW50IHRlY2hub2xvZ3kgdXNlZCB0byBtZWFzdXJlIHNvbWV0aGluZyBhYm91dCB0aGUgZ2Vub21lLiAKClNvLCB5b3UgY2FuIGFjdHVhbGx5IHNheSwgb2theSwgSSB3YW50IHRvIGxvb2sgYXQgdGhpcyAqaGcxOSogZGF0YWJhc2Ugd2l0aGluIHRoZSBNeVNRTCBzZXJ2ZXIuIEFuZCBJIHdhbnQgdG8ga25vdyB3aGF0IGFyZSBhbGwgdGhlIGZpZWxkcyBpbiB0aGlzIHBhcnRpY3VsYXIgKmFmZnlVMTMzUGx1czIqIHRhYmxlLiBXZSB1c2UgdGhlbSBhcyBhcmd1bWVudHMgZm9yICoqZGJMaXN0RmllbGRzKCkqKiBjb21tYW5kLgoKYGBge3J9CmRiTGlzdEZpZWxkcyhoZzE5LCAiYWZmeVUxMzNQbHVzMiIpCmBgYAoKPGJyIFw+ClJlbWVtYmVyIGEgdGFibGUgY29ycmVzcG9uZHMgdG8gc29tZXRoaW5nIGxpa2UgYSBkYXRhIGZyYW1lLiBBbmQgdGhlIGZpZWxkcyBjb3JyZXNwb25kIHRvIHNvbWV0aGluZyBsaWtlIHRoZSBjb2x1bW4gbmFtZXMgdGhhdCBkYXRhIGZyYW1lLiBBbmQgc28sIHlvdSBjYW4gc2VlIGlmIHdlIGxvb2sgYXQgdGhlIGZpZWxkcyBoZXJlLCB0aGVyZSBhcmUgdGhpbmdzIGxpa2UgKmJpbiwgbWF0Y2hlcywgbWlzTWF0Y2hlcyosIGFuZCBzbyBmb3J0aC4KCk5vdyBzdXBwb3NlIHdlIHdhbnQgdG8gZmluZCBvdXQsIGhvdyBtYW55IGRpZmZlcmVudCByb3dzIHRoZXJlIGFyZSBpbiB0aGlzIGRhdGEgc2V0LiBTbywgd2UgYWxyZWFkeSBrbm93IHNvbWV0aGluZyBhYm91dCB0aGUgY29sdW1ucywgcmlnaHQ/IEJlY2F1c2UgKipkYkxpc3RGaWVsZHMoKSoqIHRvbGQgdXMgYWxsIHRoZSBjb2x1bW4gbmFtZXMuIFRoZXJlIGFyZSAyMiBjb2x1bW5zIGluICpoZzE5KiBkYXRhIHNldCBvciBpbiAqYWZmeVUxMzNQbHVzMiogdGFibGUuCgpUaGVuLCB3aGF0IHdlIG1pZ2h0IHdhbnQgdG8ga25vdyBpcyBob3cgbWFueSByb3dzIGl0IGhhcy4gQW5kIHNvLCB3aGF0IHdlJ3JlIGdvaW5nIHRvIGRvIGlzIHNlbmQKYW5vdGhlciBxdWVyeSB0byB0aGUgZGF0YWJhc2UuIFNvLCBpdCdzICoqZGJHZXRRdWVyeSgpKiogYWdhaW4uCgpTbyBhZ2Fpbiwgd2UncmUgZ29pbmcgdG8gcGFzcyBhLCBpbiBxdW90ZXMgKCIiKSBhIE15U1FMIGNvbW1hbmQuIFRoaXMgaXMgYSB2ZXJ5IHNwZWNpYWwgTXlTUUwgY29tbWFuZCB0aGF0IHNheXMsICpzZWxlY3QgY291bnQgQUxMIGZyb20gYWZmeVUxMzNQbHVzMiouCgpXaGF0IGl0J3MgZG9pbmcgaXMgaXQncyBiYXNpY2FsbHkgZ29pbmcgdG8gY291bnQgYWxsIG9mIHRoZSByZWNvcmRzIGluIHRoZSB0YWJsZS4gQW5kIGl0J3MgZ29pbmcgdG8gcmV0dXJuIHRoZSBudW1iZXIgb2YgcmVjb3Jkcy4KCmBgYHtyfQpkYkdldFF1ZXJ5KGhnMTksICJzZWxlY3QgY291bnQoKikgZnJvbSBhZmZ5VTEzM1BsdXMyIikKYGBgCgo8YnIgXD4KCi0tLQoKPGJyIFw+CgojIyMgUmVhZCBmcm9tIHRoZSB0YWJsZQoKPGJyIFw+ClN1cHBvc2UgeW91IHdhbnQgdG8gZ2V0IG9uZSBvZiB0aGUgdGFibGVzIG91dC4gWW91IGNhbiBiYXNpY2FsbHkgZ2V0IHRoZSBkYXRhIGZyYW1lIG91dCBmcm9tIHRoZSBkYXRhIHNldC4gWW91IGNhbiBkbyBpdCB1c2luZyAqKmRiUmVhZFRhYmxlKiouCgpXZSBwYXNzIGEgKmRhdGFiYXNlKiB0aGF0IHdlJ3JlIGludGVyZXN0ZWQgaW4gZ2V0dGluZyB0aGUgdGFibGUgZnJvbSAoKmhnMTkqKSwKYW5kIHRoZSAqdGFibGUqIG5hbWUgdGhhdCB3ZSdyZSBpbnRlcmVzdGVkIGluIGdldHRpbmcgdGhlIGRhdGEgZnJvbSAoKmFmZnlVMTMzUGx1czIqKS4KCldoYXQgdGhhdCdsbCByZXR1cm4gaXMgdGhlIGRhdGEgZnJhbWUgaXRzZWxmLgoKYGBge3J9CmFmZnlEYXRhIDwtIGRiUmVhZFRhYmxlKGhnMTksICJhZmZ5VTEzM1BsdXMyIikKYGBgCmBgYHtyfQpoZWFkKGFmZnlEYXRhKQpgYGAKCjxiciBcPgpTbywgeW91IGNhbiBzZWUgdGhpcyBpcyB0aGUgZGF0YSB0aGF0J3MgYWN0dWFsbHkgYmVlbiBleHRyYWN0ZWQgbm93ZnJvbSB0aGF0IE15U1FMIHNlcnZlciwgZnJvbSB0aGF0IHBhcnRpY3VsYXIgZGF0YWJhc2UgKCpoZzE5KikgYW5kIHRoYXQgcGFydGljdWxhciB0YWJsZSAoKmFmZnlVMTMzUGx1czIqKS4gWW91IGNhbiBleHRyYWN0IHRoZSBkYXRhIG9uZSB0YWJsZSBhdAphIHRpbWUuCgo8YnIgXD4KCi0tLQoKPGJyIFw+CgojIyMgU2VsZWN0IGEgc3BlY2lmaWMgc3Vic2V0Cgo8YnIgXD4KT25lIGltcG9ydGFudCB0aGluZyB0byBrZWVwIGluIG1pbmQgaXMgdGhhdCBvZnRlbiBpZiBJIHVzZSBNeVNRTCBkYXRhYmFzZXMsIHRoZXJlJ2xsIGJlIGEgaHVnZSBhbW91bnQgb2YgZGF0YSBzdG9yZWQuIEFueSBwYXJ0aWN1bGFyIHRhYmxlIG1pZ2h0IGJlIGdpZ2FudGljIGFuZCBtaWdodCBiZSB0b28gYmlnIHRvIHJlYWQgaW50byBSLgoKU28sIG9uZSB0aGluZyB0aGF0IHlvdSBtaWdodCB3YW50IHRvIGRvIGlzIHNlbGVjdCBvbmx5IGEgc3Vic2V0IG9mIHRoZSBkYXRhLiBTbywgeW91IGNhbiBkbyB0aGF0IHdpdGggdGhlICoqZGJTZW5kUXVlcnkoKSoqIGNvbW1hbmQuCgpXaGF0IHlvdSBkbyBpcyBjYWxsICoqZGJTZW5kUXVlcnkoKSoqIGNvbW1hbmQsIGFuZCB5b3UgZ2l2ZSBpdCB0aGUgZGF0YWJhc2UgKCpoZzE5KikuIEFuZCB0aGVuLCB3ZSdyZSBnb2luZyB0byBzZW5kIGl0IGEgZGlmZmVyZW50IE15U1FMIGNvbW1hbmQuCgpUaGUgTXlTUUwgY29tbWFuZCBoZXJlIGlzICpzZWxlY3Qgc3RhciAoYWxsKSosIHNvIGl0J3MgZ29pbmcgdG8gc2VsZWN0IGFsbCB0aGUgZGlmZmVyZW50IG9ic2VydmF0aW9ucyBmcm9tIHRoaXMgKmFmZnlVMTMzcGx1czIqIHRhYmxlLCAqd2hlcmUgdGhlKiAqKm1pc01hdGNoZXMqKiAqdmFyaWFibGUgaXMgYmV0d2VlbiBvbmUgYW5kIHRocmVlKi4gCgpUaGVuIHdlIHNlbmQgdGhpcyBxdWVyeSB0byB0aGUgZGF0YWJhc2UuIEFuZCBub3csIHRoYXQncyBzdG9yZWQgcmVtb3RlbHkgYXQgdGhlIGRhdGFiYXNlLgoKYGBge3J9CnF1ZXJ5IDwtIGRiU2VuZFF1ZXJ5KGhnMTksICJzZWxlY3QgKiBmcm9tIGFmZnlVMTMzUGx1czIgd2hlcmUgbWlzTWF0Y2hlcyBiZXR3ZWVuIDEgYW5kIDMiKSAKYGBgCgo8YnIgXD4KSWYgd2Ugd2FudCB0byBmZXRjaCB0aGUgcmVzdWx0cyBvZiB0aGF0LCB3ZSB1c2UgdGhlICoqZmV0Y2goKSoqIGNvbW1hbmQgbGlrZSB0aGlzLiBBbmQgdGhhdCdsbCByZXR1cm4gdGhpcyAqKmFmZnlNaXMqKiwgd2hpY2ggd2lsbCBnaXZlIHVzIHNvbWUgaW5mb3JtYXRpb24gYWJvdXQgb25seSB0aGUgc2FtcGxlcyB0aGF0IHdlJ3ZlIHNlbGVjdGVkIHVzaW5nIHRoaXMgZGF0YWJhc2UgcXVlcnkuCgpgYGB7cn0KYWZmeU1pcyA8LSBmZXRjaChxdWVyeSkKYWZmeU1pcwpgYGAKCjxiciBcPgpSZW1lbWJlciwgd2Ugc2VsZWN0ZWQgdGhlICptaXNNYXRjaGVzKiBiZXR3ZWVuIG9uZSBhbmQgdGhyZWUsIHNvIHlvdSBjYW4gc2VlIHRoYXQgdGhlIHF1YW50aWxlcyByZWZsZWN0IHRoYXQgd2Ugb25seSBoYWQgZGF0YSBmb3Igd2hpY2ggdGhlICptaXNNYXRjaGVzKiBhcmUgYmV0d2VlbiBvbmUgYW5kIHRocmVlLgoKYGBge3J9CnF1YW50aWxlKGFmZnlNaXMkbWlzTWF0Y2hlcykKYGBgCgo8YnIgXD4KT3RoZXIgdGhpbmcgdGhhdCB5b3UgY2FuIGRvIGlzLCB5b3UgY2FuIGFjdHVhbGx5IGp1c3QgZmV0Y2guIFJlbWVtYmVyLCB3aGVuIHlvdSB1c2VkIHRoZSAqKlNlbmRRdWVyeSgpKiBjb21tYW5kLCBpdCBzZW50IGl0IHRvIHRoZSBkYXRhYmFzZSwgYnV0IGl0IGRpZG4ndCB0cnkgdG8gc3VjayB0aGUgZGF0YSBiYWNrIHRvIHlvdXIgcGVyc29uYWwgY29tcHV0ZXIgeWV0LgoKU3VwcG9zZSB5b3UganVzdCB3YW50IHRvIHNlZSBhIGxpdHRsZSBiaXQgb2YgdGhlIGRhdGEgYW5kIG1ha2Ugc3VyZSB0aGF0IHlvdSBkb24ndCBhY2NpZGVudGFsbHkgc3VjayBkb3duIGEgZ2lnYW50aWMgdGFibGUsIHlvdSBjYW4gdXNlIHRoZSBmZXRjaCBjb21tYW5kLCBhbmQgeW91IGNhbiB0ZWxsIGl0IG9ubHkgYnJpbmcgYmFjaywgc2F5LCB0aGUgdG9wIDEwIHJlY29yZHMuIEFuZCBzbywgd2hhdCB5b3UndmUgZ290IG5vdyBpcyBqdXN0IGEgdmVyeSBzbWFsbCBkYXRhIHNldCB0aGF0IG9ubHkgY29udGFpbnMgdGhlIGZpcnN0IDEwIHJvd3Mgb2YgdGhhdCB0YWJsZS4KCmBgYHtyfQphZmZ5TWlzU21hbGwgPC0gZmV0Y2gocXVlcnksIG49MTApCmFmZnlNaXNTbWFsbApgYGAKCjxiciBcPgpXaGVuIHlvdSBkbyB0aGF0LCB5b3UgbmVlZCB0byBjbGVhciB0aGUgcXVlcnkuIFJlbWVtYmVyIHlvdSBzZW50IGEgcXVlcnkgb3V0IHRvIHRoZSBNeVNRTCBzZXJ2ZXIsIGFuZCBub3csIGl0J3Mgc3RpbGwgc2l0dGluZyBvdXQgdGhlcmUuIEFuZCB5b3UgZmV0Y2ggdGhlIGRhdGEgYmFjaywgYnV0IGl0IGRpZG4ndCBzdG9wIHRoYXQgcXVlcnkgZnJvbSBzdGlsbCBiZWluZyBvdXQgdGhlcmUsIGF0IHRoZSBNeVNRTCBTZXJ2ZXIuIFNvLCB5b3UgbmVlZCB0byBkbyAqKmRiQ2xlYXJSZXN1bHQoKSoqIHRvIHJlLWNsZWFyIHRoYXQgcXVlcnkgZnJvbSB0aGUgcmVtb3RlIHNlcnZlciwgYW5kIGl0IHNob3VsZCByZXR1cm4gKipUUlVFKiogYWdhaW4gYmVjYXVzZSB5b3UndmUgY2xlYXJlZCB0aGF0IHJlc3VsdC4KCmBgYHtyfQpkYkNsZWFyUmVzdWx0KHF1ZXJ5KQpgYGAKCjxiciBcPgpJZiB5b3UgbG9vayBhdCB0aGUgc21hbGwgZGF0YSBzZXQgd2hlcmUgd2UgZmV0Y2hlZCBvbmx5IHRoZSB0b3AgMTAgcm93cyBvciBvYnNlcnZhdGlvbnMsIHlvdSBjYW4gc2VlIHRoYXQgdGhhdCBwcm9kdWNlcyBhIGRhdGEgZnJhbWUgdGhhdCBhY3R1YWxseSBoYXMgYSBkaW1lbnNpb24gb2YgMTAgcm93cyBhcyB3ZWxsLiBXaGF0IHdlJ3ZlIGRvbmUgaXMgc2VsZWN0IGEgdmVyeSBzcGVjaWZpYyBzdWJzZXQuCgpgYGB7cn0KZGltKGFmZnlNaXNTbWFsbCkKYGBgCgo8YnIgXD4KQW4gaW1wb3J0YW50IHBvaW50IGhlcmUgaXMgdGhhdCB5b3UgY2FuIGJhc2ljYWxseSBzZW5kIGFueSBNeVNRTCBxdWVyeSB0aGF0IHlvdSB3b3VsZCBsaWtlIHdpdGhpbiB0aGUgcXVvdGVzLiBTbywgaXQncyBhIGxpdHRsZSBiaXQgYmV5b25kIHRoZSBzY29wZSBvZiB0aGlzIGNsYXNzIHRvIHRlYWNoIHlvdSBhbGwgb2YgdGhlIGRpZmZlcmVudCBxdWVyaWVzLiBUaGUgbW9zdCBpbXBvcnRhbnQgb25lcyBhcmUgdGhlIHF1ZXJpZXMgdGhhdCBJJ3ZlIHNob3duIHlvdSBhYm91dCBzZWxlY3RpbmcsIHRoZSBzb3J0IG9mIHRoZSB0b3RhbCBjb3VudCBhbmQgc2VsZWN0aW5nIGFsbCBvZiB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgYXJlIHN1YmplY3QgdG8gc29tZSBwYXJ0aWN1bGFyIGNvbmRpdGlvbnMuCgpCdXQgaWYgeW91IGdvIHRvIHRoZSBNeVNRTCBkb2N1bWVudGF0aW9uLCB5b3UgY2FuIGFjdHVhbGx5IGNvbWUgdXAgd2l0aCBhIHF1ZXJ5IHRoYXQgd2lsbCBhbGxvdyB5b3UgdG8gdmVyeSBmbGV4aWJseSBzZWxlY3QgYWxtb3N0IGFueSBzb3J0IG9mIHN1YnNldCBvZiByb3dzIG9yIGNvbHVtbnMgZnJvbSBhIGRhdGEgc2V0IHRoYXQgeW91J3JlIGFjdHVhbGx5IGludGVyZXN0ZWQgaW4gc2VsZWN0aW5nLgoKPGJyIFw+CgotLS0KCjxiciBcPgoKIyMjIERvbid0IGZvcmdldCB0byBjbG9zZSB0aGUgY29ubmVjdGlvbiEKClJlbWVtYmVyIHRvIGNsb3NlIHRoZSBjb25uZWN0aW9uLiBTbywgdGhpcyBpcyBvbmUgb2YgdGhlIG1vc3QgY29tbW9uIG1pc3Rha2VzIHdoZW4gdXNpbmcgYWNjZXNzaW5nIGEgZGF0YWJhc2UKZnJvbSBSLCBpcyB0aGF0IHlvdSBvcGVuIGEgY29ubmVjdGlvbjsgeW91IHNlbGVjdCBzb21lIGRhdGEgb3V0OyB5b3UgZ28gaGFwcGlseSBvbiB5b3VyIHdheS4gWW91IHNob3VsZCB0cnkgdG8gY2xvc2UgdGhlIGNvbm5lY3Rpb24gYXMgc29vbiBhcyB5b3UgZG9uJ3QgbmVlZCB0aGUgY29ubmVjdGlvbiBhbnltb3JlLiBTbywgaW1tZWRpYXRlbHkgYWZ0ZXIgZXh0cmFjdGluZyBvdXQgdGhlIGRhdGEgdGhhdCB5b3UncmUgaW50ZXJlc3RlZCBpbiwgeW91IHNob3VsZCBjbG9zZSB0aGUgY29ubmVjdGlvbiBhbmQgbW92ZSBvbiB3aXRoIHRoZSByZXN0IG9mIHlvdXIgc2NyaXB0LgoKYGBge3J9CmRiRGlzY29ubmVjdChoZzE5KQpgYGAKCjxiciBcPgoKLS0tCgo8YnIgXD4KCiMjIyBGdXJ0aGVyIHJlc291cmNlcwoKPGJyIFw+CgoqIFJNeVNRTCB2aWduZXR0ZTogaHR0cDovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUk15U1FML1JNeVNRTC5wZGYKCiAgICBUaGUgUk15U1FMIHZpZ25ldHRlIGlzIHZlcnkgZ29vZC4gSXQgZ2l2ZXMgeW91IGFjY2VzcyB0byBhIGJ1bmNoIG9mIG90aGVyIE15U1FMIGNvbW1hbmRzIHRoYXQgbWlnaHQgYmUgdXNlZnVsIGZvciBzZWxlY3RpbmcgZGF0YSBmb3IgeW91ciBwYXJ0aWN1bGFyIGV4YW1wbGVzLgoKPGJyIFw+CgoqIExpc3Qgb2YgY29tbWFuZHM6IGh0dHA6Ly93d3cucGFudHoub3JnL3NvZnR3YXJlL215c3FsL215c3FsY29tbWFuZHMuaHRtbAogICAgKyAqKkRvIG5vdCwgZG8gbm90LCBkZWxldGUsIGFkZCBvciBqb2luIHRoaW5ncyBmcm9tIGVuc2VtYmwuIE9ubHkgc2VsZWN0LioqCiAgICArIEluIGdlbmVyYWwgYmUgY2FyZWZ1bCB3aXRoIG15U1FMIGNvbW1hbmRzCgogICAgVGhlcmUncyB2ZXJ5IG5pY2UgY29sbGVjdGlvbiBvZiBNeVNRTCBjb21tYW5kcyBoZXJlLCB2ZXJ5IG5pY2VseSBvcmdhbml6ZWQgdGhhdCB3aWxsIGdpdmUgeW91IGEgbG90IG1vcmUgaW5mb3JtYXRpb24gaWYgeW91IHdhbnQgdG8gYmUgYWJsZSB0byB1c2UgdGhlbS4KCiAgICBPbmUgdGhpbmcgdG8ga2VlcCBpbiBtaW5kIGlzIHRoYXQgSSBoYXZlIHNob3duIGFuIGV4YW1wbGUgaGVyZSBhYm91dCBwdWJsaWMgc3BhY2luZyBNeVNRTCBzZXJ2ZXIuIFdoYXQgSSd2ZSBkb25lIHByaW1hcmlseSBmb3IgdGhpcyBhbmFseXNpcyBpcyBzZWxlY3QgZGF0YSBmcm9tIHRoYXQgc2VydmVyLiBQbGVhc2UsIHBsZWFzZSBkbyBub3QgYWNjZXMgdGhhdCBzZXJ2ZXIgdG8gZGVsZXRlLCBhZGQgb3Igam9pbiB0aGluZ3MuIEluIG90aGVyIHdvcmRzLCBkb24ndCBwdXNoIGFueXRoaW5nIGJhY2sgaW50byB0aGUgc2VydmVyLCBvbmx5IHN1YiB0aGluZ3Mgb3Igc2VsZWN0IHRoaW5ncyBvdXQgb2YgdGhlIHNlcnZlci4gU28sIHlvdSBzaG91bGQgb25seSBiZSB1c2luZyB0aGUgc2VsZWN0IGNvbW1hbmQuCgogICAgQW5vdGhlciBpbXBvcnRhbnQgY29tcG9uZW50IGlzIHRoYXQgd2UgYWdhaW4sIGFyZSBhIGxhcmdlIGNsYXNzLiBTbywgaWYgeW91IGFsbCBoaXQgdGhhdCBzZXJ2ZXIgYXQgdGhlIHNhbWUgdGltZSwgaXQgd2lsbCBsaWtlbHkgY2F1c2UgcHJvYmxlbXMgZm9yIHRoZW0uIEl0J3MgbXVjaCBiZXR0ZXIgdG8gcHJhY3RpY2Ugb24geW91ciBvd24gbG9jYWwgdmVyc2lvbiBvZiBNeVNRTC4gSW4gY2FyZWZ1bCwgaW4gZ2VuZXJhbCwgeW91IG5lZWQgdG8gYmUgYSBsaXR0bGUgYml0IGNhcmVmdWwgd2l0aCBNeVNRTCBjb21tYW5kcyBiZWNhdXNlIHlvdSBjYW4gZGVsZXRlIGRhdGEgdGhhdCBvdGhlciBwZW9wbGUgYXJlIHdvcmtpbmcgb24uIAoKPGJyIFw+CgoqIEEgbmljZSBibG9nIHBvc3Qgc3VtbWFyaXppbmcgc29tZSBvdGhlciBjb21tYW5kczogaHR0cDovL3d3dy5yLWJsb2dnZXJzLmNvbS9teXNxbC1hbmQtci8KCiAgICBBIG5pY2UgYmxvZyBzdW1tYXJpemluZyBzb21lIG90aGVyIGNvbW1hbmRzIHRoYXQgYXJlIHVzZWQgd2l0aCBNeVNRTCBhbmQgUiBpcyByaWdodCBoZXJlLCBhbmQgc28gaWYgeW91IHdhbnQgdG8gdGFrZSBhIGxvb2sgYW5kIGxlYXJuIGEgbG90IG1vcmUsIHRoZXJlJ3MgYSB0b24gbW9yZSB0aGF0IGNhbiBiZSBsZWFybmVkIHRoZXJlLCBhbmQgUiBhbmQgTXlTUUwgcGxheSB2ZXJ5IG5pY2VseSB0b2dldGhlciBpZiB5b3Ugd2FudCB0byBnZXQgZGF0YSBvdXQuCgo8YnIgXD4KCi0tLQoKPGNlbnRlcj5FTkQ8L2NlbnRlcj4KCi0tLQ==