If you’re here, you’ve probably already setup a new project and created this notebook file - but if not, go to the square on the top right of RStudio to create a new project.


This document steps through an OpenRefine tutorial (see https://goo.gl/mQC7oe) adapted from other materials, in R…it demonstrates how fantastic OpenRefine is in many respects!

This resource is adapted from: 1. http://enipedia.tudelft.nl/wiki/OpenRefine_Tutorial#Clean_up_country_names (under a CC-By-SA 3.0 license) C.B.Davis, A. Chmieliauskas, G.P.J. Dijkema, I. Nikolic (2015), Enipedia, http://enipedia.tudelft.nl, Energy & Industry group, Faculty of Technology, Policy and Management, TU Delft, Delft, The Netherlands. 2. And http://schoolofdata.org/handbook/recipes/cleaning-data-with-refine/ Both under a CC-By-SA license. To cite, Knight, S., (2016). Cleaning Data with Refine,


Open Refine (previously Google Refine) is a data cleaning software that uses your web browser as an interface. This means it will look like it runs on the internet but all your data remains on your machine and you do not need internet connection to work with it. The main aim of Refine is to help you exploring and cleaning your data before you use it further. It is built for large datasets – so don’t worry as long as your spreadsheets can keep the information: Refine can as well. To work with your data in Refine you need to start a new project: Walkthrough: Creating a Refine project 1. Start Refine – this will open a browser window pointing to http://127.0.0.1:3333 if this doesn’t happen open the link with your browser directly 2. Create a new project: On the left tab select the “Create Project” tab:

  1. Click on “Choose Files” to choose your downloaded file and click on “next” – you can also use the URL to the CSV directly if your data is hosted on the web.
  2. You will get a preview on how refine will interpret your data – if you have selected a well formatted CSV or other file: this should be pretty automatic.
  3. Review the preview carefully to make sure the data looks right. Double check character encoding. Much, but not all data uses UTF-8 these days, but make sure you don’t see any funny characters in preview.
  4. You may want to turn off “guess data types”, particularly if you have data that contains leading zeros in numbers or identifiers which are significant.
  5. Name your project in the box on the top right side and click on “Create Project”

  6. The project will open in the project view, this is the basic interface you are going to work with: by default refine shows only 10 rows of data, you can change this on the bar above the data rows. Also you can use the navigation on the right to see the next or previous rows. You now have successfully created your first Refine project. Remember: although it runs in a web-browser, the Refine server is still on your machine – all the data is there (so no worries if you handle sensitive information)

#The following code roughly mirrors this bit in OpenRefine (stringsAsFactors=F is broadly equivalent to telling OpenRefine not to predict the datatypes)
unis <- read.csv("https://goo.gl/EJxa20", header=T, stringsAsFactors=F)

Once we created our project, let’s go and explore the data and the Refine interface a bit. Using Refine might be intimidating at first, since it seems so different from spreadsheets, once you get used to it you will notice how easily you can do things with it. One of the commonly used functions in spreadsheets is sorting and filtering data – to figure out minima, maxima or things about certain categories. Refine can do the same thing. Walkthrough: Sorting rows 1. Refine handles data similar to a spreadsheet: you have rows, columns and cells – a cell is a field defined by a row and a column. 2. To sort your rows based on a specific column click on the small downward triangle next to the column

  1. Select “Sort…” to open the sorting dialog
  2. You can select what to sort the values as and then what order to sort in. (We’ll sort in text, since for now we only have text columns)
  3. Click “OK” and your rows will be sorted based on the column
  4. To undo the sort, click on the column options again, select “sort” then “remove sort”
#probably more useful than sorting, is to view the head or tail of a file
head(unis)
unis[1:100,]
tail(unis,100)
tail(unis)
colnames(unis)
 [1] "university"   "endowment"    "numFaculty"   "numDoctoral" 
 [5] "country"      "numStaff"     "established"  "numPostgrad" 
 [9] "numUndergrad" "numStudents" 
#we can also sort it
unis <- unis[with(unis, order(country, university)), ]
head(unis)
unis

Walkthrough: Facetting rows based on a column 1. Select the column options for the column you want to facet with 2. Select “Facet”

  1. You can facet differently for text, numbers or dates – let’s facet as text – click on “Text facet”
  2. This will open a facet in the left bar

  3. Now select one or more of the choices and you’ll see how your data rows are limited to just those selected.
  4. Of course you can add more than one facet and thus filter more than once.

#in R this is probably most easily achieved using the subset function
#see http://www.statmethods.net/management/subset.html 
subset(unis, university == "Cardiff University" | university == "Acadia University", select = c("university", "country", "established"))
#this is the same thing, it's considered superior for reasons that don't matter here
unis[c("university","country","established")][unis$university == "Cardiff University" | unis$university == "Acadia University", ]
#but often, we'll want our filter to be more subtle than this, consider for example
subset(unis, university == "Cambridge", select = c("university", "country", "established"))
#but...we know that institution exists
#so, for example...
unis[grep("Cambridge", unis$university), ]
subset(unis, grepl("Cambridge", unis$university))
#note this isn't quite right ("Cambridge College" is a rather different place)

If you look closely at some facets, you’ll notice that on the bottom you have a selector saying “(blank)”. You’ll also see some duplicate options (e.g. N/A, NA, none, etc.)

Walkthrough: Filling in the (blank)s 1. Choose the “(blank)” facet in your “numPostgrad” column 2. Hover over ‘edit’ and replace (blank) with NA (or whatever other value)

  1. You can also edit individual values within the table on the right

  2. Use the menu to navigate to ‘cluster and edit’ (note the other options here are also very useful)

  3. This allows you to merge multiple the multiple NA values using string matching – note that ‘none’ (i.e., 0) is not the same as NA (not available)
  4. Play with the different options on the ‘country’ column and merge the values

#here it's useful to do two things, first learn about replacement, e.g.:
unis$established[unis$established==""] <- NA
#str_replace from the stringr package is also very useful

#and to explore your values
table(unis$established)

#hm...there are a lot of those, how about
table(unis[c("established")][is.character(unis$established),])
#ok so it looks like there might be two options here.
#either, we use a fuzzy matching (e.g. using agrep) to identify strings that look similar, possibly using a ground truth (e.g. a reference list of university names) to match these against
#in OpenRefine this is called reconciliation http://freeyourmetadata.org/reconciliation/ 


#or - a slower mechanism - we use entity recognition
#it's worth noting that in this discussion of a similar problem, someone recommends using openrefine! https://stackoverflow.com/questions/6683380/techniques-for-finding-near-duplicate-records 
#https://www.r-bloggers.com/merging-data-sets-based-on-partially-matched-data-elements/ and this great blog post discusses implementing some of the openrefine approaches in R (although, only partially, and not all of them - i.e., openrefine is really really powerful!)

#using entity  recognition!
#https://rpubs.com/lmullen/nlp-chapter
library(openNLP)
library(NLP)
entities <- function(doc, kind) {
  s <- doc$content
  a <- annotations(doc)[[1]]
  if(hasArg(kind)) {
    k <- sapply(a$features, `[[`, "kind")
    s[a[k == kind]]
  } else {
    s[a[a$type == "entity"]]
  }
}
#text <- c("the year 2015 is now")
word_ann <- Maxent_Word_Token_Annotator()
sent_ann <- Maxent_Sent_Token_Annotator()
location_ann <- Maxent_Entity_Annotator(kind = "location")
organization_ann <- Maxent_Entity_Annotator(kind = "organization")
money_ann <- Maxent_Entity_Annotator(kind = "money")
date_ann <- Maxent_Entity_Annotator(kind = "date")
pipeline <- list(sent_ann,
                 word_ann,
                 location_ann,
                 organization_ann,
                 money_ann,
                 date_ann)

Note we only need to do this for unique values, given it’s very timeconsuming it’s well worth subsetting and rejoining

estab <- as.data.frame(unique(unis$established))
estab$established_ann <- lapply(estab$`unique(unis$established)`, function(x) annotate(x, list(sent_ann,word_ann,date_ann)))
estab$established_ann_plain <- apply(estab[c("unique(unis$established)","established_ann")], 1, function(x) AnnotatedPlainTextDocument(x[[1]], x[[2]]))
estab$established_date_extract <- lapply(estab$established_ann_plain, function(x) (x)[[1]][[1]])
estab$established_date_extract <- unlist(estab$established_date_extract)
#a tonne of cleaning is likely here, but at least its of date values!
unis <- merge(unis, estab[c("unique(unis$established)","established_date_extract")], by.x="established", by.y="unique(unis$established)" )
#let's just save this, we don't want to need  to redo the labelling! 
#we need to select columns if you want to save as csv because you can't save list 
save(unis,file="unis.Rda")
library(stringr) #for some cleaning
package 㤼㸱stringr㤼㸲 was built under R version 3.3.3
unis$university <- str_trim(unis$university,side = "both")
#converting things like this is surprisingly hard if you don't know what to search for...: ("Universidad Ju%C3%A1rez Aut%C3%B3noma de Tabasco")
org <- as.data.frame(unique(unis$university))
org$uni_org <- lapply(org$`unique(unis$university)`, function(x) annotate(x, list(sent_ann,word_ann,organization_ann)))
org$uni_org_plain <- apply(org[c("unique(unis$university)","uni_org")], 1, function(x) AnnotatedPlainTextDocument(x[[1]], x[[2]]))
org$uni_org_extract <- lapply(org$uni_org_plain, function(x) (x)[[1]][[1]])
org$uni_org_extract <- unlist(org$uni_org_extract)
View(org[grep("Cambridge", org$`unique(unis$university)`), ])
unis <- merge(unis, org[c("unique(unis$university)", "uni_org_extract")], by.x = "university", by.y="unique(unis$university)")
save(unis,file="unis.Rda")
#how do we convert words to numbers, given messy data?
endow <- as.data.frame(unique(unis$endowment))
endow$endowment_mon <- lapply(endow$`unique(unis$endowment)`, function(x) annotate(x, list(sent_ann,word_ann,money_ann)))
endow$endowment_mon_plain <- apply(endow[c("unique(unis$endowment)","endowment_mon")], 1, function(x) AnnotatedPlainTextDocument(x[[1]], x[[2]]))
endow$endowment_mon_extract <- lapply(endow$endowment_mon_plain, function(x) (x)[[1]][[1]])
endow$endowment_mon_extract <- unlist(endow$endowment_mon_extract)  #I haven't checked these at all, do they look about right?
unis <- merge(unis, endow[c("unique(unis$endowment)", "endowment_mon_extract")], by.x="endowment", by.y="unique(unis$endowment)")
save(unis,file="unis.Rda")
#load("unis.Rda")
#write.csv(unis,"unis.csv") #let's just save this, we don't want to need  to redo the 
#extract locations, but also merge near duplicates
#devtools::install_github("ropensci/monkeylearn")

Step 8: Deduplicate entries ([near] duplicate rows) There are a lot of (nearly) duplicate rows in the data, this can happen for various reasons including human error and because multiple historic values are stored. We want to keep just one copy. To do this (based on documentation here), click on the column with the university names, and then click on “Sort”. Once you do this, you will notice that there is a new “Sort” menu at the top. Click on this and select “Reorder rows permanently”. This may take a while as it renumbers the rows in which the entries appear.

Then on the column with university names, Edit cells -> Blank down

Then on the same column, Facet -> Customized facets -> Facet by blank Now we want to remove all the blank rows, so select true, then on the “All” column on the left, Edit rows -> Remove all matching rows, like you have done when working with the numStudents and endowment columns. Once you remove all the facets, and you now have a (mostly) cleaned data set.

#rather than do it this way, we'd probably use the entity recognition approach

Step 9: Exploring the data with scatter plots Click on the “endowment” column, Facet -> Scatterplot facet. This shows the relationships between all of the numeric values in each of the columns. Click on “log” to get a better view.

Click on the plot for endowment vs. numStudents. You can now drag select a portion of the plot, and then see the rows corresponding to that selection.

Step 10: Geocoding names and addresses This next part shows (based on documentation here) how to go from a description of a place (i.e. the name of a university) to values for its (likely) geographic coordinates. Behind the scenes, this uses Google Maps to figure out what is the most likely location you are asking for. To learn how to do this, you don’t need to do process the whole data set. This can take a while, and Google limits you to 2000 requests per day. It’s better to just select around 10 rows and verify that it works. An easy way to get a limited set of rows is by using a numeric log facet of the number of students, so use Facet -> Customized facets -> Numeric log facet

Use this facet to make a selection of around ten rows, and then check the matching rows number to verify that you have a reasonable selection size:

Now the fun begins and we want to do Edit column -> Add column by fetching URLs. In other words, the values of the cells in the new column are based on data that is retrieved from the Internet. Enter in the expression below, and you should see a list of URLs with the names of the universities at the end of the URLs. Specify a new column name such as “geocodingResponse”, and set the throttle delay to around 500 milliseconds. “http://maps.google.com/maps/api/geocode/json?sensor=false&address=” + escape(value, “url”)

You should get a bunch of data back. To convert this into a more readable format, you need to click on the geocodingResponse column, and then on Edit column -> Add column based on this column. Enter in the expression below with(value.parseJson().results[0].geometry.location, pair, pair.lat +“,” + pair.lng) Now you have a single column with coordinates. You can split this into columns for latitude and longitude by selecting Edit Column -> Split into several columns and specifying a separator of “,”. These columns can then be renamed using Edit Column -> Rename this column.

load.file("unis_location.Rda")
Error: could not find function "load.file"

Step 11: Export Data The data can be exported to formats such as Excel. If you read this into tools such as SPSS and notice that the last column is missing, then open the file up in Excel, re-save it, and try to open it up again in SPSS.

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KSWYgeW91J3JlIGhlcmUsIHlvdSd2ZSBwcm9iYWJseSBhbHJlYWR5IHNldHVwIGEgbmV3IHByb2plY3QgYW5kIGNyZWF0ZWQgdGhpcyBub3RlYm9vayBmaWxlIC0gYnV0IGlmIG5vdCwgZ28gdG8gdGhlIHNxdWFyZSBvbiB0aGUgdG9wIHJpZ2h0IG9mIFJTdHVkaW8gdG8gY3JlYXRlIGEgbmV3IHByb2plY3QuDQoNCioqKioqKg0KDQpUaGlzIGRvY3VtZW50IHN0ZXBzIHRocm91Z2ggYW4gT3BlblJlZmluZSB0dXRvcmlhbCAoc2VlIGh0dHBzOi8vZ29vLmdsL21RQzdvZSkgYWRhcHRlZCBmcm9tIG90aGVyIG1hdGVyaWFscywgaW4gUi4uLml0IGRlbW9uc3RyYXRlcyBob3cgZmFudGFzdGljIE9wZW5SZWZpbmUgaXMgaW4gbWFueSByZXNwZWN0cyEgDQoNClRoaXMgcmVzb3VyY2UgaXMgYWRhcHRlZCBmcm9tOg0KMS4JaHR0cDovL2VuaXBlZGlhLnR1ZGVsZnQubmwvd2lraS9PcGVuUmVmaW5lX1R1dG9yaWFsI0NsZWFuX3VwX2NvdW50cnlfbmFtZXMgKHVuZGVyIGEgQ0MtQnktU0EgMy4wIGxpY2Vuc2UpIEMuQi5EYXZpcywgQS4gQ2htaWVsaWF1c2thcywgRy5QLkouIERpamtlbWEsIEkuIE5pa29saWMgKDIwMTUpLCBFbmlwZWRpYSwgaHR0cDovL2VuaXBlZGlhLnR1ZGVsZnQubmwsIEVuZXJneSAmIEluZHVzdHJ5IGdyb3VwLCBGYWN1bHR5IG9mIFRlY2hub2xvZ3ksIFBvbGljeSBhbmQgTWFuYWdlbWVudCwgVFUgRGVsZnQsIERlbGZ0LCBUaGUgTmV0aGVybGFuZHMuIA0KMi4JQW5kIGh0dHA6Ly9zY2hvb2xvZmRhdGEub3JnL2hhbmRib29rL3JlY2lwZXMvY2xlYW5pbmctZGF0YS13aXRoLXJlZmluZS8gDQpCb3RoIHVuZGVyIGEgQ0MtQnktU0EgbGljZW5zZS4gVG8gY2l0ZSwgS25pZ2h0LCBTLiwgKDIwMTYpLiBDbGVhbmluZyBEYXRhIHdpdGggUmVmaW5lLCANCg0KKioqKioqDQoNCk9wZW4gUmVmaW5lIChwcmV2aW91c2x5IEdvb2dsZSBSZWZpbmUpIGlzIGEgZGF0YSBjbGVhbmluZyBzb2Z0d2FyZSB0aGF0IHVzZXMgeW91ciB3ZWIgYnJvd3NlciBhcyBhbiBpbnRlcmZhY2UuIFRoaXMgbWVhbnMgaXQgd2lsbCBsb29rIGxpa2UgaXQgcnVucyBvbiB0aGUgaW50ZXJuZXQgYnV0IGFsbCB5b3VyIGRhdGEgcmVtYWlucyBvbiB5b3VyIG1hY2hpbmUgYW5kIHlvdSBkbyBub3QgbmVlZCBpbnRlcm5ldCBjb25uZWN0aW9uIHRvIHdvcmsgd2l0aCBpdC4NClRoZSBtYWluIGFpbSBvZiBSZWZpbmUgaXMgdG8gaGVscCB5b3UgZXhwbG9yaW5nIGFuZCBjbGVhbmluZyB5b3VyIGRhdGEgYmVmb3JlIHlvdSB1c2UgaXQgZnVydGhlci4gSXQgaXMgYnVpbHQgZm9yIGxhcmdlIGRhdGFzZXRzIOKAkyBzbyBkb27igJl0IHdvcnJ5IGFzIGxvbmcgYXMgeW91ciBzcHJlYWRzaGVldHMgY2FuIGtlZXAgdGhlIGluZm9ybWF0aW9uOiBSZWZpbmUgY2FuIGFzIHdlbGwuDQpUbyB3b3JrIHdpdGggeW91ciBkYXRhIGluIFJlZmluZSB5b3UgbmVlZCB0byBzdGFydCBhIG5ldyBwcm9qZWN0Og0KV2Fsa3Rocm91Z2g6IENyZWF0aW5nIGEgUmVmaW5lIHByb2plY3QNCjEuCVN0YXJ0IFJlZmluZSDigJMgdGhpcyB3aWxsIG9wZW4gYSBicm93c2VyIHdpbmRvdyBwb2ludGluZyB0byBodHRwOi8vMTI3LjAuMC4xOjMzMzMgaWYgdGhpcyBkb2VzbuKAmXQgaGFwcGVuIG9wZW4gdGhlIGxpbmsgd2l0aCB5b3VyIGJyb3dzZXIgZGlyZWN0bHkNCjIuCUNyZWF0ZSBhIG5ldyBwcm9qZWN0OiBPbiB0aGUgbGVmdCB0YWIgc2VsZWN0IHRoZSDigJxDcmVhdGUgUHJvamVjdOKAnSB0YWI6DQoNCjMuCUNsaWNrIG9uIOKAnENob29zZSBGaWxlc+KAnSB0byBjaG9vc2UgeW91ciBkb3dubG9hZGVkIGZpbGUgYW5kIGNsaWNrIG9uIOKAnG5leHTigJ0g4oCTIHlvdSBjYW4gYWxzbyB1c2UgdGhlIFVSTCB0byB0aGUgQ1NWIGRpcmVjdGx5IGlmIHlvdXIgZGF0YSBpcyBob3N0ZWQgb24gdGhlIHdlYi4NCjQuCVlvdSB3aWxsIGdldCBhIHByZXZpZXcgb24gaG93IHJlZmluZSB3aWxsIGludGVycHJldCB5b3VyIGRhdGEg4oCTIGlmIHlvdSBoYXZlIHNlbGVjdGVkIGEgd2VsbCBmb3JtYXR0ZWQgQ1NWIG9yIG90aGVyIGZpbGU6IHRoaXMgc2hvdWxkIGJlIHByZXR0eSBhdXRvbWF0aWMuDQo1LglSZXZpZXcgdGhlIHByZXZpZXcgY2FyZWZ1bGx5IHRvIG1ha2Ugc3VyZSB0aGUgZGF0YSBsb29rcyByaWdodC4gRG91YmxlIGNoZWNrIGNoYXJhY3RlciBlbmNvZGluZy4gTXVjaCwgYnV0IG5vdCBhbGwgZGF0YSB1c2VzIFVURi04IHRoZXNlIGRheXMsIGJ1dCBtYWtlIHN1cmUgeW91IGRvbuKAmXQgc2VlIGFueSBmdW5ueSBjaGFyYWN0ZXJzIGluIHByZXZpZXcuDQo2LglZb3UgbWF5IHdhbnQgdG8gdHVybiBvZmYg4oCcZ3Vlc3MgZGF0YSB0eXBlc+KAnSwgcGFydGljdWxhcmx5IGlmIHlvdSBoYXZlIGRhdGEgdGhhdCBjb250YWlucyBsZWFkaW5nIHplcm9zIGluIG51bWJlcnMgb3IgaWRlbnRpZmllcnMgd2hpY2ggYXJlIHNpZ25pZmljYW50Lg0KNy4JTmFtZSB5b3VyIHByb2plY3QgaW4gdGhlIGJveCBvbiB0aGUgdG9wIHJpZ2h0IHNpZGUgYW5kIGNsaWNrIG9uIOKAnENyZWF0ZSBQcm9qZWN04oCdDQoNCjguCVRoZSBwcm9qZWN0IHdpbGwgb3BlbiBpbiB0aGUgcHJvamVjdCB2aWV3LCB0aGlzIGlzIHRoZSBiYXNpYyBpbnRlcmZhY2UgeW91IGFyZSBnb2luZyB0byB3b3JrIHdpdGg6IGJ5IGRlZmF1bHQgcmVmaW5lIHNob3dzIG9ubHkgMTAgcm93cyBvZiBkYXRhLCB5b3UgY2FuIGNoYW5nZSB0aGlzIG9uIHRoZSBiYXIgYWJvdmUgdGhlIGRhdGEgcm93cy4gQWxzbyB5b3UgY2FuIHVzZSB0aGUgbmF2aWdhdGlvbiBvbiB0aGUgcmlnaHQgdG8gc2VlIHRoZSBuZXh0IG9yIHByZXZpb3VzIHJvd3MuDQpZb3Ugbm93IGhhdmUgc3VjY2Vzc2Z1bGx5IGNyZWF0ZWQgeW91ciBmaXJzdCBSZWZpbmUgcHJvamVjdC4gUmVtZW1iZXI6IGFsdGhvdWdoIGl0IHJ1bnMgaW4gYSB3ZWItYnJvd3NlciwgdGhlIFJlZmluZSBzZXJ2ZXIgaXMgc3RpbGwgb24geW91ciBtYWNoaW5lIOKAkyBhbGwgdGhlIGRhdGEgaXMgdGhlcmUgKHNvIG5vIHdvcnJpZXMgaWYgeW91IGhhbmRsZSBzZW5zaXRpdmUgaW5mb3JtYXRpb24pDQoNCmBgYHtyfQ0KI1RoZSBmb2xsb3dpbmcgY29kZSByb3VnaGx5IG1pcnJvcnMgdGhpcyBiaXQgaW4gT3BlblJlZmluZSAoc3RyaW5nc0FzRmFjdG9ycz1GIGlzIGJyb2FkbHkgZXF1aXZhbGVudCB0byB0ZWxsaW5nIE9wZW5SZWZpbmUgbm90IHRvIHByZWRpY3QgdGhlIGRhdGF0eXBlcykNCnVuaXMgPC0gcmVhZC5jc3YoImh0dHBzOi8vZ29vLmdsL0VKeGEyMCIsIGhlYWRlcj1ULCBzdHJpbmdzQXNGYWN0b3JzPUYpDQpgYGANCg0KKioqKioqDQoNCk9uY2Ugd2UgY3JlYXRlZCBvdXIgcHJvamVjdCwgbGV04oCZcyBnbyBhbmQgZXhwbG9yZSB0aGUgZGF0YSBhbmQgdGhlIFJlZmluZSBpbnRlcmZhY2UgYSBiaXQuIFVzaW5nIFJlZmluZSBtaWdodCBiZSBpbnRpbWlkYXRpbmcgYXQgZmlyc3QsIHNpbmNlIGl0IHNlZW1zIHNvIGRpZmZlcmVudCBmcm9tIHNwcmVhZHNoZWV0cywgb25jZSB5b3UgZ2V0IHVzZWQgdG8gaXQgeW91IHdpbGwgbm90aWNlIGhvdyBlYXNpbHkgeW91IGNhbiBkbyB0aGluZ3Mgd2l0aCBpdC4NCk9uZSBvZiB0aGUgY29tbW9ubHkgdXNlZCBmdW5jdGlvbnMgaW4gc3ByZWFkc2hlZXRzIGlzIHNvcnRpbmcgYW5kIGZpbHRlcmluZyBkYXRhIOKAkyB0byBmaWd1cmUgb3V0IG1pbmltYSwgbWF4aW1hIG9yIHRoaW5ncyBhYm91dCBjZXJ0YWluIGNhdGVnb3JpZXMuIFJlZmluZSBjYW4gZG8gdGhlIHNhbWUgdGhpbmcuDQpXYWxrdGhyb3VnaDogU29ydGluZyByb3dzDQoxLglSZWZpbmUgaGFuZGxlcyBkYXRhIHNpbWlsYXIgdG8gYSBzcHJlYWRzaGVldDogeW91IGhhdmUgcm93cywgY29sdW1ucyBhbmQgY2VsbHMg4oCTIGEgY2VsbCBpcyBhIGZpZWxkIGRlZmluZWQgYnkgYSByb3cgYW5kIGEgY29sdW1uLg0KMi4JVG8gc29ydCB5b3VyIHJvd3MgYmFzZWQgb24gYSBzcGVjaWZpYyBjb2x1bW4gY2xpY2sgb24gdGhlIHNtYWxsIGRvd253YXJkIHRyaWFuZ2xlIG5leHQgdG8gdGhlIGNvbHVtbg0KDQozLglTZWxlY3Qg4oCcU29ydOKApuKAnSB0byBvcGVuIHRoZSBzb3J0aW5nIGRpYWxvZw0KNC4JWW91IGNhbiBzZWxlY3Qgd2hhdCB0byBzb3J0IHRoZSB2YWx1ZXMgYXMgYW5kIHRoZW4gd2hhdCBvcmRlciB0byBzb3J0IGluLiAoV2XigJlsbCBzb3J0IGluIHRleHQsIHNpbmNlIGZvciBub3cgd2Ugb25seSBoYXZlIHRleHQgY29sdW1ucykNCjUuCUNsaWNrIOKAnE9L4oCdIGFuZCB5b3VyIHJvd3Mgd2lsbCBiZSBzb3J0ZWQgYmFzZWQgb24gdGhlIGNvbHVtbg0KNi4JVG8gdW5kbyB0aGUgc29ydCwgY2xpY2sgb24gdGhlIGNvbHVtbiBvcHRpb25zIGFnYWluLCBzZWxlY3Qg4oCcc29ydOKAnSB0aGVuIOKAnHJlbW92ZSBzb3J04oCdDQoNCmBgYHtyfQ0KI3Byb2JhYmx5IG1vcmUgdXNlZnVsIHRoYW4gc29ydGluZywgaXMgdG8gdmlldyB0aGUgaGVhZCBvciB0YWlsIG9mIGEgZmlsZQ0KaGVhZCh1bmlzKQ0KDQp1bmlzWzE6MTAwLF0NCnRhaWwodW5pcywxMDApDQp0YWlsKHVuaXMpDQoNCmNvbG5hbWVzKHVuaXMpDQojd2UgY2FuIGFsc28gc29ydCBpdA0KdW5pcyA8LSB1bmlzW3dpdGgodW5pcywgb3JkZXIoY291bnRyeSwgdW5pdmVyc2l0eSkpLCBdDQoNCmhlYWQodW5pcykNCnVuaXMNCmBgYA0KDQpXYWxrdGhyb3VnaDogRmFjZXR0aW5nIHJvd3MgYmFzZWQgb24gYSBjb2x1bW4NCjEuCVNlbGVjdCB0aGUgY29sdW1uIG9wdGlvbnMgZm9yIHRoZSBjb2x1bW4geW91IHdhbnQgdG8gZmFjZXQgd2l0aA0KMi4JU2VsZWN0IOKAnEZhY2V04oCdDQogDQozLglZb3UgY2FuIGZhY2V0IGRpZmZlcmVudGx5IGZvciB0ZXh0LCBudW1iZXJzIG9yIGRhdGVzIOKAkyBsZXTigJlzIGZhY2V0IGFzIHRleHQg4oCTIGNsaWNrIG9uIOKAnFRleHQgZmFjZXTigJ0NCjQuCVRoaXMgd2lsbCBvcGVuIGEgZmFjZXQgaW4gdGhlIGxlZnQgYmFyDQogDQo1LglOb3cgc2VsZWN0IG9uZSBvciBtb3JlIG9mIHRoZSBjaG9pY2VzIGFuZCB5b3XigJlsbCBzZWUgaG93IHlvdXIgZGF0YSByb3dzIGFyZSBsaW1pdGVkIHRvIGp1c3QgdGhvc2Ugc2VsZWN0ZWQuDQo2LglPZiBjb3Vyc2UgeW91IGNhbiBhZGQgbW9yZSB0aGFuIG9uZSBmYWNldCBhbmQgdGh1cyBmaWx0ZXIgbW9yZSB0aGFuIG9uY2UuDQpgYGB7cn0NCiNpbiBSIHRoaXMgaXMgcHJvYmFibHkgbW9zdCBlYXNpbHkgYWNoaWV2ZWQgdXNpbmcgdGhlIHN1YnNldCBmdW5jdGlvbg0KI3NlZSBodHRwOi8vd3d3LnN0YXRtZXRob2RzLm5ldC9tYW5hZ2VtZW50L3N1YnNldC5odG1sIA0KDQpzdWJzZXQodW5pcywgdW5pdmVyc2l0eSA9PSAiQ2FyZGlmZiBVbml2ZXJzaXR5IiB8IHVuaXZlcnNpdHkgPT0gIkFjYWRpYSBVbml2ZXJzaXR5Iiwgc2VsZWN0ID0gYygidW5pdmVyc2l0eSIsICJjb3VudHJ5IiwgImVzdGFibGlzaGVkIikpDQoNCiN0aGlzIGlzIHRoZSBzYW1lIHRoaW5nLCBpdCdzIGNvbnNpZGVyZWQgc3VwZXJpb3IgZm9yIHJlYXNvbnMgdGhhdCBkb24ndCBtYXR0ZXIgaGVyZQ0KdW5pc1tjKCJ1bml2ZXJzaXR5IiwiY291bnRyeSIsImVzdGFibGlzaGVkIildW3VuaXMkdW5pdmVyc2l0eSA9PSAiQ2FyZGlmZiBVbml2ZXJzaXR5IiB8IHVuaXMkdW5pdmVyc2l0eSA9PSAiQWNhZGlhIFVuaXZlcnNpdHkiLCBdDQoNCiNidXQgb2Z0ZW4sIHdlJ2xsIHdhbnQgb3VyIGZpbHRlciB0byBiZSBtb3JlIHN1YnRsZSB0aGFuIHRoaXMsIGNvbnNpZGVyIGZvciBleGFtcGxlDQpzdWJzZXQodW5pcywgdW5pdmVyc2l0eSA9PSAiQ2FtYnJpZGdlIiwgc2VsZWN0ID0gYygidW5pdmVyc2l0eSIsICJjb3VudHJ5IiwgImVzdGFibGlzaGVkIikpDQojYnV0Li4ud2Uga25vdyB0aGF0IGluc3RpdHV0aW9uIGV4aXN0cw0KDQojc28sIGZvciBleGFtcGxlLi4uDQp1bmlzW2dyZXAoIkNhbWJyaWRnZSIsIHVuaXMkdW5pdmVyc2l0eSksIF0NCnN1YnNldCh1bmlzLCBncmVwbCgiQ2FtYnJpZGdlIiwgdW5pcyR1bml2ZXJzaXR5KSkNCiNub3RlIHRoaXMgaXNuJ3QgcXVpdGUgcmlnaHQgKCJDYW1icmlkZ2UgQ29sbGVnZSIgaXMgYSByYXRoZXIgZGlmZmVyZW50IHBsYWNlKQ0KYGBgDQoNCklmIHlvdSBsb29rIGNsb3NlbHkgYXQgc29tZSBmYWNldHMsIHlvdeKAmWxsIG5vdGljZSB0aGF0IG9uIHRoZSBib3R0b20geW91IGhhdmUgYSBzZWxlY3RvciBzYXlpbmcg4oCcKGJsYW5rKeKAnS4gIFlvdeKAmWxsIGFsc28gc2VlIHNvbWUgZHVwbGljYXRlIG9wdGlvbnMgKGUuZy4gTi9BLCBOQSwgbm9uZSwgZXRjLikNCg0KV2Fsa3Rocm91Z2g6IEZpbGxpbmcgaW4gdGhlIChibGFuaylzDQoxLglDaG9vc2UgdGhlIOKAnChibGFuaynigJ0gZmFjZXQgaW4geW91ciDigJxudW1Qb3N0Z3JhZOKAnSBjb2x1bW4NCjIuCUhvdmVyIG92ZXIg4oCYZWRpdOKAmSBhbmQgcmVwbGFjZSAoYmxhbmspIHdpdGggTkEgKG9yIHdoYXRldmVyIG90aGVyIHZhbHVlKQ0KDQozLglZb3UgY2FuIGFsc28gZWRpdCBpbmRpdmlkdWFsIHZhbHVlcyB3aXRoaW4gdGhlIHRhYmxlIG9uIHRoZSByaWdodCAgDQoNCjQuCVVzZSB0aGUgbWVudSB0byBuYXZpZ2F0ZSB0byDigJhjbHVzdGVyIGFuZCBlZGl04oCZIChub3RlIHRoZSBvdGhlciBvcHRpb25zIGhlcmUgYXJlIGFsc28gdmVyeSB1c2VmdWwpDQoNCjUuCVRoaXMgYWxsb3dzIHlvdSB0byBtZXJnZSBtdWx0aXBsZSB0aGUgbXVsdGlwbGUgTkEgdmFsdWVzIHVzaW5nIHN0cmluZyBtYXRjaGluZyDigJMgbm90ZSB0aGF0IOKAmG5vbmXigJkgKGkuZS4sIDApIGlzIG5vdCB0aGUgc2FtZSBhcyBOQSAobm90IGF2YWlsYWJsZSkNCjYuCVBsYXkgd2l0aCB0aGUgZGlmZmVyZW50IG9wdGlvbnMgb24gdGhlIOKAmGNvdW50cnnigJkgY29sdW1uIGFuZCBtZXJnZSB0aGUgdmFsdWVzDQoNCmBgYHtyfQ0KI2hlcmUgaXQncyB1c2VmdWwgdG8gZG8gdHdvIHRoaW5ncywgZmlyc3QgbGVhcm4gYWJvdXQgcmVwbGFjZW1lbnQsIGUuZy46DQp1bmlzJGVzdGFibGlzaGVkW3VuaXMkZXN0YWJsaXNoZWQ9PSIiXSA8LSBOQQ0KI3N0cl9yZXBsYWNlIGZyb20gdGhlIHN0cmluZ3IgcGFja2FnZSBpcyBhbHNvIHZlcnkgdXNlZnVsDQoNCiNhbmQgdG8gZXhwbG9yZSB5b3VyIHZhbHVlcw0KdGFibGUodW5pcyRlc3RhYmxpc2hlZCkNCg0KI2htLi4udGhlcmUgYXJlIGEgbG90IG9mIHRob3NlLCBob3cgYWJvdXQNCnRhYmxlKHVuaXNbYygiZXN0YWJsaXNoZWQiKV1baXMuY2hhcmFjdGVyKHVuaXMkZXN0YWJsaXNoZWQpLF0pDQoNCg0KYGBgDQoNCmBgYHtyfQ0KI29rIHNvIGl0IGxvb2tzIGxpa2UgdGhlcmUgbWlnaHQgYmUgdHdvIG9wdGlvbnMgaGVyZS4NCiNlaXRoZXIsIHdlIHVzZSBhIGZ1enp5IG1hdGNoaW5nIChlLmcuIHVzaW5nIGFncmVwKSB0byBpZGVudGlmeSBzdHJpbmdzIHRoYXQgbG9vayBzaW1pbGFyLCBwb3NzaWJseSB1c2luZyBhIGdyb3VuZCB0cnV0aCAoZS5nLiBhIHJlZmVyZW5jZSBsaXN0IG9mIHVuaXZlcnNpdHkgbmFtZXMpIHRvIG1hdGNoIHRoZXNlIGFnYWluc3QNCiNpbiBPcGVuUmVmaW5lIHRoaXMgaXMgY2FsbGVkIHJlY29uY2lsaWF0aW9uIGh0dHA6Ly9mcmVleW91cm1ldGFkYXRhLm9yZy9yZWNvbmNpbGlhdGlvbi8gDQoNCg0KI29yIC0gYSBzbG93ZXIgbWVjaGFuaXNtIC0gd2UgdXNlIGVudGl0eSByZWNvZ25pdGlvbg0KI2l0J3Mgd29ydGggbm90aW5nIHRoYXQgaW4gdGhpcyBkaXNjdXNzaW9uIG9mIGEgc2ltaWxhciBwcm9ibGVtLCBzb21lb25lIHJlY29tbWVuZHMgdXNpbmcgb3BlbnJlZmluZSEgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjY4MzM4MC90ZWNobmlxdWVzLWZvci1maW5kaW5nLW5lYXItZHVwbGljYXRlLXJlY29yZHMgDQojaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vbWVyZ2luZy1kYXRhLXNldHMtYmFzZWQtb24tcGFydGlhbGx5LW1hdGNoZWQtZGF0YS1lbGVtZW50cy8gYW5kIHRoaXMgZ3JlYXQgYmxvZyBwb3N0IGRpc2N1c3NlcyBpbXBsZW1lbnRpbmcgc29tZSBvZiB0aGUgb3BlbnJlZmluZSBhcHByb2FjaGVzIGluIFIgKGFsdGhvdWdoLCBvbmx5IHBhcnRpYWxseSwgYW5kIG5vdCBhbGwgb2YgdGhlbSAtIGkuZS4sIG9wZW5yZWZpbmUgaXMgcmVhbGx5IHJlYWxseSBwb3dlcmZ1bCEpDQoNCiN1c2luZyBlbnRpdHkgIHJlY29nbml0aW9uIQ0KI2h0dHBzOi8vcnB1YnMuY29tL2xtdWxsZW4vbmxwLWNoYXB0ZXINCmBgYA0KDQpgYGB7cn0NCmxpYnJhcnkob3Blbk5MUCkNCmxpYnJhcnkoTkxQKQ0KZW50aXRpZXMgPC0gZnVuY3Rpb24oZG9jLCBraW5kKSB7DQogIHMgPC0gZG9jJGNvbnRlbnQNCiAgYSA8LSBhbm5vdGF0aW9ucyhkb2MpW1sxXV0NCiAgaWYoaGFzQXJnKGtpbmQpKSB7DQogICAgayA8LSBzYXBwbHkoYSRmZWF0dXJlcywgYFtbYCwgImtpbmQiKQ0KICAgIHNbYVtrID09IGtpbmRdXQ0KICB9IGVsc2Ugew0KICAgIHNbYVthJHR5cGUgPT0gImVudGl0eSJdXQ0KICB9DQp9DQoNCiN0ZXh0IDwtIGMoInRoZSB5ZWFyIDIwMTUgaXMgbm93IikNCndvcmRfYW5uIDwtIE1heGVudF9Xb3JkX1Rva2VuX0Fubm90YXRvcigpDQpzZW50X2FubiA8LSBNYXhlbnRfU2VudF9Ub2tlbl9Bbm5vdGF0b3IoKQ0KbG9jYXRpb25fYW5uIDwtIE1heGVudF9FbnRpdHlfQW5ub3RhdG9yKGtpbmQgPSAibG9jYXRpb24iKQ0Kb3JnYW5pemF0aW9uX2FubiA8LSBNYXhlbnRfRW50aXR5X0Fubm90YXRvcihraW5kID0gIm9yZ2FuaXphdGlvbiIpDQptb25leV9hbm4gPC0gTWF4ZW50X0VudGl0eV9Bbm5vdGF0b3Ioa2luZCA9ICJtb25leSIpDQpkYXRlX2FubiA8LSBNYXhlbnRfRW50aXR5X0Fubm90YXRvcihraW5kID0gImRhdGUiKQ0KDQpwaXBlbGluZSA8LSBsaXN0KHNlbnRfYW5uLA0KICAgICAgICAgICAgICAgICB3b3JkX2FubiwNCiAgICAgICAgICAgICAgICAgbG9jYXRpb25fYW5uLA0KICAgICAgICAgICAgICAgICBvcmdhbml6YXRpb25fYW5uLA0KICAgICAgICAgICAgICAgICBtb25leV9hbm4sDQogICAgICAgICAgICAgICAgIGRhdGVfYW5uKQ0KYGBgDQoNCk5vdGUgd2Ugb25seSBuZWVkIHRvIGRvIHRoaXMgZm9yIHVuaXF1ZSB2YWx1ZXMsIGdpdmVuIGl0J3MgdmVyeSB0aW1lY29uc3VtaW5nIGl0J3Mgd2VsbCB3b3J0aCBzdWJzZXR0aW5nIGFuZCByZWpvaW5pbmcNCmBgYHtyfQ0KZXN0YWIgPC0gYXMuZGF0YS5mcmFtZSh1bmlxdWUodW5pcyRlc3RhYmxpc2hlZCkpDQoNCmVzdGFiJGVzdGFibGlzaGVkX2FubiA8LSBsYXBwbHkoZXN0YWIkYHVuaXF1ZSh1bmlzJGVzdGFibGlzaGVkKWAsIGZ1bmN0aW9uKHgpIGFubm90YXRlKHgsIGxpc3Qoc2VudF9hbm4sd29yZF9hbm4sZGF0ZV9hbm4pKSkNCg0KZXN0YWIkZXN0YWJsaXNoZWRfYW5uX3BsYWluIDwtIGFwcGx5KGVzdGFiW2MoInVuaXF1ZSh1bmlzJGVzdGFibGlzaGVkKSIsImVzdGFibGlzaGVkX2FubiIpXSwgMSwgZnVuY3Rpb24oeCkgQW5ub3RhdGVkUGxhaW5UZXh0RG9jdW1lbnQoeFtbMV1dLCB4W1syXV0pKQ0KDQplc3RhYiRlc3RhYmxpc2hlZF9kYXRlX2V4dHJhY3QgPC0gbGFwcGx5KGVzdGFiJGVzdGFibGlzaGVkX2Fubl9wbGFpbiwgZnVuY3Rpb24oeCkgKHgpW1sxXV1bWzFdXSkNCg0KZXN0YWIkZXN0YWJsaXNoZWRfZGF0ZV9leHRyYWN0IDwtIHVubGlzdChlc3RhYiRlc3RhYmxpc2hlZF9kYXRlX2V4dHJhY3QpDQojYSB0b25uZSBvZiBjbGVhbmluZyBpcyBsaWtlbHkgaGVyZSwgYnV0IGF0IGxlYXN0IGl0cyBvZiBkYXRlIHZhbHVlcyENCg0KdW5pcyA8LSBtZXJnZSh1bmlzLCBlc3RhYltjKCJ1bmlxdWUodW5pcyRlc3RhYmxpc2hlZCkiLCJlc3RhYmxpc2hlZF9kYXRlX2V4dHJhY3QiKV0sIGJ5Lng9ImVzdGFibGlzaGVkIiwgYnkueT0idW5pcXVlKHVuaXMkZXN0YWJsaXNoZWQpIiApDQoNCiNsZXQncyBqdXN0IHNhdmUgdGhpcywgd2UgZG9uJ3Qgd2FudCB0byBuZWVkICB0byByZWRvIHRoZSBsYWJlbGxpbmchIA0KI3dlIG5lZWQgdG8gc2VsZWN0IGNvbHVtbnMgaWYgeW91IHdhbnQgdG8gc2F2ZSBhcyBjc3YgYmVjYXVzZSB5b3UgY2FuJ3Qgc2F2ZSBsaXN0IA0KDQpzYXZlKHVuaXMsZmlsZT0idW5pcy5SZGEiKQ0KYGBgDQoNCg0KYGBge3J9DQpsaWJyYXJ5KHN0cmluZ3IpICNmb3Igc29tZSBjbGVhbmluZw0KdW5pcyR1bml2ZXJzaXR5IDwtIHN0cl90cmltKHVuaXMkdW5pdmVyc2l0eSxzaWRlID0gImJvdGgiKQ0KI2NvbnZlcnRpbmcgdGhpbmdzIGxpa2UgdGhpcyBpcyBzdXJwcmlzaW5nbHkgaGFyZCBpZiB5b3UgZG9uJ3Qga25vdyB3aGF0IHRvIHNlYXJjaCBmb3IuLi46ICgiVW5pdmVyc2lkYWQgSnUlQzMlQTFyZXogQXV0JUMzJUIzbm9tYSBkZSBUYWJhc2NvIikNCg0Kb3JnIDwtIGFzLmRhdGEuZnJhbWUodW5pcXVlKHVuaXMkdW5pdmVyc2l0eSkpDQoNCm9yZyR1bmlfb3JnIDwtIGxhcHBseShvcmckYHVuaXF1ZSh1bmlzJHVuaXZlcnNpdHkpYCwgZnVuY3Rpb24oeCkgYW5ub3RhdGUoeCwgbGlzdChzZW50X2Fubix3b3JkX2Fubixvcmdhbml6YXRpb25fYW5uKSkpDQoNCm9yZyR1bmlfb3JnX3BsYWluIDwtIGFwcGx5KG9yZ1tjKCJ1bmlxdWUodW5pcyR1bml2ZXJzaXR5KSIsInVuaV9vcmciKV0sIDEsIGZ1bmN0aW9uKHgpIEFubm90YXRlZFBsYWluVGV4dERvY3VtZW50KHhbWzFdXSwgeFtbMl1dKSkNCg0KDQpvcmckdW5pX29yZ19leHRyYWN0IDwtIGxhcHBseShvcmckdW5pX29yZ19wbGFpbiwgZnVuY3Rpb24oeCkgKHgpW1sxXV1bWzFdXSkNCg0Kb3JnJHVuaV9vcmdfZXh0cmFjdCA8LSB1bmxpc3Qob3JnJHVuaV9vcmdfZXh0cmFjdCkNCg0KVmlldyhvcmdbZ3JlcCgiQ2FtYnJpZGdlIiwgb3JnJGB1bmlxdWUodW5pcyR1bml2ZXJzaXR5KWApLCBdKQ0KDQp1bmlzIDwtIG1lcmdlKHVuaXMsIG9yZ1tjKCJ1bmlxdWUodW5pcyR1bml2ZXJzaXR5KSIsICJ1bmlfb3JnX2V4dHJhY3QiKV0sIGJ5LnggPSAidW5pdmVyc2l0eSIsIGJ5Lnk9InVuaXF1ZSh1bmlzJHVuaXZlcnNpdHkpIikNCg0Kc2F2ZSh1bmlzLGZpbGU9InVuaXMuUmRhIikNCg0KYGBgDQoNCmBgYHtyfQ0KI2hvdyBkbyB3ZSBjb252ZXJ0IHdvcmRzIHRvIG51bWJlcnMsIGdpdmVuIG1lc3N5IGRhdGE/DQplbmRvdyA8LSBhcy5kYXRhLmZyYW1lKHVuaXF1ZSh1bmlzJGVuZG93bWVudCkpDQoNCmVuZG93JGVuZG93bWVudF9tb24gPC0gbGFwcGx5KGVuZG93JGB1bmlxdWUodW5pcyRlbmRvd21lbnQpYCwgZnVuY3Rpb24oeCkgYW5ub3RhdGUoeCwgbGlzdChzZW50X2Fubix3b3JkX2Fubixtb25leV9hbm4pKSkNCg0KZW5kb3ckZW5kb3dtZW50X21vbl9wbGFpbiA8LSBhcHBseShlbmRvd1tjKCJ1bmlxdWUodW5pcyRlbmRvd21lbnQpIiwiZW5kb3dtZW50X21vbiIpXSwgMSwgZnVuY3Rpb24oeCkgQW5ub3RhdGVkUGxhaW5UZXh0RG9jdW1lbnQoeFtbMV1dLCB4W1syXV0pKQ0KDQplbmRvdyRlbmRvd21lbnRfbW9uX2V4dHJhY3QgPC0gbGFwcGx5KGVuZG93JGVuZG93bWVudF9tb25fcGxhaW4sIGZ1bmN0aW9uKHgpICh4KVtbMV1dW1sxXV0pDQoNCmVuZG93JGVuZG93bWVudF9tb25fZXh0cmFjdCA8LSB1bmxpc3QoZW5kb3ckZW5kb3dtZW50X21vbl9leHRyYWN0KSAgI0kgaGF2ZW4ndCBjaGVja2VkIHRoZXNlIGF0IGFsbCwgZG8gdGhleSBsb29rIGFib3V0IHJpZ2h0Pw0KDQp1bmlzIDwtIG1lcmdlKHVuaXMsIGVuZG93W2MoInVuaXF1ZSh1bmlzJGVuZG93bWVudCkiLCAiZW5kb3dtZW50X21vbl9leHRyYWN0IildLCBieS54PSJlbmRvd21lbnQiLCBieS55PSJ1bmlxdWUodW5pcyRlbmRvd21lbnQpIikNCg0Kc2F2ZSh1bmlzLGZpbGU9InVuaXMuUmRhIikNCg0KI2xvYWQoInVuaXMuUmRhIikNCg0KI3dyaXRlLmNzdih1bmlzLCJ1bmlzLmNzdiIpICNsZXQncyBqdXN0IHNhdmUgdGhpcywgd2UgZG9uJ3Qgd2FudCB0byBuZWVkICB0byByZWRvIHRoZSANCmBgYA0KDQoNCg0KDQpgYGB7cn0NCiNleHRyYWN0IGxvY2F0aW9ucywgYnV0IGFsc28gbWVyZ2UgbmVhciBkdXBsaWNhdGVzDQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJyb3BlbnNjaS9tb25rZXlsZWFybiIpDQoNCmBgYA0KDQpTdGVwIDg6IERlZHVwbGljYXRlIGVudHJpZXMgKFtuZWFyXSBkdXBsaWNhdGUgcm93cykNClRoZXJlIGFyZSBhIGxvdCBvZiAobmVhcmx5KSBkdXBsaWNhdGUgcm93cyBpbiB0aGUgZGF0YSwgdGhpcyBjYW4gaGFwcGVuIGZvciB2YXJpb3VzIHJlYXNvbnMgaW5jbHVkaW5nIGh1bWFuIGVycm9yIGFuZCBiZWNhdXNlIG11bHRpcGxlIGhpc3RvcmljIHZhbHVlcyBhcmUgc3RvcmVkLiBXZSB3YW50IHRvIGtlZXAganVzdCBvbmUgY29weS4NClRvIGRvIHRoaXMgKGJhc2VkIG9uIGRvY3VtZW50YXRpb24gaGVyZSksIGNsaWNrIG9uIHRoZSBjb2x1bW4gd2l0aCB0aGUgdW5pdmVyc2l0eSBuYW1lcywgYW5kIHRoZW4gY2xpY2sgb24gIlNvcnQiLiBPbmNlIHlvdSBkbyB0aGlzLCB5b3Ugd2lsbCBub3RpY2UgdGhhdCB0aGVyZSBpcyBhIG5ldyAiU29ydCIgbWVudSBhdCB0aGUgdG9wLiBDbGljayBvbiB0aGlzIGFuZCBzZWxlY3QgIlJlb3JkZXIgcm93cyBwZXJtYW5lbnRseSIuIFRoaXMgbWF5IHRha2UgYSB3aGlsZSBhcyBpdCByZW51bWJlcnMgdGhlIHJvd3MgaW4gd2hpY2ggdGhlIGVudHJpZXMgYXBwZWFyLiANCg0KVGhlbiBvbiB0aGUgY29sdW1uIHdpdGggdW5pdmVyc2l0eSBuYW1lcywgRWRpdCBjZWxscyAtPiBCbGFuayBkb3duIA0KDQpUaGVuIG9uIHRoZSBzYW1lIGNvbHVtbiwgRmFjZXQgLT4gQ3VzdG9taXplZCBmYWNldHMgLT4gRmFjZXQgYnkgYmxhbmsgDQpOb3cgd2Ugd2FudCB0byByZW1vdmUgYWxsIHRoZSBibGFuayByb3dzLCBzbyBzZWxlY3QgdHJ1ZSwgdGhlbiBvbiB0aGUgIkFsbCIgY29sdW1uIG9uIHRoZSBsZWZ0LCBFZGl0IHJvd3MgLT4gUmVtb3ZlIGFsbCBtYXRjaGluZyByb3dzLCBsaWtlIHlvdSBoYXZlIGRvbmUgd2hlbiB3b3JraW5nIHdpdGggdGhlIG51bVN0dWRlbnRzIGFuZCBlbmRvd21lbnQgY29sdW1ucy4gDQpPbmNlIHlvdSByZW1vdmUgYWxsIHRoZSBmYWNldHMsIGFuZCB5b3Ugbm93IGhhdmUgYSAobW9zdGx5KSBjbGVhbmVkIGRhdGEgc2V0LiANCg0KDQpgYGB7cn0NCiNyYXRoZXIgdGhhbiBkbyBpdCB0aGlzIHdheSwgd2UnZCBwcm9iYWJseSB1c2UgdGhlIGVudGl0eSByZWNvZ25pdGlvbiBhcHByb2FjaA0KDQpgYGANCg0KDQpTdGVwIDk6IEV4cGxvcmluZyB0aGUgZGF0YSB3aXRoIHNjYXR0ZXIgcGxvdHMNCkNsaWNrIG9uIHRoZSAiZW5kb3dtZW50IiBjb2x1bW4sIEZhY2V0IC0+IFNjYXR0ZXJwbG90IGZhY2V0LiANClRoaXMgc2hvd3MgdGhlIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiBhbGwgb2YgdGhlIG51bWVyaWMgdmFsdWVzIGluIGVhY2ggb2YgdGhlIGNvbHVtbnMuIENsaWNrIG9uICJsb2ciIHRvIGdldCBhIGJldHRlciB2aWV3LiANCg0KQ2xpY2sgb24gdGhlIHBsb3QgZm9yIGVuZG93bWVudCB2cy4gbnVtU3R1ZGVudHMuIFlvdSBjYW4gbm93IGRyYWcgc2VsZWN0IGEgcG9ydGlvbiBvZiB0aGUgcGxvdCwgYW5kIHRoZW4gc2VlIHRoZSByb3dzIGNvcnJlc3BvbmRpbmcgdG8gdGhhdCBzZWxlY3Rpb24uIA0KDQpgYGB7cn0NCg0KYGBgDQoNClN0ZXAgMTA6IEdlb2NvZGluZyBuYW1lcyBhbmQgYWRkcmVzc2VzDQpUaGlzIG5leHQgcGFydCBzaG93cyAoYmFzZWQgb24gZG9jdW1lbnRhdGlvbiBoZXJlKSBob3cgdG8gZ28gZnJvbSBhIGRlc2NyaXB0aW9uIG9mIGEgcGxhY2UgKGkuZS4gdGhlIG5hbWUgb2YgYSB1bml2ZXJzaXR5KSB0byB2YWx1ZXMgZm9yIGl0cyAobGlrZWx5KSBnZW9ncmFwaGljIGNvb3JkaW5hdGVzLiBCZWhpbmQgdGhlIHNjZW5lcywgdGhpcyB1c2VzIEdvb2dsZSBNYXBzIHRvIGZpZ3VyZSBvdXQgd2hhdCBpcyB0aGUgbW9zdCBsaWtlbHkgbG9jYXRpb24geW91IGFyZSBhc2tpbmcgZm9yLiANClRvIGxlYXJuIGhvdyB0byBkbyB0aGlzLCB5b3UgZG9uJ3QgbmVlZCB0byBkbyBwcm9jZXNzIHRoZSB3aG9sZSBkYXRhIHNldC4gVGhpcyBjYW4gdGFrZSBhIHdoaWxlLCBhbmQgR29vZ2xlIGxpbWl0cyB5b3UgdG8gMjAwMCByZXF1ZXN0cyBwZXIgZGF5LiBJdCdzIGJldHRlciB0byBqdXN0IHNlbGVjdCBhcm91bmQgMTAgcm93cyBhbmQgdmVyaWZ5IHRoYXQgaXQgd29ya3MuIA0KQW4gZWFzeSB3YXkgdG8gZ2V0IGEgbGltaXRlZCBzZXQgb2Ygcm93cyBpcyBieSB1c2luZyBhIG51bWVyaWMgbG9nIGZhY2V0IG9mIHRoZSBudW1iZXIgb2Ygc3R1ZGVudHMsIHNvIHVzZSBGYWNldCAtPiBDdXN0b21pemVkIGZhY2V0cyAtPiBOdW1lcmljIGxvZyBmYWNldCANCiANClVzZSB0aGlzIGZhY2V0IHRvIG1ha2UgYSBzZWxlY3Rpb24gb2YgYXJvdW5kIHRlbiByb3dzLCBhbmQgdGhlbiBjaGVjayB0aGUgbWF0Y2hpbmcgcm93cyBudW1iZXIgdG8gdmVyaWZ5IHRoYXQgeW91IGhhdmUgYSByZWFzb25hYmxlIHNlbGVjdGlvbiBzaXplOiANCg0KDQpOb3cgdGhlIGZ1biBiZWdpbnMgYW5kIHdlIHdhbnQgdG8gZG8gRWRpdCBjb2x1bW4gLT4gQWRkIGNvbHVtbiBieSBmZXRjaGluZyBVUkxzLiBJbiBvdGhlciB3b3JkcywgdGhlIHZhbHVlcyBvZiB0aGUgY2VsbHMgaW4gdGhlIG5ldyBjb2x1bW4gYXJlIGJhc2VkIG9uIGRhdGEgdGhhdCBpcyByZXRyaWV2ZWQgZnJvbSB0aGUgSW50ZXJuZXQuIA0KRW50ZXIgaW4gdGhlIGV4cHJlc3Npb24gYmVsb3csIGFuZCB5b3Ugc2hvdWxkIHNlZSBhIGxpc3Qgb2YgVVJMcyB3aXRoIHRoZSBuYW1lcyBvZiB0aGUgdW5pdmVyc2l0aWVzIGF0IHRoZSBlbmQgb2YgdGhlIFVSTHMuIFNwZWNpZnkgYSBuZXcgY29sdW1uIG5hbWUgc3VjaCBhcyAiZ2VvY29kaW5nUmVzcG9uc2UiLCBhbmQgc2V0IHRoZSB0aHJvdHRsZSBkZWxheSB0byBhcm91bmQgNTAwIG1pbGxpc2Vjb25kcy4gDQoiaHR0cDovL21hcHMuZ29vZ2xlLmNvbS9tYXBzL2FwaS9nZW9jb2RlL2pzb24/c2Vuc29yPWZhbHNlJmFkZHJlc3M9IiArIGVzY2FwZSh2YWx1ZSwgInVybCIpDQoNCg0KWW91IHNob3VsZCBnZXQgYSBidW5jaCBvZiBkYXRhIGJhY2suIFRvIGNvbnZlcnQgdGhpcyBpbnRvIGEgbW9yZSByZWFkYWJsZSBmb3JtYXQsIHlvdSBuZWVkIHRvIGNsaWNrIG9uIHRoZSBnZW9jb2RpbmdSZXNwb25zZSBjb2x1bW4sIGFuZCB0aGVuIG9uIEVkaXQgY29sdW1uIC0+IEFkZCBjb2x1bW4gYmFzZWQgb24gdGhpcyBjb2x1bW4uIEVudGVyIGluIHRoZSBleHByZXNzaW9uIGJlbG93IA0Kd2l0aCh2YWx1ZS5wYXJzZUpzb24oKS5yZXN1bHRzWzBdLmdlb21ldHJ5LmxvY2F0aW9uLCBwYWlyLCBwYWlyLmxhdCArIiwgIiArIHBhaXIubG5nKQ0KTm93IHlvdSBoYXZlIGEgc2luZ2xlIGNvbHVtbiB3aXRoIGNvb3JkaW5hdGVzLiBZb3UgY2FuIHNwbGl0IHRoaXMgaW50byBjb2x1bW5zIGZvciBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGJ5IHNlbGVjdGluZyBFZGl0IENvbHVtbiAtPiBTcGxpdCBpbnRvIHNldmVyYWwgY29sdW1ucyBhbmQgc3BlY2lmeWluZyBhIHNlcGFyYXRvciBvZiAiLCIuIFRoZXNlIGNvbHVtbnMgY2FuIHRoZW4gYmUgcmVuYW1lZCB1c2luZyBFZGl0IENvbHVtbiAtPiBSZW5hbWUgdGhpcyBjb2x1bW4uIA0KYGBge3J9DQojdGhlcmUgYXJlIGEgZmV3IHdheXMgd2UgY2FuIGRvIHRoaXMgaW4gUg0KI3RoZSBtb3N0IG9idmlvdXMgb25lIGlzIHVzaW5nIGdvb2dsZW1hcHMgYXBpIGdlb2NvZGluZyBodHRwczovL2NlbmdlbC5naXRodWIuaW8vcnNwYXRpYWwvNV9HZW9jb2RpbmcubmIuaHRtbCNpc3N1ZXMgLSBidXQgaXQgbGltaXRzIHVzIHRvIDI1MDAgcXVlcmllcyBhIGRheS4gIHRvIHVzZSB0aGF0IA0KI2xpYnJhcnkoZ2dtYXApDQojZ2VvY29kZSh1bmlzJHVuaV9vcmdfZXh0cmFjdFtbMV1dKSAgKCxldGMuKSB0byBnZXQgbGF0bG9uZw0KDQojSSB0aGluayBpdCBzaG91bGQgYWxzbyBiZSBwb3NzaWJsZSB0byB1c2UgV2lraURhdGEgKHBvc3NpYmx5IERCUGVkaWEpIHRvIGFjY2VzcyB0aGUgb3JnYW5pc2F0aW9uIGNvb3JkaW5hdGVzLiBMaWtlbHkgbW9yZSBtaXNzaW5nIGRhdGEoPykgYnV0IHRoYXQgY2FuIGFsd2F5cyBiZSBmaWxsZWQgd2l0aCBnb29nbGUgaWYgbmVjZXNzYXJ5IHdpdGhvdXQgZmlyc3QgdXNpbmcgdGhlIDI1MDAgYSBkYXkgbGltaXQuLi4ub2YgY291cnNlIGlmIHlvdSBzdWJzZXQgdG8gdW5pcXVlIHVuaXMgdGhlcmUgYXJlIGEgbG90IGZld2VyISANCmxpYnJhcnkoV2lraWRhdGFSKQ0KDQojb2sgc28gZmlyc3Qgd2UgaGF2ZSB0byBmaW5kIHRoZSBXaWtpZGF0YSBlbnRpdHkgY29kZSBmb3IgYWxsIG9mIHRoZSB1bml2ZXJzaXRpZXMNCnVuaXMkd2lraWRhdGEgPC0gTkENCg0Kd2tkIDwtIGFzLmRhdGEuZnJhbWUodW5pcXVlKHVuaXMkdW5pX29yZ19leHRyYWN0KSkNCndrZCR3aWtpZGF0YSA8LSBOQQ0Kd2tkJHdpa2lkYXRhIDwtIGxhcHBseSh3a2RbLDFdLCBmdW5jdGlvbih4KSBmaW5kX2l0ZW0oKHgpLCBsYW5ndWFnZSA9ICJlbiIsIGxpbWl0ID0gMSkpW1sxXV0kaWQgI0ZBTFNFIFBPU0lUSVZFUyEhICBUaGlzIGlzIHByZXR0eSBzbG93DQojd2tkJHdpa2lkYXRhIDwtIGxhcHBseSh3a2Qkd2lraWRhdGEsIGZ1bmN0aW9uKHgpIGlmZWxzZShsZW5ndGgoeCkgPiAwLCB1bmxpc3QoeFtbMV1dW1syXV0pLCAiIikpDQoNCndrZCRsYXQgPC0gTkENCndrZCRsb25nIDwtIE5BDQoNCiNnZXRfZ2VvX2VudGl0eSgiUTM1Nzk0IiwgbGFuZ3VhZ2UgPSAiZW4iLCByYWRpdXMgPSBOVUxMKVtbYygzLDQpXV0NCnggPC0gIChsYXBwbHkod2tkWywyXSwgZnVuY3Rpb24oeCkgew0KICBpZih4ICE9ICIiKSB7DQogICAgdW5saXN0KGdldF9nZW9fZW50aXR5KHgsIGxhbmd1YWdlID0gImVuIiwgcmFkaXVzID0gTlVMTClbYygzOjQpXSkNCiAgICB9IGVsc2Ugew0KICAgICAgIiJ9DQp9KSkNCg0Kd2tkJGxhdCA8LSB1bmxpc3QobGFwcGx5KHgsIGZ1bmN0aW9uKHkpIGlmZWxzZShsZW5ndGgoeSkgPiAwLCB1bmxpc3QoeSlbMV0sICIiKSkpDQp3a2QkbG9uZyA8LSB1bmxpc3QobGFwcGx5KHgsIGZ1bmN0aW9uKHkpIGlmZWxzZShsZW5ndGgoeSkgPiAwLCB1bmxpc3QoeSlbMl0sICIiKSkpDQoNCnJtKHgpDQpzYXZlKHdrZCxmaWxlPSJ1bmlzX2xvY2F0aW9uLlJkYSIpDQoNCmxvYWQoInVuaXNfbG9jYXRpb24uUmRhIikNCg0KI29rLCBub3cgd2UgY2FuIG1hcCB0aGlzLiBBZ2Fpbiwgd2UgY2FuIGRvIHRoYXQgaW4gT3BlblJlZmluZSBvbiBnb29nbGUgZnVzaW9uIHRhYmxlcyByZWFsbHkgZWFzaWx5LiBUbyBkbyBpdCBpbiBSIHJlcXVpcmVzIGEgYml0IG9mIGxlZyB3b3JrLCBhbmQgdGhlcmUgYXJlIHZhcmlvdXMgb3B0aW9ucyBkZXBlbmRpbmcgb24gd2hhdCB3ZSB3YW50IHRvIHZpc3VhbGlzZSAoZS5nLiwgZG8gd2Ugd2FudCB0byBtYXAgdW5pdmVyc2l0aWVzIHRvIHBvaW50cywgb3IgdG8gZG8gYSBoZWF0bWFwIG9mIGVuZG93bWVudCBzaXplcywgb3IuLi4pDQoNCmBgYA0KDQpTdGVwIDExOiBFeHBvcnQgRGF0YQ0KVGhlIGRhdGEgY2FuIGJlIGV4cG9ydGVkIHRvIGZvcm1hdHMgc3VjaCBhcyBFeGNlbC4gSWYgeW91IHJlYWQgdGhpcyBpbnRvIHRvb2xzIHN1Y2ggYXMgU1BTUyBhbmQgbm90aWNlIHRoYXQgdGhlIGxhc3QgY29sdW1uIGlzIG1pc3NpbmcsIHRoZW4gb3BlbiB0aGUgZmlsZSB1cCBpbiBFeGNlbCwgcmUtc2F2ZSBpdCwgYW5kIHRyeSB0byBvcGVuIGl0IHVwIGFnYWluIGluIFNQU1MuIA0KYGBge3J9DQoNCmBgYA==