Intro

There is kind of a lot going on in this show-and-tell so I’m taking a moment up-front to give some why behind the what.

The first thing I’m providing here is a quick intro to my GitHub Pages Blog. The reason I’m providing this background is that I’m posting this tutorial to my GitHub blog. The content is written in an Rmarkdown, rendered to an html notebook, then pushed to the GitHub repository for my GitHub pages blog.

The second item on the agenda is the tidyverse::googledrive package. I don’t have a ton of interesting stuff to say about this package. I’m providing some code that works pretty well for me. These code samples come with the following caveat: so far everything about the tidyverse::googledrive package has worked for me without incident. I may not be super helpful in the troubleshooting arena as I haven’t really had to troubleshoot anything yet.

Google Drive is a pretty nice, low friction platform for cloud data storage/file sharing/collaboration. I’m guessing it’s the part of this vignette that people are most interested. I’m also guessing a lot of people are kind of like me, not really enamored with Google Drive but willing to give it a shot.

The third topic I’m touching on here is importing data from a MySQL database running on an Amazon cloud. I have a few other pretty involved blog posts on this topic so I’m really just planning to scratch the surface a little on this one. The main reason I’m doing this is that I think there are some limitations to the Google Drive workflow and I wanted to illustrate one pretty simple alternative.

Packages and Dependencies

library(googledrive)
library(stringr) # some string functions for cleaning EIDL data
library(dplyr)

Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union
library(DBI) # for some database interface methods
library(here) # manage directories
here() starts at C:/Users/aaron.mamula/Desktop/ResearchBlog/aaronmams.github.io
library(DT) # for pretty Rmarkdown tables
package 㤼㸱DT㤼㸲 was built under R version 4.0.3Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

GitHub Blog

I’ve written what I think is a pretty digestible walk-through on getting started using GitHub pages to host a research blog. This walk-through lives in the most unlikely of places, my research blog: Blogging on GitHub Pages with Jekyll.

I don’t want to (and I’m not really qualified to anyway) get hung-up on the particulars of webhosting with GitHub pages. What I want to emphasize at this stage is that, if you want to get a simple website/blog up and running, git/GitHub make it pretty easy. I did it in like 2 hours (possibly less):

  1. forked an existing open-source GitHub repository that happened to be a Jekyll blog
  2. changed the name of that repo to aaronmams.github.io

By naming the repository github-username.github.io GitHub knows to associate the content of that repo with the GitHub pages domain they automagically allocate to my GitHub account.

First point of order: you don’t have to do things the way I did them. You can build a website “from the ground up” if you want and put it up on GitHub pages by putting the source files in the GitHub repository called github-username.github.io. Emily Markowitz has a really cool personal website that she runs on GitHub pages.

Second point of order: the static site generator Jekyll plays no role in the day-to-day maintenance of my research blog. I manage new posts through R Studio & GitHub by writing new posts in Rmarkdown, rendering them to markdown (.md), and pushing them to the _posts/ sub-directory of the repository. The only Jekyll-centric element of my blog that really matters is that new posts must be named according to the convention: 2020-10-27-cool blog title. This is just how Jekyll demands that posts be named in order to recognize them as content to be rendered to the site.

Google Drive

Authentication/Connectivity

Probably the most important piece of the tidyverse::googledrive pipeline is the authentication process. Unfortunately, I don’t have much to say about this. Basically, the googledrive package pretty much did all the work for me.

Because it’s difficult to illustrate this authentication process “on-the-fly”, I did a quick 5 min. screen recording that’s available on YouTube, R-googledrive-authentication.

drive_auth()
Using an auto-discovered, cached token.
To suppress this message, modify your code or options to clearly consent to the use of a cached token.
See gargle's "Non-interactive auth" vignette for more details:
https://gargle.r-lib.org/articles/non-interactive-auth.html
The googledrive package is using a cached token for aaron.mamula@noaa.gov.
drive_user()
Auto-refreshing stale OAuth token.
Logged in as:
  *  displayName: Aaron Mamula - NOAA Federal
  * emailAddress: aaron.mamula@noaa.gov

The following resource on managing authentication tokens securely maybe also be a helpful troubleshooting resource if folks need it.

Find Resources

Using the drive_find() method we can explore Google Drive resources available in our session:

drive.stuff <- drive_find(n_max=10)
head(drive.stuff)

One potentially useful way to filter results from drive_find() is to search for resources of a particular type. Here, I’ll look for google sheet file types. Other arguments that can be provided include

  • “csv”
  • application/pdf
head(drive_find(type="spreadsheet",n_max=10)$name)
[1] "SWFSC WORKOUT"                                                                   
[2] "R Workshop Video Links"                                                          
[3] "R Workshop Detailed Schedule"                                                    
[4] "Registration: R Workshop for Economics and Human Dimensions Research (Responses)"
[5] "R for Social Scientists Pre-workshop Questionnaire (Responses)"                  
[6] "Workshop Info"                                                                   

The method drive_get() pulls down a lot of meta-type info on available resources.

example <- drive_get("R Workshop Video Links")
example

Note that the field drive_resource nests a rather large list in a tibble column.

names(example$drive_resource[[1]])
 [1] "kind"                         "id"                           "name"                        
 [4] "mimeType"                     "starred"                      "trashed"                     
 [7] "explicitlyTrashed"            "parents"                      "spaces"                      
[10] "version"                      "webViewLink"                  "iconLink"                    
[13] "hasThumbnail"                 "thumbnailLink"                "thumbnailVersion"            
[16] "viewedByMe"                   "viewedByMeTime"               "createdTime"                 
[19] "modifiedTime"                 "modifiedByMeTime"             "modifiedByMe"                
[22] "owners"                       "lastModifyingUser"            "shared"                      
[25] "ownedByMe"                    "capabilities"                 "viewersCanCopyContent"       
[28] "copyRequiresWriterPermission" "writersCanShare"              "permissions"                 
[31] "permissionIds"                "quotaBytesUsed"               "isAppAuthorized"             
[34] "exportLinks"                 

I don’t currently have a need for most of this info…but I can imagine that some people may want to look-up google drive resources by characteristics or properties. For example, I can see where it might be valuable to know who the last person to modify a file was:

example$drive_resource[[1]]$lastModifyingUser
$kind
[1] "drive#user"

$displayName
[1] "Aaron Mamula - NOAA Federal"

$me
[1] TRUE

$permissionId
[1] "11938931740274108721"

$emailAddress
[1] "aaron.mamula@noaa.gov"

File Downloads

The drive_find() and drive_get() methods are nice discovery tools. If we have actual source data that we would like to access from Google Drive, we need to bring it down into the workspace. For this we can use drive_download().

I have some data from the Small Business Administration on Covid relief from the Paycheck Protection Program and Economic Injury Disaster Loans Program. These data exist as a collection of .csv files currently housed in google drive. The following will download one of these data files into the data/ subdirectory of my current project.

# download the EIDL data from google drive into a local "data" sub-directory...note that I'm shipping these data off
# to a different project as I don't want to house source data inside my blog.

drive_download("Cara-R-Internship/CovidData/EIDLLoans1.csv",path="C:/Users/aaron.mamula/Desktop/R-Projects/PPP-EIDL-Analysis/data/EIDLLoans1.csv",
               overwrite=T)
File downloaded:
  * EIDLLoans1.csv
Saved locally as:
  * C:/Users/aaron.mamula/Desktop/R-Projects/PPP-EIDL-Analysis/data/EIDLLoans1.csv

Something that I found interesting is that the drive_download() method doesn’t actually require the specificity that I provided above. The method will find my EIDLLoans1.csv file whether I provide it nesting folder names or not:

drive_download("PPP Data 150k plus 080820.csv",path="C:/Users/aaron.mamula/Desktop/R-Projects/PPP-EIDL-Analysis/data/ppp.csv",
               overwrite=T)
File downloaded:
  * PPP Data 150k plus 080820.csv
Saved locally as:
  * C:/Users/aaron.mamula/Desktop/R-Projects/PPP-EIDL-Analysis/data/ppp.csv

The drive_download() function made a local copy of the .csv file in my data/ sub-directory. To access these data in the R Workspace we just use the familiar read.csv() method:

df <- read.csv("C:/Users/aaron.mamula/Desktop/R-Projects/PPP-EIDL-Analysis/data/ppp.csv")
str(df)
'data.frame':   662515 obs. of  16 variables:
 $ LoanRange    : chr  "d $350,000-1 million" "d $350,000-1 million" "d $350,000-1 million" "d $350,000-1 million" ...
 $ BusinessName : chr  "AERO BOX LLC" "BOYER CHILDREN'S CLINIC" "KIRTLEY CONSTRUCTION INC" "PLEASANT PLACES, INC." ...
 $ Address      : chr  "N/A" "1850 BOYER AVE E" "1661 MARTIN RANCH RD" "7684 Southrail Road" ...
 $ City         : chr  "N/A" "SEATTLE" "SAN BERNARDINO" "North Charleston" ...
 $ State        : chr  "" "" "" "" ...
 $ Zip          : int  NA 98112 92407 29420 29150 NA NA 32259 NA NA ...
 $ NAICSCode    : int  484210 NA 236115 561730 325510 424210 721110 813110 326199 423140 ...
 $ BusinessType : chr  "" "Non-Profit Organization" "Corporation" "Sole Proprietorship" ...
 $ RaceEthnicity: chr  "Unanswered" "Unanswered" "Unanswered" "Unanswered" ...
 $ Gender       : chr  "Unanswered" "Unanswered" "Unanswered" "Male Owned" ...
 $ Veteran      : chr  "Unanswered" "Unanswered" "Unanswered" "Non-Veteran" ...
 $ NonProfit    : chr  "" "Y" "" "" ...
 $ JobsReported : int  NA 75 21 73 62 NA NA 89 NA NA ...
 $ DateApproved : chr  "05/03/2020" "05/03/2020" "05/03/2020" "05/03/2020" ...
 $ Lender       : chr  "The Huntington National Bank" "Bank of America, National Association" "Bank of America, National Association" "Synovus Bank" ...
 $ CD           : chr  "" "WA-07" "CA-31" "SC-01" ...
#clean city names & clean loan range field
df <- df %>% mutate(city.name=toupper(trimws(City)),
                    state = toupper(trimws(State)),
                    LoanRange = trimws(substr(LoanRange,2,nchar(LoanRange)))) 

# find Santa Cruz County Businesses
ppp.sc <- df %>% filter(Zip %in% c(95001,95010,05018,95017,95019,95041,95060,
                                   95062,95065,96064,95066,95073,95003,95005,95007))
# Organize by JobsReported
ppp.sc <- ppp.sc %>% arrange(-JobsReported) 
ppp.sc %>% select(LoanRange,BusinessName,City,JobsReported,DateApproved)

What if one would like to import data from a .csv in google drive WITHOUT downloading a local version of the .csv?

Unfortunately, I don’t have a great answer for that. Here are a couple leads for you tho:

  1. There is a googlesheets package that is part of the tidyverse. So if you’re ok having the data in a google sheet rather than a .csv this could be a good option.

  2. There is probably a way to use read.csv() directly on the url. I haven’t had any success in this arena yet but it could just be that I’m not understanding what url string I should be using. This Stack Overflow question claims to have cracked the case.

AWS

This might seem like kind of a tangent but I think it’s related enough to the previous googledrive section to warrant inclusion. Here’s why: Google Drive is a nice, easy-to-use cloud platform for data storage. AWS is another cloud-based alternative that requires a little more set-up effort but results (I think) in a cleaner workflow.

I have a series of blog posts where I tried to do a reasonably thorough reporting on a self-guided AWS expedition I took a while back:

  1. Connect to Amazon RDS w/MySQL Workbench
  2. AWS Custom VPCs
  3. Using a Virtual EC2 Server to Connect to RDS
  4. Using Python Script to Connct to AWS RDS

DB Backstory

I got a bunch of needle logs from the City of Santa Cruz. These are reports that city workers fill out whenever they pick up a used needle from a public space. They write down an approximate address/description and how many needles were encountered. I received these data as hard copies and jammed them into .csv files. Then I looked up the addresses as best I could on google maps and created a look-up table with lat/long coordinates for each location.

The database currently has two tables:

  1. needle_events which contains the individual needle encounters
  2. location_geo which contains the lat/long coordinates for each location description.

DB Set-up

This a pretty detailed log of my first experience setting up a relational database on AWS. In the article I also link to a number of other tutorials that can be used to set up a simple relational database on Amazon’s RDS service.

This step is pretty much just toggling through and selecting from a menu of options provided to you by Amazon.

DB Connection

From here it’s totally feasible to connect to your database through R. I like to preface my R connection with a connection via some other database client. Since the DB I’m using here is a MySQL database, I used MySQL Workbench (a free MySQL client) to establish my initial connection. This has the added benefit of giving me a nice pointy-clicky interface with which to write new observations to database tables by pushing .csv files up.

The R connection is really simple (mostly because I don’t have much security fencing on this database): just need a database endpoint and some access credentials:

cn <- dbConnect(drv      = RMySQL::MySQL(), 
                username = "admin", 
                password = "nmfssocialscience", 
                host     = "mams-teaching-public.c65i4tmttvql.us-west-1.rds.amazonaws.com", 
                port     = 3306, 
                dbname   = "needle_waste")

Analysis

Once the database connection is established, it is pretty straightforward to import data and start doing stuff with it.

The dbGetQuery() method from the DBI package allows SQL syntax to be passed to the database engine and returns results to the R workspace. Here, I’m joining two tables from the database

df.joined <- dbGetQuery(cn, "SELECT * FROM needle_events inner join locations_geo on needle_events.LOCATION_RECORDED=locations_geo.LOCATION_RECORDED")
head(df.joined)

Misc. Takeaways

  1. It seems that it is possible to post html and html notebook files to a Jekyll Blog. This is kinda cool, I have been rendering stuff to .md files before posting and it’s cool to know I don’t have to.

  2. The googledrive package has some nice wrappers for working with Google Drive. I’m not seeing a lot of utility here that’s really game-changing for my workflow…but maybe if I work with it a little more I’ll warm-up to it.

  3. I really like rendering tables using DT::datatable(dataframe) inside my Rmarkdowns. I just like to way it makes my tables look. I wasn’t able to do this with my GitHub blog. Something about this table formatting was generating html tags that couldn’t be rendered.

  4. There’s probably a lot of red-tape type stuff involved in using AWS as federal employees. As evidenced by Amazon’s exiting public sector partnerships, it’s definitely possible to use Amazon’s cloud for gob’ment data. However, I don’t know any of the particulars about when/where/how it might be appropriate for federal employees to use AWS to warehouse data. I do know that

  • AWS-RDS is not that hard to use once you get the hang of it, and
  • it’s pretty smooth to pipe data into R from an Amazon cloud.
  1. If you’re curious, the big roadblock for me in terms of actually using Jekyll to manage blog content was a Ruby version conflict between what Jekyll required and what my Mac OS had installed. For some reason this ate up like 2 weeks of my life and never got resolved. I’ve since updated to Mac Catalina (which is purported to ship with Ruby 2.6.3) so I’m guessing the issue would probably resolve itself if I cared enough to try and run Jekyll again….but since I don’t have to, I’m probably not gonna.

Unsolicited Commentary

I think a lot of us were moved (probably to varying degrees) by the Richard McElreath video that Roy recently distributed. The portion of the talk that resonated most with me with was the emphasis placed on proper data management/data curation practices. Basically, it’s hard for research to be reproducible if the data aren’t shareable. And it’s hard for data to be shareable if they exist as a potpourri of flat files on somebody’s hard-drive.

I’d like to think that what I presented here could kind of be marketed as a “starter kit” for working with data in “the cloud”…which is kind of an important skill for those interested in reproducible research.

Consider the following popular workflow for managing research projects with multiple contributors:

Github fork --> git clone --> R Studio code --> git commit --> git push/pull.

This is good for collaborative analysis but GitHub isn’t a great place for source data. So if the source data are being periodically updated, you need a data distribution platform that’s accessible to all project collaborators.

Google Drive and Amazon Web Services are two relatively low-cost cloud data storage platforms. This makes them nice options for practicing collaborative data management.

LS0tDQp0aXRsZTogIlIgd2l0aCBHb29nbGUgRHJpdmUsIGFuZCBBV1MiDQphdXRob3I6ICJBYXJvbiBNYW11bGEiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgdG9jX2RlcHRoOiA0DQogICAgdGhlbWU6IGZsYXRseQ0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMgSW50cm8NCg0KVGhlcmUgaXMga2luZCBvZiBhIGxvdCBnb2luZyBvbiBpbiB0aGlzIHNob3ctYW5kLXRlbGwgc28gSSdtIHRha2luZyBhIG1vbWVudCB1cC1mcm9udCB0byBnaXZlIHNvbWUgd2h5IGJlaGluZCB0aGUgd2hhdC4gDQoNClRoZSBmaXJzdCB0aGluZyBJJ20gcHJvdmlkaW5nIGhlcmUgaXMgYSBxdWljayBpbnRybyB0byBteSBHaXRIdWIgUGFnZXMgQmxvZy4gVGhlIHJlYXNvbiBJJ20gcHJvdmlkaW5nIHRoaXMgYmFja2dyb3VuZCBpcyB0aGF0IEknbSBwb3N0aW5nIHRoaXMgdHV0b3JpYWwgdG8gbXkgR2l0SHViIGJsb2cuIFRoZSBjb250ZW50IGlzIHdyaXR0ZW4gaW4gYW4gUm1hcmtkb3duLCByZW5kZXJlZCB0byBhbiBodG1sIG5vdGVib29rLCB0aGVuIHB1c2hlZCB0byB0aGUgR2l0SHViIHJlcG9zaXRvcnkgZm9yIG15IEdpdEh1YiBwYWdlcyBibG9nLg0KDQpUaGUgc2Vjb25kIGl0ZW0gb24gdGhlIGFnZW5kYSBpcyB0aGUgW3RpZHl2ZXJzZTo6Z29vZ2xlZHJpdmUgcGFja2FnZV0oaHR0cHM6Ly9nb29nbGVkcml2ZS50aWR5dmVyc2Uub3JnLykuIEkgZG9uJ3QgaGF2ZSBhIHRvbiBvZiBpbnRlcmVzdGluZyBzdHVmZiB0byBzYXkgYWJvdXQgdGhpcyBwYWNrYWdlLiBJJ20gcHJvdmlkaW5nIHNvbWUgY29kZSB0aGF0IHdvcmtzIHByZXR0eSB3ZWxsIGZvciBtZS4gVGhlc2UgY29kZSBzYW1wbGVzIGNvbWUgd2l0aCB0aGUgZm9sbG93aW5nIGNhdmVhdDogc28gZmFyIGV2ZXJ5dGhpbmcgYWJvdXQgdGhlIGBgYHRpZHl2ZXJzZTo6Z29vZ2xlZHJpdmVgYGAgcGFja2FnZSBoYXMgd29ya2VkIGZvciBtZSB3aXRob3V0IGluY2lkZW50LiBJIG1heSBub3QgYmUgc3VwZXIgaGVscGZ1bCBpbiB0aGUgdHJvdWJsZXNob290aW5nIGFyZW5hIGFzIEkgaGF2ZW4ndCByZWFsbHkgaGFkIHRvIHRyb3VibGVzaG9vdCBhbnl0aGluZyB5ZXQuDQoNCkdvb2dsZSBEcml2ZSBpcyBhIHByZXR0eSBuaWNlLCBsb3cgZnJpY3Rpb24gcGxhdGZvcm0gZm9yIGNsb3VkIGRhdGEgc3RvcmFnZS9maWxlIHNoYXJpbmcvY29sbGFib3JhdGlvbi4gSSdtIGd1ZXNzaW5nIGl0J3MgdGhlIHBhcnQgb2YgdGhpcyB2aWduZXR0ZSB0aGF0IHBlb3BsZSBhcmUgbW9zdCBpbnRlcmVzdGVkLiBJJ20gYWxzbyBndWVzc2luZyBhIGxvdCBvZiBwZW9wbGUgYXJlIGtpbmQgb2YgbGlrZSBtZSwgbm90IHJlYWxseSBlbmFtb3JlZCB3aXRoIEdvb2dsZSBEcml2ZSBidXQgd2lsbGluZyB0byBnaXZlIGl0IGEgc2hvdC4NCg0KVGhlIHRoaXJkIHRvcGljIEknbSB0b3VjaGluZyBvbiBoZXJlIGlzIGltcG9ydGluZyBkYXRhIGZyb20gYSBNeVNRTCBkYXRhYmFzZSBydW5uaW5nIG9uIGFuIEFtYXpvbiBjbG91ZC4gSSBoYXZlIGEgZmV3IG90aGVyIHByZXR0eSBpbnZvbHZlZCBibG9nIHBvc3RzIG9uIHRoaXMgdG9waWMgc28gSSdtIHJlYWxseSBqdXN0IHBsYW5uaW5nIHRvIHNjcmF0Y2ggdGhlIHN1cmZhY2UgYSBsaXR0bGUgb24gdGhpcyBvbmUuIFRoZSBtYWluIHJlYXNvbiBJJ20gZG9pbmcgdGhpcyBpcyB0aGF0IEkgdGhpbmsgdGhlcmUgYXJlIHNvbWUgbGltaXRhdGlvbnMgdG8gdGhlIEdvb2dsZSBEcml2ZSB3b3JrZmxvdyBhbmQgSSB3YW50ZWQgdG8gaWxsdXN0cmF0ZSBvbmUgcHJldHR5IHNpbXBsZSBhbHRlcm5hdGl2ZS4NCg0KIyBQYWNrYWdlcyBhbmQgRGVwZW5kZW5jaWVzDQoNCmBgYHtyfQ0KbGlicmFyeShnb29nbGVkcml2ZSkNCmxpYnJhcnkoc3RyaW5ncikgIyBzb21lIHN0cmluZyBmdW5jdGlvbnMgZm9yIGNsZWFuaW5nIEVJREwgZGF0YQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoREJJKSAjIGZvciBzb21lIGRhdGFiYXNlIGludGVyZmFjZSBtZXRob2RzDQpsaWJyYXJ5KGhlcmUpICMgbWFuYWdlIGRpcmVjdG9yaWVzDQpsaWJyYXJ5KERUKSAjIGZvciBwcmV0dHkgUm1hcmtkb3duIHRhYmxlcw0KYGBgDQoNCiMgR2l0SHViIEJsb2cNCg0KSSd2ZSB3cml0dGVuIHdoYXQgSSB0aGluayBpcyBhIHByZXR0eSBkaWdlc3RpYmxlIHdhbGstdGhyb3VnaCBvbiBnZXR0aW5nIHN0YXJ0ZWQgdXNpbmcgR2l0SHViIHBhZ2VzIHRvIGhvc3QgYSByZXNlYXJjaCBibG9nLiBUaGlzIHdhbGstdGhyb3VnaCBsaXZlcyBpbiB0aGUgbW9zdCB1bmxpa2VseSBvZiBwbGFjZXMsIG15IHJlc2VhcmNoIGJsb2c6IFtCbG9nZ2luZyBvbiBHaXRIdWIgUGFnZXMgd2l0aCBKZWt5bGxdKGh0dHBzOi8vYWFyb25tYW1zLmdpdGh1Yi5pby90ZXN0LykuDQoNCkkgZG9uJ3Qgd2FudCB0byAoYW5kIEknbSBub3QgcmVhbGx5IHF1YWxpZmllZCB0byBhbnl3YXkpIGdldCBodW5nLXVwIG9uIHRoZSBwYXJ0aWN1bGFycyBvZiB3ZWJob3N0aW5nIHdpdGggR2l0SHViIHBhZ2VzLiBXaGF0IEkgd2FudCB0byBlbXBoYXNpemUgYXQgdGhpcyBzdGFnZSBpcyB0aGF0LCBpZiB5b3Ugd2FudCB0byBnZXQgYSBzaW1wbGUgd2Vic2l0ZS9ibG9nIHVwIGFuZCBydW5uaW5nLCBnaXQvR2l0SHViIG1ha2UgaXQgcHJldHR5IGVhc3kuIEkgZGlkIGl0IGluIGxpa2UgMiBob3VycyAocG9zc2libHkgbGVzcyk6IA0KDQoxLiBmb3JrZWQgYW4gZXhpc3Rpbmcgb3Blbi1zb3VyY2UgR2l0SHViIHJlcG9zaXRvcnkgdGhhdCBoYXBwZW5lZCB0byBiZSBhIEpla3lsbCBibG9nDQoyLiBjaGFuZ2VkIHRoZSBuYW1lIG9mIHRoYXQgcmVwbyB0byBgYGBhYXJvbm1hbXMuZ2l0aHViLmlvYGBgDQoNCkJ5IG5hbWluZyB0aGUgcmVwb3NpdG9yeSBgYGBnaXRodWItdXNlcm5hbWUuZ2l0aHViLmlvYGBgIEdpdEh1YiBrbm93cyB0byBhc3NvY2lhdGUgdGhlIGNvbnRlbnQgb2YgdGhhdCByZXBvIHdpdGggdGhlIEdpdEh1YiBwYWdlcyBkb21haW4gdGhleSBhdXRvbWFnaWNhbGx5IGFsbG9jYXRlIHRvIG15IEdpdEh1YiBhY2NvdW50Lg0KDQpGaXJzdCBwb2ludCBvZiBvcmRlcjogeW91IGRvbid0IGhhdmUgdG8gZG8gdGhpbmdzIHRoZSB3YXkgSSBkaWQgdGhlbS4gWW91IGNhbiBidWlsZCBhIHdlYnNpdGUgImZyb20gdGhlIGdyb3VuZCB1cCIgaWYgeW91IHdhbnQgYW5kIHB1dCBpdCB1cCBvbiBHaXRIdWIgcGFnZXMgYnkgcHV0dGluZyB0aGUgc291cmNlIGZpbGVzIGluIHRoZSBHaXRIdWIgcmVwb3NpdG9yeSBjYWxsZWQgYGBgZ2l0aHViLXVzZXJuYW1lLmdpdGh1Yi5pb2BgYC4gW0VtaWx5IE1hcmtvd2l0el0oaHR0cHM6Ly9lbWlseWhtYXJrb3dpdHouZ2l0aHViLmlvL2VtaWx5aG1hcmtvd2l0ei8pIGhhcyBhIHJlYWxseSBjb29sIHBlcnNvbmFsIHdlYnNpdGUgdGhhdCBzaGUgcnVucyBvbiBHaXRIdWIgcGFnZXMuIA0KDQpTZWNvbmQgcG9pbnQgb2Ygb3JkZXI6IHRoZSBzdGF0aWMgc2l0ZSBnZW5lcmF0b3IgSmVreWxsIHBsYXlzIG5vIHJvbGUgaW4gdGhlIGRheS10by1kYXkgbWFpbnRlbmFuY2Ugb2YgbXkgcmVzZWFyY2ggYmxvZy4gSSBtYW5hZ2UgbmV3IHBvc3RzIHRocm91Z2ggUiBTdHVkaW8gJiBHaXRIdWIgYnkgd3JpdGluZyBuZXcgcG9zdHMgaW4gUm1hcmtkb3duLCByZW5kZXJpbmcgdGhlbSB0byBtYXJrZG93biAoLm1kKSwgYW5kIHB1c2hpbmcgdGhlbSB0byB0aGUgYGBgX3Bvc3RzL2BgYCBzdWItZGlyZWN0b3J5IG9mIHRoZSByZXBvc2l0b3J5LiBUaGUgb25seSBKZWt5bGwtY2VudHJpYyBlbGVtZW50IG9mIG15IGJsb2cgdGhhdCByZWFsbHkgbWF0dGVycyBpcyB0aGF0IG5ldyBwb3N0cyBtdXN0IGJlIG5hbWVkIGFjY29yZGluZyB0byB0aGUgY29udmVudGlvbjogYGBgMjAyMC0xMC0yNy1jb29sIGJsb2cgdGl0bGVgYGAuIFRoaXMgaXMganVzdCBob3cgSmVreWxsIGRlbWFuZHMgdGhhdCBwb3N0cyBiZSBuYW1lZCBpbiBvcmRlciB0byByZWNvZ25pemUgdGhlbSBhcyBjb250ZW50IHRvIGJlIHJlbmRlcmVkIHRvIHRoZSBzaXRlLiANCg0KDQojIEdvb2dsZSBEcml2ZSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyMgQXV0aGVudGljYXRpb24vQ29ubmVjdGl2aXR5DQoNClByb2JhYmx5IHRoZSBtb3N0IGltcG9ydGFudCBwaWVjZSBvZiB0aGUgYGBgdGlkeXZlcnNlOjpnb29nbGVkcml2ZWBgYCBwaXBlbGluZSBpcyB0aGUgYXV0aGVudGljYXRpb24gcHJvY2Vzcy4gVW5mb3J0dW5hdGVseSwgSSBkb24ndCBoYXZlIG11Y2ggdG8gc2F5IGFib3V0IHRoaXMuIEJhc2ljYWxseSwgdGhlIGBgYGdvb2dsZWRyaXZlYGBgIHBhY2thZ2UgcHJldHR5IG11Y2ggZGlkIGFsbCB0aGUgd29yayBmb3IgbWUuIA0KDQpCZWNhdXNlIGl0J3MgZGlmZmljdWx0IHRvIGlsbHVzdHJhdGUgdGhpcyBhdXRoZW50aWNhdGlvbiBwcm9jZXNzICJvbi10aGUtZmx5IiwgSSBkaWQgYSBxdWljayA1IG1pbi4gc2NyZWVuIHJlY29yZGluZyB0aGF0J3MgYXZhaWxhYmxlIG9uIFlvdVR1YmUsIFtSLWdvb2dsZWRyaXZlLWF1dGhlbnRpY2F0aW9uXShodHRwczovL3lvdXR1LmJlL2x2UF9TZjhEckpNKS4NCg0KYGBge3J9DQpkcml2ZV9hdXRoKCkNCmRyaXZlX3VzZXIoKQ0KYGBgDQoNClRoZSBmb2xsb3dpbmcgcmVzb3VyY2Ugb24gW21hbmFnaW5nIGF1dGhlbnRpY2F0aW9uIHRva2VucyBzZWN1cmVseV0oaHR0cHM6Ly9nYXJnbGUuci1saWIub3JnL2FydGljbGVzL2FydGljbGVzL21hbmFnaW5nLXRva2Vucy1zZWN1cmVseS5odG1sKSBtYXliZSBhbHNvIGJlIGEgaGVscGZ1bCB0cm91Ymxlc2hvb3RpbmcgcmVzb3VyY2UgaWYgZm9sa3MgbmVlZCBpdC4NCg0KIyMgRmluZCBSZXNvdXJjZXMNCg0KVXNpbmcgdGhlIGBgYGRyaXZlX2ZpbmQoKWBgYCBtZXRob2Qgd2UgY2FuIGV4cGxvcmUgR29vZ2xlIERyaXZlIHJlc291cmNlcyBhdmFpbGFibGUgaW4gb3VyIHNlc3Npb246DQoNCmBgYHtyfQ0KZHJpdmUuc3R1ZmYgPC0gZHJpdmVfZmluZChuX21heD0xMCkNCmhlYWQoZHJpdmUuc3R1ZmYpDQpgYGANCg0KT25lIHBvdGVudGlhbGx5IHVzZWZ1bCB3YXkgdG8gZmlsdGVyIHJlc3VsdHMgZnJvbSBgYGBkcml2ZV9maW5kKClgYGAgaXMgdG8gc2VhcmNoIGZvciByZXNvdXJjZXMgb2YgYSBwYXJ0aWN1bGFyIHR5cGUuIEhlcmUsIEknbGwgbG9vayBmb3IgZ29vZ2xlIHNoZWV0IGZpbGUgdHlwZXMuIE90aGVyIGFyZ3VtZW50cyB0aGF0IGNhbiBiZSBwcm92aWRlZCBpbmNsdWRlIA0KDQoqICJjc3YiDQoqIGFwcGxpY2F0aW9uL3BkZg0KDQpgYGB7cn0NCmhlYWQoZHJpdmVfZmluZCh0eXBlPSJzcHJlYWRzaGVldCIsbl9tYXg9MTApJG5hbWUpDQoNCmBgYA0KDQpUaGUgbWV0aG9kIGBgYGRyaXZlX2dldCgpYGBgIHB1bGxzIGRvd24gYSBsb3Qgb2YgbWV0YS10eXBlIGluZm8gb24gYXZhaWxhYmxlIHJlc291cmNlcy4gDQoNCmBgYHtyfQ0KZXhhbXBsZSA8LSBkcml2ZV9nZXQoIlIgV29ya3Nob3AgVmlkZW8gTGlua3MiKQ0KZXhhbXBsZQ0KYGBgDQoNCk5vdGUgdGhhdCB0aGUgZmllbGQgYGBgZHJpdmVfcmVzb3VyY2VgYGAgbmVzdHMgYSByYXRoZXIgbGFyZ2UgbGlzdCBpbiBhIHRpYmJsZSBjb2x1bW4uICANCg0KYGBge3J9DQpuYW1lcyhleGFtcGxlJGRyaXZlX3Jlc291cmNlW1sxXV0pDQpgYGANCkkgZG9uJ3QgY3VycmVudGx5IGhhdmUgYSBuZWVkIGZvciBtb3N0IG9mIHRoaXMgaW5mby4uLmJ1dCBJIGNhbiBpbWFnaW5lIHRoYXQgc29tZSBwZW9wbGUgbWF5IHdhbnQgdG8gbG9vay11cCBnb29nbGUgZHJpdmUgcmVzb3VyY2VzIGJ5IGNoYXJhY3RlcmlzdGljcyBvciBwcm9wZXJ0aWVzLiBGb3IgZXhhbXBsZSwgSSBjYW4gc2VlIHdoZXJlIGl0IG1pZ2h0IGJlIHZhbHVhYmxlIHRvIGtub3cgd2hvIHRoZSBsYXN0IHBlcnNvbiB0byBtb2RpZnkgYSBmaWxlIHdhczoNCg0KYGBge3J9DQpleGFtcGxlJGRyaXZlX3Jlc291cmNlW1sxXV0kbGFzdE1vZGlmeWluZ1VzZXINCmBgYA0KIyMgRmlsZSBEb3dubG9hZHMNCg0KVGhlIGBgYGRyaXZlX2ZpbmQoKWBgYCBhbmQgYGBgZHJpdmVfZ2V0KClgYGAgbWV0aG9kcyBhcmUgbmljZSBkaXNjb3ZlcnkgdG9vbHMuIElmIHdlIGhhdmUgYWN0dWFsIHNvdXJjZSBkYXRhIHRoYXQgd2Ugd291bGQgbGlrZSB0byBhY2Nlc3MgZnJvbSBHb29nbGUgRHJpdmUsIHdlIG5lZWQgdG8gYnJpbmcgaXQgZG93biBpbnRvIHRoZSB3b3Jrc3BhY2UuIEZvciB0aGlzIHdlIGNhbiB1c2UgYGBgZHJpdmVfZG93bmxvYWQoKWBgYC4NCg0KSSBoYXZlIHNvbWUgZGF0YSBmcm9tIHRoZSBTbWFsbCBCdXNpbmVzcyBBZG1pbmlzdHJhdGlvbiBvbiBDb3ZpZCByZWxpZWYgZnJvbSB0aGUgUGF5Y2hlY2sgUHJvdGVjdGlvbiBQcm9ncmFtIGFuZCBFY29ub21pYyBJbmp1cnkgRGlzYXN0ZXIgTG9hbnMgUHJvZ3JhbS4gVGhlc2UgZGF0YSBleGlzdCBhcyBhIGNvbGxlY3Rpb24gb2YgLmNzdiBmaWxlcyBjdXJyZW50bHkgaG91c2VkIGluIGdvb2dsZSBkcml2ZS4gVGhlIGZvbGxvd2luZyB3aWxsIGRvd25sb2FkIG9uZSBvZiB0aGVzZSBkYXRhIGZpbGVzIGludG8gdGhlIGBgYGRhdGEvYGBgIHN1YmRpcmVjdG9yeSBvZiBteSBjdXJyZW50IHByb2plY3QuDQoNCmBgYHtyfQ0KIyBkb3dubG9hZCB0aGUgRUlETCBkYXRhIGZyb20gZ29vZ2xlIGRyaXZlIGludG8gYSBsb2NhbCAiZGF0YSIgc3ViLWRpcmVjdG9yeS4uLm5vdGUgdGhhdCBJJ20gc2hpcHBpbmcgdGhlc2UgZGF0YSBvZmYNCiMgdG8gYSBkaWZmZXJlbnQgcHJvamVjdCBhcyBJIGRvbid0IHdhbnQgdG8gaG91c2Ugc291cmNlIGRhdGEgaW5zaWRlIG15IGJsb2cuDQoNCmRyaXZlX2Rvd25sb2FkKCJDYXJhLVItSW50ZXJuc2hpcC9Db3ZpZERhdGEvRUlETExvYW5zMS5jc3YiLHBhdGg9IkM6L1VzZXJzL2Fhcm9uLm1hbXVsYS9EZXNrdG9wL1ItUHJvamVjdHMvUFBQLUVJREwtQW5hbHlzaXMvZGF0YS9FSURMTG9hbnMxLmNzdiIsDQogICAgICAgICAgICAgICBvdmVyd3JpdGU9VCkNCg0KDQpgYGANClNvbWV0aGluZyB0aGF0IEkgZm91bmQgaW50ZXJlc3RpbmcgaXMgdGhhdCB0aGUgYGBgZHJpdmVfZG93bmxvYWQoKWBgYCBtZXRob2QgZG9lc24ndCBhY3R1YWxseSByZXF1aXJlIHRoZSBzcGVjaWZpY2l0eSB0aGF0IEkgcHJvdmlkZWQgYWJvdmUuIFRoZSBtZXRob2Qgd2lsbCBmaW5kIG15IGBgYEVJRExMb2FuczEuY3N2YGBgIGZpbGUgd2hldGhlciBJIHByb3ZpZGUgaXQgbmVzdGluZyBmb2xkZXIgbmFtZXMgb3Igbm90Og0KDQpgYGB7cn0NCmRyaXZlX2Rvd25sb2FkKCJQUFAgRGF0YSAxNTBrIHBsdXMgMDgwODIwLmNzdiIscGF0aD0iQzovVXNlcnMvYWFyb24ubWFtdWxhL0Rlc2t0b3AvUi1Qcm9qZWN0cy9QUFAtRUlETC1BbmFseXNpcy9kYXRhL3BwcC5jc3YiLA0KICAgICAgICAgICAgICAgb3ZlcndyaXRlPVQpDQpgYGANCg0KVGhlIGBgYGRyaXZlX2Rvd25sb2FkKClgYGAgZnVuY3Rpb24gbWFkZSBhIGxvY2FsIGNvcHkgb2YgdGhlIC5jc3YgZmlsZSBpbiBteSBgYGBkYXRhL2BgYCBzdWItZGlyZWN0b3J5LiBUbyBhY2Nlc3MgdGhlc2UgZGF0YSBpbiB0aGUgUiBXb3Jrc3BhY2Ugd2UganVzdCB1c2UgdGhlIGZhbWlsaWFyIGBgYHJlYWQuY3N2KClgYGAgbWV0aG9kOg0KDQoNCmBgYHtyfQ0KZGYgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL2Fhcm9uLm1hbXVsYS9EZXNrdG9wL1ItUHJvamVjdHMvUFBQLUVJREwtQW5hbHlzaXMvZGF0YS9wcHAuY3N2IikNCnN0cihkZikNCmBgYA0KYGBge3J9DQojY2xlYW4gY2l0eSBuYW1lcyAmIGNsZWFuIGxvYW4gcmFuZ2UgZmllbGQNCmRmIDwtIGRmICU+JSBtdXRhdGUoY2l0eS5uYW1lPXRvdXBwZXIodHJpbXdzKENpdHkpKSwNCiAgICAgICAgICAgICAgICAgICAgc3RhdGUgPSB0b3VwcGVyKHRyaW13cyhTdGF0ZSkpLA0KICAgICAgICAgICAgICAgICAgICBMb2FuUmFuZ2UgPSB0cmltd3Moc3Vic3RyKExvYW5SYW5nZSwyLG5jaGFyKExvYW5SYW5nZSkpKSkgDQoNCiMgZmluZCBTYW50YSBDcnV6IENvdW50eSBCdXNpbmVzc2VzDQpwcHAuc2MgPC0gZGYgJT4lIGZpbHRlcihaaXAgJWluJSBjKDk1MDAxLDk1MDEwLDA1MDE4LDk1MDE3LDk1MDE5LDk1MDQxLDk1MDYwLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA5NTA2Miw5NTA2NSw5NjA2NCw5NTA2Niw5NTA3Myw5NTAwMyw5NTAwNSw5NTAwNykpDQpgYGANCg0KYGBge3J9DQojIE9yZ2FuaXplIGJ5IEpvYnNSZXBvcnRlZA0KcHBwLnNjIDwtIHBwcC5zYyAlPiUgYXJyYW5nZSgtSm9ic1JlcG9ydGVkKSANCnBwcC5zYyAlPiUgc2VsZWN0KExvYW5SYW5nZSxCdXNpbmVzc05hbWUsQ2l0eSxKb2JzUmVwb3J0ZWQsRGF0ZUFwcHJvdmVkKQ0KYGBgDQoNCg0KV2hhdCBpZiBvbmUgd291bGQgbGlrZSB0byBpbXBvcnQgZGF0YSBmcm9tIGEgLmNzdiBpbiBnb29nbGUgZHJpdmUgV0lUSE9VVCBkb3dubG9hZGluZyBhIGxvY2FsIHZlcnNpb24gb2YgdGhlIC5jc3Y/IA0KDQpVbmZvcnR1bmF0ZWx5LCBJIGRvbid0IGhhdmUgYSBncmVhdCBhbnN3ZXIgZm9yIHRoYXQuIEhlcmUgYXJlIGEgY291cGxlIGxlYWRzIGZvciB5b3UgdGhvOg0KDQoxLiBUaGVyZSBpcyBhIFtnb29nbGVzaGVldHNdKGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvZ29vZ2xlc2hlZXRzNCkgcGFja2FnZSB0aGF0IGlzIHBhcnQgb2YgdGhlIHRpZHl2ZXJzZS4gU28gaWYgeW91J3JlIG9rIGhhdmluZyB0aGUgZGF0YSBpbiBhIGdvb2dsZSBzaGVldCByYXRoZXIgdGhhbiBhIC5jc3YgdGhpcyBjb3VsZCBiZSBhIGdvb2Qgb3B0aW9uLg0KDQoyLiBUaGVyZSBpcyBwcm9iYWJseSBhIHdheSB0byB1c2UgYGBgcmVhZC5jc3YoKWBgYCBkaXJlY3RseSBvbiB0aGUgdXJsLiBJIGhhdmVuJ3QgaGFkIGFueSBzdWNjZXNzIGluIHRoaXMgYXJlbmEgeWV0IGJ1dCBpdCBjb3VsZCBqdXN0IGJlIHRoYXQgSSdtIG5vdCB1bmRlcnN0YW5kaW5nIHdoYXQgdXJsIHN0cmluZyBJIHNob3VsZCBiZSB1c2luZy4gW1RoaXMgU3RhY2sgT3ZlcmZsb3cgcXVlc3Rpb25dKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzMzMTM1MDYwL3JlYWQtY3N2LWZpbGUtaG9zdGVkLW9uLWdvb2dsZS1kcml2ZSkgY2xhaW1zIHRvIGhhdmUgY3JhY2tlZCB0aGUgY2FzZS4gDQoNCiMgQVdTIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQpUaGlzIG1pZ2h0IHNlZW0gbGlrZSBraW5kIG9mIGEgdGFuZ2VudCBidXQgSSB0aGluayBpdCdzIHJlbGF0ZWQgZW5vdWdoIHRvIHRoZSBwcmV2aW91cyBgYGBnb29nbGVkcml2ZWBgYCBzZWN0aW9uIHRvIHdhcnJhbnQgaW5jbHVzaW9uLiBIZXJlJ3Mgd2h5OiBHb29nbGUgRHJpdmUgaXMgYSBuaWNlLCBlYXN5LXRvLXVzZSBjbG91ZCBwbGF0Zm9ybSBmb3IgZGF0YSBzdG9yYWdlLiBBV1MgaXMgYW5vdGhlciBjbG91ZC1iYXNlZCBhbHRlcm5hdGl2ZSB0aGF0IHJlcXVpcmVzIGEgbGl0dGxlIG1vcmUgc2V0LXVwIGVmZm9ydCBidXQgcmVzdWx0cyAoSSB0aGluaykgaW4gYSBjbGVhbmVyIHdvcmtmbG93Lg0KDQpJIGhhdmUgYSBzZXJpZXMgb2YgYmxvZyBwb3N0cyB3aGVyZSBJIHRyaWVkIHRvIGRvIGEgcmVhc29uYWJseSB0aG9yb3VnaCByZXBvcnRpbmcgb24gYSBzZWxmLWd1aWRlZCBBV1MgZXhwZWRpdGlvbiBJIHRvb2sgYSB3aGlsZSBiYWNrOg0KDQoxLiBbQ29ubmVjdCB0byBBbWF6b24gUkRTIHcvTXlTUUwgV29ya2JlbmNoXShodHRwczovL2Fhcm9ubWFtcy5naXRodWIuaW8vQ29ubmVjdC10by1BbWF6b24tUkRTLURCLXdpdGgtTXlTUUwtV29ya2JlbmNoLykNCjIuIFtBV1MgQ3VzdG9tIFZQQ3NdKGh0dHBzOi8vYWFyb25tYW1zLmdpdGh1Yi5pby9BV1MtTXlTUUwtREItaW4tYS1DdXN0b20tVlBDLykNCjMuIFtVc2luZyBhIFZpcnR1YWwgRUMyIFNlcnZlciB0byBDb25uZWN0IHRvIFJEU10oaHR0cHM6Ly9hYXJvbm1hbXMuZ2l0aHViLmlvL0Nsb3VkLWNvbXB1dGluZy13aXRoLVB5dGhvbiwtTXlTUUwtaW4tQVdTLykNCjQuIFtVc2luZyBQeXRob24gU2NyaXB0IHRvIENvbm5jdCB0byBBV1MgUkRTXShodHRwczovL2Fhcm9ubWFtcy5naXRodWIuaW8vQ29ubmVjdGluZy1weXRob24tdG8tQVdTLVJEUy1EQi1pbi10aGUtY2xvdWQvKQ0KDQojIyBEQiBCYWNrc3RvcnkNCg0KSSBnb3QgYSBidW5jaCBvZiBuZWVkbGUgbG9ncyBmcm9tIHRoZSBDaXR5IG9mIFNhbnRhIENydXouIFRoZXNlIGFyZSByZXBvcnRzIHRoYXQgY2l0eSB3b3JrZXJzIGZpbGwgb3V0IHdoZW5ldmVyIHRoZXkgcGljayB1cCBhIHVzZWQgbmVlZGxlIGZyb20gYSBwdWJsaWMgc3BhY2UuIFRoZXkgd3JpdGUgZG93biBhbiBhcHByb3hpbWF0ZSBhZGRyZXNzL2Rlc2NyaXB0aW9uIGFuZCBob3cgbWFueSBuZWVkbGVzIHdlcmUgZW5jb3VudGVyZWQuIEkgcmVjZWl2ZWQgdGhlc2UgZGF0YSBhcyBoYXJkIGNvcGllcyBhbmQgamFtbWVkIHRoZW0gaW50byAuY3N2IGZpbGVzLiBUaGVuIEkgbG9va2VkIHVwIHRoZSBhZGRyZXNzZXMgYXMgYmVzdCBJIGNvdWxkIG9uIGdvb2dsZSBtYXBzIGFuZCBjcmVhdGVkIGEgbG9vay11cCB0YWJsZSB3aXRoIGxhdC9sb25nIGNvb3JkaW5hdGVzIGZvciBlYWNoIGxvY2F0aW9uLg0KDQpUaGUgZGF0YWJhc2UgY3VycmVudGx5IGhhcyB0d28gdGFibGVzOg0KDQoxLiBgYGBuZWVkbGVfZXZlbnRzYGBgIHdoaWNoIGNvbnRhaW5zIHRoZSBpbmRpdmlkdWFsIG5lZWRsZSBlbmNvdW50ZXJzDQoyLiBgYGBsb2NhdGlvbl9nZW9gYGAgd2hpY2ggY29udGFpbnMgdGhlIGxhdC9sb25nIGNvb3JkaW5hdGVzIGZvciBlYWNoIGxvY2F0aW9uIGRlc2NyaXB0aW9uLg0KDQojIyBEQiBTZXQtdXANCg0KW1RoaXMgYSBwcmV0dHkgZGV0YWlsZWQgbG9nIG9mIG15IGZpcnN0IGV4cGVyaWVuY2Ugc2V0dGluZyB1cCBhIHJlbGF0aW9uYWwgZGF0YWJhc2Ugb24gQVdTXShodHRwczovL2Fhcm9ubWFtcy5naXRodWIuaW8vQ29ubmVjdC10by1BbWF6b24tUkRTLURCLXdpdGgtTXlTUUwtV29ya2JlbmNoLykuIEluIHRoZSBhcnRpY2xlIEkgYWxzbyBsaW5rIHRvIGEgbnVtYmVyIG9mIG90aGVyIHR1dG9yaWFscyB0aGF0IGNhbiBiZSB1c2VkIHRvIHNldCB1cCBhIHNpbXBsZSByZWxhdGlvbmFsIGRhdGFiYXNlIG9uIEFtYXpvbidzIFJEUyBzZXJ2aWNlLiANCg0KVGhpcyBzdGVwIGlzIHByZXR0eSBtdWNoIGp1c3QgdG9nZ2xpbmcgdGhyb3VnaCBhbmQgc2VsZWN0aW5nIGZyb20gYSBtZW51IG9mIG9wdGlvbnMgcHJvdmlkZWQgdG8geW91IGJ5IEFtYXpvbi4NCg0KIyMgREIgQ29ubmVjdGlvbg0KDQpGcm9tIGhlcmUgaXQncyB0b3RhbGx5IGZlYXNpYmxlIHRvIGNvbm5lY3QgdG8geW91ciBkYXRhYmFzZSB0aHJvdWdoIFIuIEkgbGlrZSB0byBwcmVmYWNlIG15IFIgY29ubmVjdGlvbiB3aXRoIGEgY29ubmVjdGlvbiB2aWEgc29tZSBvdGhlciBkYXRhYmFzZSBjbGllbnQuIFNpbmNlIHRoZSBEQiBJJ20gdXNpbmcgaGVyZSBpcyBhIE15U1FMIGRhdGFiYXNlLCBJIHVzZWQgTXlTUUwgV29ya2JlbmNoIChhIGZyZWUgTXlTUUwgY2xpZW50KSB0byBlc3RhYmxpc2ggbXkgaW5pdGlhbCBjb25uZWN0aW9uLiBUaGlzIGhhcyB0aGUgYWRkZWQgYmVuZWZpdCBvZiBnaXZpbmcgbWUgYSBuaWNlIHBvaW50eS1jbGlja3kgaW50ZXJmYWNlIHdpdGggd2hpY2ggdG8gd3JpdGUgbmV3IG9ic2VydmF0aW9ucyB0byBkYXRhYmFzZSB0YWJsZXMgYnkgcHVzaGluZyAuY3N2IGZpbGVzIHVwLg0KDQpUaGUgUiBjb25uZWN0aW9uIGlzIHJlYWxseSBzaW1wbGUgKG1vc3RseSBiZWNhdXNlIEkgZG9uJ3QgaGF2ZSBtdWNoIHNlY3VyaXR5IGZlbmNpbmcgb24gdGhpcyBkYXRhYmFzZSk6IGp1c3QgbmVlZCBhIGRhdGFiYXNlIGVuZHBvaW50IGFuZCBzb21lIGFjY2VzcyBjcmVkZW50aWFsczoNCg0KYGBge3J9DQpjbiA8LSBkYkNvbm5lY3QoZHJ2ICAgICAgPSBSTXlTUUw6Ok15U1FMKCksIA0KICAgICAgICAgICAgICAgIHVzZXJuYW1lID0gImFkbWluIiwgDQogICAgICAgICAgICAgICAgcGFzc3dvcmQgPSAibm1mc3NvY2lhbHNjaWVuY2UiLCANCiAgICAgICAgICAgICAgICBob3N0ICAgICA9ICJtYW1zLXRlYWNoaW5nLXB1YmxpYy5jNjVpNHRtdHR2cWwudXMtd2VzdC0xLnJkcy5hbWF6b25hd3MuY29tIiwgDQogICAgICAgICAgICAgICAgcG9ydCAgICAgPSAzMzA2LCANCiAgICAgICAgICAgICAgICBkYm5hbWUgICA9ICJuZWVkbGVfd2FzdGUiKQ0KDQpgYGANCg0KIyMgQW5hbHlzaXMNCg0KT25jZSB0aGUgZGF0YWJhc2UgY29ubmVjdGlvbiBpcyBlc3RhYmxpc2hlZCwgaXQgaXMgcHJldHR5IHN0cmFpZ2h0Zm9yd2FyZCB0byBpbXBvcnQgZGF0YSBhbmQgc3RhcnQgZG9pbmcgc3R1ZmYgd2l0aCBpdC4NCg0KVGhlIGBgYGRiR2V0UXVlcnkoKWBgYCBtZXRob2QgZnJvbSB0aGUgYGBgREJJYGBgIHBhY2thZ2UgYWxsb3dzIFNRTCBzeW50YXggdG8gYmUgcGFzc2VkIHRvIHRoZSBkYXRhYmFzZSBlbmdpbmUgYW5kIHJldHVybnMgcmVzdWx0cyB0byB0aGUgUiB3b3Jrc3BhY2UuIEhlcmUsIEknbSBqb2luaW5nIHR3byB0YWJsZXMgZnJvbSB0aGUgZGF0YWJhc2UgDQoNCmBgYHtyfQ0KZGYuam9pbmVkIDwtIGRiR2V0UXVlcnkoY24sICJTRUxFQ1QgKiBGUk9NIG5lZWRsZV9ldmVudHMgaW5uZXIgam9pbiBsb2NhdGlvbnNfZ2VvIG9uIG5lZWRsZV9ldmVudHMuTE9DQVRJT05fUkVDT1JERUQ9bG9jYXRpb25zX2dlby5MT0NBVElPTl9SRUNPUkRFRCIpDQpoZWFkKGRmLmpvaW5lZCkNCmBgYA0KDQojIE1pc2MuIFRha2Vhd2F5cw0KDQoxLiBJdCBzZWVtcyB0aGF0IGl0IGlzIHBvc3NpYmxlIHRvIHBvc3QgaHRtbCBhbmQgaHRtbCBub3RlYm9vayBmaWxlcyB0byBhIEpla3lsbCBCbG9nLiBUaGlzIGlzIGtpbmRhIGNvb2wsIEkgaGF2ZSBiZWVuIHJlbmRlcmluZyBzdHVmZiB0byAubWQgZmlsZXMgYmVmb3JlIHBvc3RpbmcgYW5kIGl0J3MgY29vbCB0byBrbm93IEkgZG9uJ3QgaGF2ZSB0by4NCg0KMi4gVGhlIGBgYGdvb2dsZWRyaXZlYGBgIHBhY2thZ2UgaGFzIHNvbWUgbmljZSB3cmFwcGVycyBmb3Igd29ya2luZyB3aXRoIEdvb2dsZSBEcml2ZS4gSSdtIG5vdCBzZWVpbmcgYSBsb3Qgb2YgdXRpbGl0eSBoZXJlIHRoYXQncyByZWFsbHkgZ2FtZS1jaGFuZ2luZyBmb3IgbXkgd29ya2Zsb3cuLi5idXQgbWF5YmUgaWYgSSB3b3JrIHdpdGggaXQgYSBsaXR0bGUgbW9yZSBJJ2xsIHdhcm0tdXAgdG8gaXQuDQoNCjMuIEkgcmVhbGx5IGxpa2UgcmVuZGVyaW5nIHRhYmxlcyB1c2luZyBgYGBEVDo6ZGF0YXRhYmxlKGRhdGFmcmFtZSlgYGAgaW5zaWRlIG15IFJtYXJrZG93bnMuIEkganVzdCBsaWtlIHRvIHdheSBpdCBtYWtlcyBteSB0YWJsZXMgbG9vay4gSSB3YXNuJ3QgYWJsZSB0byBkbyB0aGlzIHdpdGggbXkgR2l0SHViIGJsb2cuIFNvbWV0aGluZyBhYm91dCB0aGlzIHRhYmxlIGZvcm1hdHRpbmcgd2FzIGdlbmVyYXRpbmcgaHRtbCB0YWdzIHRoYXQgY291bGRuJ3QgYmUgcmVuZGVyZWQuDQoNCjQuIFRoZXJlJ3MgcHJvYmFibHkgYSBsb3Qgb2YgcmVkLXRhcGUgdHlwZSBzdHVmZiBpbnZvbHZlZCBpbiB1c2luZyBBV1MgYXMgZmVkZXJhbCBlbXBsb3llZXMuIEFzIGV2aWRlbmNlZCBieSBbQW1hem9uJ3MgZXhpdGluZyBwdWJsaWMgc2VjdG9yIHBhcnRuZXJzaGlwc10oaHR0cHM6Ly9hd3MuYW1hem9uLmNvbS9ibG9ncy9wdWJsaWNzZWN0b3Ivbm9hYS1hbmQtYXdzLWV4cGFuZC1jb21taXRtZW50LXRvLWluY3JlYXNlLWFjY2Vzcy10by1lbnZpcm9ubWVudGFsLWRhdGEvKSwgaXQncyBkZWZpbml0ZWx5IHBvc3NpYmxlIHRvIHVzZSBBbWF6b24ncyBjbG91ZCBmb3IgZ29iJ21lbnQgZGF0YS4gSG93ZXZlciwgSSBkb24ndCBrbm93IGFueSBvZiB0aGUgcGFydGljdWxhcnMgYWJvdXQgd2hlbi93aGVyZS9ob3cgaXQgbWlnaHQgYmUgYXBwcm9wcmlhdGUgZm9yIGZlZGVyYWwgZW1wbG95ZWVzIHRvIHVzZSBBV1MgdG8gd2FyZWhvdXNlIGRhdGEuIEkgZG8ga25vdyB0aGF0IA0KDQoqIEFXUy1SRFMgaXMgbm90IHRoYXQgaGFyZCB0byB1c2Ugb25jZSB5b3UgZ2V0IHRoZSBoYW5nIG9mIGl0LCBhbmQNCiogaXQncyBwcmV0dHkgc21vb3RoIHRvIHBpcGUgZGF0YSBpbnRvIFIgZnJvbSBhbiBBbWF6b24gY2xvdWQuDQoNCjUuIElmIHlvdSdyZSBjdXJpb3VzLCB0aGUgYmlnIHJvYWRibG9jayBmb3IgbWUgaW4gdGVybXMgb2YgYWN0dWFsbHkgdXNpbmcgSmVreWxsIHRvIG1hbmFnZSBibG9nIGNvbnRlbnQgd2FzIGEgUnVieSB2ZXJzaW9uIGNvbmZsaWN0IGJldHdlZW4gd2hhdCBKZWt5bGwgcmVxdWlyZWQgYW5kIHdoYXQgbXkgTWFjIE9TIGhhZCBpbnN0YWxsZWQuIEZvciBzb21lIHJlYXNvbiB0aGlzIGF0ZSB1cCBsaWtlIDIgd2Vla3Mgb2YgbXkgbGlmZSBhbmQgbmV2ZXIgZ290IHJlc29sdmVkLiBJJ3ZlIHNpbmNlIHVwZGF0ZWQgdG8gTWFjIENhdGFsaW5hICh3aGljaCBpcyBwdXJwb3J0ZWQgdG8gc2hpcCB3aXRoIFJ1YnkgMi42LjMpIHNvIEknbSBndWVzc2luZyB0aGUgaXNzdWUgd291bGQgcHJvYmFibHkgcmVzb2x2ZSBpdHNlbGYgaWYgSSBjYXJlZCBlbm91Z2ggdG8gdHJ5IGFuZCBydW4gSmVreWxsIGFnYWluLi4uLmJ1dCBzaW5jZSBJIGRvbid0IGhhdmUgdG8sIEknbSBwcm9iYWJseSBub3QgZ29ubmEuICAgDQoNCg0KIyBVbnNvbGljaXRlZCBDb21tZW50YXJ5DQoNCkkgdGhpbmsgYSBsb3Qgb2YgdXMgd2VyZSBtb3ZlZCAocHJvYmFibHkgdG8gdmFyeWluZyBkZWdyZWVzKSBieSB0aGUgW1JpY2hhcmQgTWNFbHJlYXRoIHZpZGVvXShodHRwczovL3lvdXR1LmJlL3p3UmRPOV9HR2hZKSB0aGF0IFJveSByZWNlbnRseSBkaXN0cmlidXRlZC4gVGhlIHBvcnRpb24gb2YgdGhlIHRhbGsgdGhhdCByZXNvbmF0ZWQgbW9zdCB3aXRoIG1lIHdpdGggd2FzIHRoZSBlbXBoYXNpcyBwbGFjZWQgb24gcHJvcGVyIGRhdGEgbWFuYWdlbWVudC9kYXRhIGN1cmF0aW9uIHByYWN0aWNlcy4gQmFzaWNhbGx5LCBpdCdzIGhhcmQgZm9yIHJlc2VhcmNoIHRvIGJlIHJlcHJvZHVjaWJsZSBpZiB0aGUgZGF0YSBhcmVuJ3Qgc2hhcmVhYmxlLiBBbmQgaXQncyBoYXJkIGZvciBkYXRhIHRvIGJlIHNoYXJlYWJsZSBpZiB0aGV5IGV4aXN0IGFzIGEgcG90cG91cnJpIG9mIGZsYXQgZmlsZXMgb24gc29tZWJvZHkncyBoYXJkLWRyaXZlLiANCg0KSSdkIGxpa2UgdG8gdGhpbmsgdGhhdCB3aGF0IEkgcHJlc2VudGVkIGhlcmUgY291bGQga2luZCBvZiBiZSBtYXJrZXRlZCBhcyBhICJzdGFydGVyIGtpdCIgZm9yIHdvcmtpbmcgd2l0aCBkYXRhIGluICJ0aGUgY2xvdWQiLi4ud2hpY2ggaXMga2luZCBvZiBhbiBpbXBvcnRhbnQgc2tpbGwgZm9yIHRob3NlIGludGVyZXN0ZWQgaW4gcmVwcm9kdWNpYmxlIHJlc2VhcmNoLiAgIA0KDQpDb25zaWRlciB0aGUgZm9sbG93aW5nIHBvcHVsYXIgd29ya2Zsb3cgZm9yIG1hbmFnaW5nIHJlc2VhcmNoIHByb2plY3RzIHdpdGggbXVsdGlwbGUgY29udHJpYnV0b3JzOg0KDQpgYGBHaXRodWIgZm9yayAtLT4gZ2l0IGNsb25lIC0tPiBSIFN0dWRpbyBjb2RlIC0tPiBnaXQgY29tbWl0IC0tPiBnaXQgcHVzaC9wdWxsYGBgLg0KDQpUaGlzIGlzIGdvb2QgZm9yIGNvbGxhYm9yYXRpdmUgYW5hbHlzaXMgYnV0IEdpdEh1YiBpc24ndCBhIGdyZWF0IHBsYWNlIGZvciBzb3VyY2UgZGF0YS4gU28gaWYgdGhlIHNvdXJjZSBkYXRhIGFyZSBiZWluZyBwZXJpb2RpY2FsbHkgdXBkYXRlZCwgeW91IG5lZWQgYSBkYXRhIGRpc3RyaWJ1dGlvbiBwbGF0Zm9ybSB0aGF0J3MgYWNjZXNzaWJsZSB0byBhbGwgcHJvamVjdCBjb2xsYWJvcmF0b3JzLg0KDQpHb29nbGUgRHJpdmUgYW5kIEFtYXpvbiBXZWIgU2VydmljZXMgYXJlIHR3byByZWxhdGl2ZWx5IGxvdy1jb3N0IGNsb3VkIGRhdGEgc3RvcmFnZSBwbGF0Zm9ybXMuIFRoaXMgbWFrZXMgdGhlbSBuaWNlIG9wdGlvbnMgZm9yIHByYWN0aWNpbmcgY29sbGFib3JhdGl2ZSBkYXRhIG1hbmFnZW1lbnQuIA0KDQo=