1 Overview

More countries continue to adopt DHIS2 as a national health information system, but recently, the software’s new features to capture many different types of data have often outpaced its native tools to analyze and visualize those data. This is especially the case for individual-level health records found in DHIS2 Tracker.

The folliwjng tutorial will demonstrate how to extract longitudinal, individual-level data from DHIS2, then display it through interactive HTML widgets, to provide an overview of trends which can be “drilled down” for further analysis.

1.0.1 Why is R a good fit for DHIS2?

In the past five years, R has become one of the top languages for health analysts researchers, in particular those those with less vocational training in programming. Various R packages have been developed to pull online data into an R environment through APIs, interface R with Javascript data visualization libraries, and roll the results into modern, interactive HTML reports.

So its easier than ever to link R with DHIS2 for robust analysis and visualization, yet the available documentation does not address HTML widgets or tracker data.

1.0.2 Who This is Tutorial For

  • You have some basic grasp of R, Python, STATA, or another scripting language
  • You want routine high-level reports from DHIS2, particularly from production instances where you lack admin access (e.g. the patient database is hosted by another country), so the reports should be fully reproducible by a colleague
  • You’re interested in combining event data in tracker programs for basic models requiring longitudinal individual data
  • You want some interactivity between maps, tables, and charts to improve data exploration

1.0.3 Who This Tutorial is NOT For

  • You code a lot in Javascript already. You can develop a custom app for your DHIS2 use case, and share it with the community!
  • You want to dump all data from DHIS2 into another system for advanced analysis. Probably easier to pull from the database tables directly.
  • You want to parameterize your reports, so data can be real-time and tailored to a distinct user/org unit. You can probably use DHIS2’s native Push Analytics and HTML reports, or you can step your R skills with a Shiny dashboard.

2 Getting started

First things first: Welcome! This is an RMarkdown document. If you have R and R studio on your local machine, you can download the code by clicking the button at top right and try it yourself.

We’ll work a bit with how to extract data from the DHIS2 API. To do that, we will make sure to set up our R session with the required R packages.(This, it turns out, is not so intuitive, although a handy method for reproducible session set-up is found here) Downloading packages fresh may take some time, especially if you have slow internet…

We also need to add the baseurl and username to log-in to your DHIS2 instance with the httr package and keyring package

Note: It is best practice not to store the password within the script as it can create security vulnerabilities. The user who “knits” the script into HTML will be prompted for the dhis2 Play demo password. It will then be stored as a local environment variable on the user’s machine

knitr::opts_chunk$set(echo=TRUE, warning=FALSE, message=FALSE, paged.print=FALSE)
options(encoding = "UTF-8")

#IMPORTANT MANUAL STEPS BEFORE RUNNING
#credentials and global variables#
#(include https://  and last backslash of DHIS2 URL, in quotes)
baseurl<-"https://play.dhis2.org/2.31.5/"
username<-"admin"


##______Now load packages and set up_____##
##first load required packages

required_packages <- c("ggplot2","stringr","httr","jsonlite","assertthat","readr","tibble","plyr","tidyr","dygraphs","xts","lubridate","knitr","askpass","DT","keyring","devtools","crosstalk","plotly","knitr","leaflet","parcoords","diffr","listviewer","highcharter", "sf")
#hint: use sessionInfo() to find loaded packages after finishing devlopment, then copy them above

#define the function to check that a package is installed
is_installed <- function(mypkg) is.element(mypkg, installed.packages()[,1])

#check if each required package is installed, if it isnt install it from CRAN, if it is, then load it
load_or_install<-function(required_packages) {
  for(package_name in required_packages)  {
    if(!is_installed(package_name))     {
       install.packages(package_name,repos="http://lib.stat.cmu.edu/R/CRAN")  }
    library(package_name,character.only=TRUE,quietly=TRUE,verbose=FALSE)
  }
}

#run the function
load_or_install(required_packages)

#youll now be prompted for password (district)
require(keyring)
keyring::key_set("Password", username=username)
## Please enter password in TK window (Alt+Tab)

This function will log in to the DHIS2 instance. Once we log in, we do not need to log in again for all other queries! (Credit for this function to Jason Pickering’s very useful R-for-DHIS2 tutorials

loginDHIS2<-function(baseurl,username) {
    url<-paste0(baseurl,"api/me")
    r<-GET(url,authenticate(username, key_get("Password", username=username) ))
    assert_that(r$status_code == 200L) }

if(loginDHIS2(baseurl,username)){
  print("successfully logged in")
}
## [1] "successfully logged in"

3 Rendering Pivot Tables and Reports from DHIS2

This is how data from DHIS2 are often shared. Often, it’s the easiest solution for the context…

Source: LSTHM, CStock

Source: LSTHM, CStock

Source: LSTHM, CStock

But by leveraging the DHIS2 analytics API in RMarkdown, we can download and analyze pivot tables, event reports, metadata, and even raw data, then render the results into PDF, word, and HTML documents to share. There is no reason to send excel files or screenshots back and forth with your partners.

The easiest way to construct an analytics API query is using the pivot table or event reports apps. Then, “download” and “download as html” will give you the link( the video below explains how to download a pivot table as XLS, choose download as html instead for the analytics link). You can change the extension to .json, .html+css, or .csv. See DHIS2 developer guide for more details

The next query searches for ANC lab events in the last 3 months in the “WHO RMNCH Tracker” program that have hemoglobin values. In a reaslistic schenario, a low hemoglobin value should go up over time due to iron supplementation treatment.

url<-paste0(baseurl,
            "api/29/analytics/events/query/WSGAb5XwJ3Y.csv?", #event query for "RMCH tracker" program
            "dimension=pe:LAST_3_MONTHS", #Events last month
            "&stage=edqlbukwRfQ&dimension=ou:ImspTQPwCqd", #stage and org unit (root)
          
            #data dimensions and other display properties
            "&dimension=tYPIZKEVh23&dimension=vANAXwtLwcT&dimension=w75KJ2mc4zz&", 
            "displayProperty=NAME&tableLayout=true&columns=pe;ou;tYPIZKEVh23;vANAXwtLwcT;w75KJ2mc4zz&rows=pe;ou;tYPIZKEVh23;vANAXwtLwcT;w75KJ2mc4zz") 

#now we GET command to fetch the data, and then read the content into a dataframe
output<-httr::GET(url)

report1 <-read_csv(content(output))

#lets take a look at the data
head(report1)
## # A tibble: 6 x 11
##   Event `Program stage` `Event date`        Longitude Latitude
##   <chr> <chr>           <dttm>                  <dbl>    <dbl>
## 1 SXvh~ edqlbukwRfQ     2019-07-31 12:05:00         0        0
## 2 tR95~ edqlbukwRfQ     2019-08-02 12:05:00         0        0
## 3 oYRZ~ edqlbukwRfQ     2019-08-12 12:05:00         0        0
## 4 azHI~ edqlbukwRfQ     2019-08-31 12:05:00         0        0
## 5 N7xg~ edqlbukwRfQ     2019-07-29 12:05:00         0        0
## 6 M2Sa~ edqlbukwRfQ     2019-08-15 12:05:00         0        0
## # ... with 6 more variables: `Organisation unit name` <chr>, `Organisation
## #   unit code` <chr>, `Organisation unit` <chr>, `WHO RMNCH Days
## #   enrolled` <dbl>, `WHOMCH Hemoglobin value` <dbl>, `First name` <chr>

4 Data Tables

Modern web pages render data more usefully, and usually interactively. We can do that in R simply with the DT package.

4.1 Ugly Example

Heres the default datatable layout

DT::datatable(report1)

Maybe that was done a bit too simply…

4.2 Pretty Example

Now let’s make the presentation of data a bit nicer

#now try a "pretty" data table

#well just get all event date, ou name, and data columns. 
#Then sort by event date and ou name
lengthcols <- function(x) length(colnames(x))

pretty_report1 <- report1 %>%
  select(c(1, 3, 6, 9, (lengthcols(report1)-2):lengthcols(report1))) %>% #get only the necessary data columns
  arrange(desc(`Event date`)) %>%
  mutate("Event date"=as.Date(lubridate::ymd_hms(`Event date`, tz = "UTC"))) %>%
  #Lets also add the event ID as a hyperlink
  mutate("Event Link"=paste0("<a href=\"",baseurl,"api/events/",Event,"\">","Event Link","</a>"))


#the extension will allow us to pretty print, with a hyperlink
#create this as a function so we can come back to it later

makeDT <-function(x){
              DT::datatable(x,
              filter = 'bottom',
              caption = "Event report example",
              extensions = 'Buttons', 
              escape = FALSE,
              rownames = FALSE,
              width= "100%",
              class = "compact",
              options = list(
                pageLength = 5,
                dom = 'Blfrtip', 
                buttons = list(I("colvis"),
                c('copy', 'csv', 'excel', 'pdf'))))
}

makeDT(pretty_report1)

Advantages of DT Format

  • Sort by column, and start typing in the search bars at the bottom of the columns to filter
  • Filter by date range
  • Change the page size
  • Use a global search across columns
  • Change what columns are visible
  • Export the table to other common formats
  • Click the Event Link to go to an API query for the event

You can find more options for the DT package here.

4.2.1 Detect Patterns 1

What if we can plot the event data over “days enrolled”? Do we detect any trend?

ggplot(report1, aes(x = `WHO RMNCH Days enrolled`, y = `WHOMCH Hemoglobin value`)) +
    geom_point()

Looks like theres very little pattern between days enrolled and hemoglobin value! At the very least in demo…

5 Merge Events by Enrollments

But many of these events can be grouped by their enrollment. Lets merge these data with enrollment IDs from the past 6 months. (Demo server maxes out after 1000 enrollments, so we’ll just get those for now…)

#get start date for "last 3 months"
fourmonthsago <-round_date(ymd(Sys.Date()), unit = "month") %m-% months(4) 


#We will use the EVENTS resource. 
#This allows us to query directly for all events of a certain program and stage, 
#but not based on data values. 
#For simplicity we'll just get first 1000 records.

url<-paste0(baseurl,"api/29/events.json?fields=enrollment,event,trackedEntityInstance",
                    "&program=WSGAb5XwJ3Y&startDate=",fourmonthsago,
                    "&stage=edqlbukwRfQ",
                    "&pageSize=",1000)

enrollment_IDs <-fromJSON(content(GET(url),"text"))


#Now we will join the enrollment IDs to the event report
#We will also create a link to the record in the tracker capture app
events_with_enrollments<- report1 %>%
  dplyr::inner_join(enrollment_IDs$events, by = c("Event" = "event")) %>%
    mutate("Event date"=as.Date(lubridate::ymd_hms(`Event date`, tz = "UTC"))) %>%
  mutate("Record Link"=paste0("<a href=\"",baseurl,"/dhis-web-tracker-capture/index.html#/dashboard?",
                          "tei=",trackedEntityInstance,
                          "&program=WSGAb5XwJ3Y&ou=",`Organisation unit`,
                          "\">Record Link</a>")) %>%
  #just select important variables and arrange
  select(c(1, 3, `Organisation unit name`:`Record Link`))%>%
  select(-c("trackedEntityInstance", `Organisation unit code`, `Organisation unit`)) %>%
  arrange(desc(`Event date`))

makeDT(events_with_enrollments)

5.1 Detect Patterns 2

Maybe there are temporal trends within enrollments?

#Add a ggplot line chart for hemoglobin over days enrolled
ggplot(events_with_enrollments, 
       aes(x=`WHO RMNCH Days enrolled`, 
          y=`WHOMCH Hemoglobin value`)) +
      geom_line(aes(group = enrollment))

Negative results here.. But more realistic data would show individual trends!

5.2 Longitudinal data: Growth Patterns

In the example below, we download the CDC’s length-for-age Z-score data for infant girls, and render the data in an interactive growth chart.

Remember, a child register in DHIS2 Tracker could use a similar approach to displaying growth of a population cohort over time, where each series represents an individual infant’s weight measurements.

birthdate<-fourmonthsago

cdc_data<-read_csv("https://www.cdc.gov/growthcharts/data/zscore/zwtageinf.csv")
cdc_data<- cdc_data %>%
#for simplicity, just look at girls data
  subset(Sex==2) %>%
  mutate("weeks_diff" = 4 * as.numeric(Agemos)) %>%
  mutate("date"= birthdate + lubridate::weeks((weeks_diff)))


#gather up the data and display as line chart
hc <- cdc_data %>% gather(zscore, weight, `-2`:`2`) %>%
  hchart(type = "line", hcaes(x = "Agemos", y = "weight", group="zscore")) %>%
      hc_title(text = "Zscores")


#we can do the same as a time series chart
xtchart<-as.xts(cdc_data, order.by=as.POSIXct(cdc_data$date)) %>% 
  subset(select = `2`:`-2`) %>%
  dygraph(main = "Z-Scores", ylab = "Weight in Kg") %>%
  dyOptions(colors = RColorBrewer::brewer.pal(7, "Dark2")) %>%
    dyHighlight(highlightSeriesBackgroundAlpha = 0.6,
              hideOnMouseOut = FALSE,
              highlightSeriesOpts = list(strokeWidth = 3)) %>%
  dyRangeSelector()

#this method uses highcharter  
hc
#this method uses dygraphs
xtchart

6 Interactive Charts in Crosstalk

Now we can add some more interactivity! We will include brushing capability that dynamically links a chart and table. This work builds on the R’s Crosstalk package, which joins certain types of HTML widgets together based on a common “shared data” object. Importantly, the linking happens directly within the browser, eliminating the need for a Shiny server. However, note that only select packages are available to work with crosstalk, and performance will slow with large datasets.

For this example, we will use DT package, Plotly package, and Leaflet package.

#first we created a "shared data object"
sd <- SharedData$new(events_with_enrollments)

#create a plotly scatterplot
p<-plot_ly(sd, x= ~`WHO RMNCH Days enrolled`, 
          y= ~`WHOMCH Hemoglobin value`,
          color = ~`Organisation unit name`,
          size = 3,
          showlegend=FALSE) %>% 
  add_markers(alpha = 0.5) %>%
  highlight(on = "plotly_selected",  off = "plotly_deselect")


#We'll use the custom datatable function from earlier on the shared data
dt<-makeDT(sd)


#crosstalk comes with bootstrap functions to help us arrange the interactive objects
#put a org unit filter on one side, OU on another
bscols(widths = c(4,NA),
  list(
    filter_select("orgUnit", "Filter by OrgUnit", sd, ~`Organisation unit name`),
  p),
  dt)

How to use interactive filter down:

  • Click on one or multiple scatter plot dots
  • Click and drag across an area of dots on scatter plot
  • Select one or multiple event from table
  • Choose a specific org unit

7 Advanced - Linked Map

Now we will link a leaflet map. First, let’s fetch coordinates for the event org units, and display them on a simple map.

library(leaflet)
library(sf)
library(dplyr)

#get OU coordinates and merge with the data

url<-paste0(baseurl,"api/29/organisationUnits.csv?paging=false&fields=name,coordinates&filter=coordinates:!null")
output <-read_csv(content(GET(url)))

#merge the name of the org units with the event data
mapobj<-events_with_enrollments %>%
dplyr::rename("name"=`Organisation unit name`) %>%
dplyr::inner_join(output) %>%
separate(coordinates, c("longitude","latitude"), sep = ",", extra = "drop")
mapobj$latitude<-parse_number(mapobj$latitude)
mapobj$longitude<-parse_number(mapobj$longitude)



#create map object
leaflet(mapobj) %>% addTiles() %>%
  addCircles(lat = ~latitude, lng = ~longitude)

Very cool. Now we can link with the data table and scatterplot. If you highlight an area on the map or scatterplot, it will highlight the other visual, and filter the table! Select any number of table items to highlight on the chart and map.

###we need to alter out DT function to hide the lat/long columns
#eventually we will want to hide geographic coordinates in the columns
#this function returns the position of the coordinates in the table so we can exclude them
hidecols<-function(x, a, b) if (is.SharedData(x) && 
                                a %in% colnames(x$origData()) && 
                                b %in% colnames(x$origData())){
                            removals<-x$origData()
                            return(c(grep(a, colnames(removals))-1,
                                    grep(b, colnames(removals))-1))
                            rm(removals)
  }


#remember that DT function earlier? 
makeDT <-function(x){
              DT::datatable(x,
              filter = 'bottom',
              caption = "Event report example",
              extensions = 'Buttons', 
              escape = FALSE,
              rownames = FALSE,
              width= "100%",
              class = "compact",
              options = list(
                pageLength = 5,
                dom = 'Blfrtip', 
                buttons = list(I("colvis"),
                c('copy', 'csv', 'excel', 'pdf')),
                
        #we just hide these two columns now
                columnDefs = list(list(visible=FALSE, 
                                     targets=hidecols(x,
                                                    "latitude","longitude")))))

}



#now we apply the map as a shared data object!
sd1 <- SharedData$new(mapobj)

p<-plot_ly(sd1, x= ~`WHO RMNCH Days enrolled`, 
          y= ~`WHOMCH Hemoglobin value`,
          color = ~name,
          size = 3,
          showlegend=FALSE) %>% 
  add_markers(alpha = 0.3) %>%
  highlight(on = "plotly_selected", off = "plotly_deselect")


#We'll use the custom datatable function from earlier on the shared data
dt<-makeDT(sd1)


#still trying to figure out how to highlight on the map better...

bscols(widths = c(4,NA),
  list(filter_select("orgUnit", "Filter by OrgUnit", sd1, ~name),
  leaflet(sd1, width = "100%", height = 300) %>%
    addTiles() %>%
    addMarkers(lat = ~latitude, lng = ~longitude)),
p)
dt

Hmmm thats a lot of information, and very tightly packed.

You can display it in a separate page as a flexdashboard. See the flexdashboard demo of these data here.

8 Advanced - Parallel Coordinates

Another great way to display correlations across many data points is a parallel coordinates chart. Using the parcoords package, we can visualize thousands of patient records at once with just a few lines of code.

As an HTML widget, parallel coordinates can be “brushed” – click and drag on one more or axes, and the graphic highlights rows within the selected range. You can also arrange the columns into a better order.

In the example below, we log in to an older demo instance (2.29) which has more data for the “Inpatient Morbidity Mortality” program. These data are cross-sectional, so we only pull from one event report. But it would also be possible to join event reports from multiple stages by the enrollment, where every row is the same data point from a stage.

baseurl<-"https://play.dhis2.org/2.29/"
username<-"admin"
password<-"district"

loginDHIS2(baseurl, username)
## [1] TRUE
url<-paste0(baseurl,"api/29/analytics/events/query/eBAyeGv0exc.csv?dimension=pe:201812;201811&dimension=ou:ImspTQPwCqd&dimension=x7PaHGvgWY2&dimension=vV9UWAZohSf&dimension=GieVkTxp4HH&dimension=qrur9Dvnyt5&dimension=b8hd33dWjR6&dimension=oZg33kd9taw&stage=Zj7UnCAulEk&displayProperty=NAME&tableLayout=true&columns=pe;ou;x7PaHGvgWY2;vV9UWAZohSf;GieVkTxp4HH;qrur9Dvnyt5;b8hd33dWjR6;oZg33kd9taw&rows=pe;ou;x7PaHGvgWY2;vV9UWAZohSf;GieVkTxp4HH;qrur9Dvnyt5;b8hd33dWjR6;oZg33kd9taw") 
#now we GET command to fetch the data, and then read the content into a dataframe
report2 <-read_csv(content(GET(url)))

colength<-length(report2)

#index the observations by event date, only display data columns (last 5)
r2 <- report2 %>%
 arrange(`Event date`) %>%
 select((colength-5):colength)


#parallel coordinates with color based on gender
parcoords::parcoords(data = r2,
  color = list(
 # discrete or categorical column
 colorScale = "scaleOrdinal",
 colorBy = "Gender",
 colorScheme = "schemeCategory10"),
 withD3 = TRUE,
   brushMode = '1D-axes',
   alphaOnBrushed = 0.2,
   queue = TRUE,
   rate = 50,
   reorderable = TRUE)

It looks like Trainingland has some very fat babies, and some gravity-defying seniors! But with so much data it can be hard to read correlations in this plot. And once we identify these outliers, we will need to investigate their cases…

Let’s filter it down to fewer cases, and link the plot to a data table.

These visuals are better displayed side-by-side in the complementary flexdashboard example.

#include organization unit in our output
r2 <- report2 %>%
 arrange(`Event date`) %>%
 select((colength-5):colength)


#to improve performance, we'll ony select the most recent 1000 observations
new_r2<-head(r2, 1000)

sd2 <- crosstalk::SharedData$new(new_r2)

pc <- parcoords(data = sd2,
  brushMode = '1d',
  alphaOnBrushed = 0.2,
  queue = TRUE,
  rate = 50,
  reorderable = TRUE)



bscols(widths=c(12),
            pc,
            makeDT(sd2))

9 R Tools for Metadata Import/Export Support

We can also use R to perform metadata update operations. Some simple tools in R Markdown can help us understand what data we are actually being updated.

Here is a basic example: updating your user name!

library(diffr)

url<-paste0(baseurl, "api/me")
me<-fromJSON(content(GET(url),"text"))

#what is this data?
listviewer::jsonedit(me)
#hey, thats not me!
new_me<-me
new_me$name<-"Brian O'Donnell"
#note JSON data is represented as nested lists in R!

write_json(me, "me.json", auto_unbox=TRUE, pretty=TRUE)
write_json(new_me, "new_me.json", auto_unbox=TRUE, pretty=TRUE)

#see the difference
diffr::diffr("me.json", "new_me.json")
url<-paste0(baseurl, "api/users/",me$id,".json")
r<-httr::PUT(url=url, body=upload_file("new_me.json"), content_type_json())

#how did the import work out?
listviewer::jsonedit(content(r))

10 Summary

This demo showed use cases where data can be meaningfully extracted from the DHIS2 API into R, providing examples of how you might automate existing reporting systems, or improve the analysis/visualization of existing Tracker data.

Note there are other ways you can use DHIS2 + RMarkdown to do some things DHIS2 cannot currently do, like:

  • Analyze user access and audit logs
  • Visualize relationship networks between tracker instances
  • Organize favorites by number of views
  • Check metadata like indicators or data elements for inconsistencies

But no one language or workflow can solve all possible data extraction and munging problems. And eventually, the methods described above will all need to be reproduced and updated with something even better…

So I hope this tutorial helps more DHIS2 users find a new, sustainable way to work with their data in R.

Institutional affiliationsInstitutional affiliations

Institutional affiliations

Additional RMarkdown styles and options available at the RMarkdown website. More options for interactive HTML widgets are available here.

Template document produced by Brian O’Donnell for the eRegistries Initiative at the Norwegian Institute of Public Health.

Visit eRegistries on the DHIS2 Community of Practice.

LS0tDQp0aXRsZTogIlJvdXRpbmUgREhJUzIgVHJhY2tlciBSZXBvcnRzIHdpdGggUiINCmF1dGhvcjogIkJyaWFuIE8nRG9ubmVsbCAoTklQSCkiDQpkYXRlOiAiT2N0b2JlciA5LCAyMDE5Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZm9sZGluZzogc2hvdw0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiAnNCcNCi0tLQ0KDQoNCiMgT3ZlcnZpZXcNCk1vcmUgY291bnRyaWVzIGNvbnRpbnVlIHRvIGFkb3B0IERISVMyIGFzIGEgbmF0aW9uYWwgaGVhbHRoIGluZm9ybWF0aW9uIHN5c3RlbSwgYnV0IHJlY2VudGx5LCB0aGUgc29mdHdhcmUncyBuZXcgZmVhdHVyZXMgdG8gY2FwdHVyZSBtYW55IGRpZmZlcmVudCB0eXBlcyBvZiBkYXRhIGhhdmUgb2Z0ZW4gb3V0cGFjZWQgaXRzIG5hdGl2ZSB0b29scyB0byBhbmFseXplIGFuZCB2aXN1YWxpemUgdGhvc2UgZGF0YS4gVGhpcyBpcyBlc3BlY2lhbGx5IHRoZSBjYXNlIGZvciBpbmRpdmlkdWFsLWxldmVsIGhlYWx0aCByZWNvcmRzIGZvdW5kIGluIFtESElTMiBUcmFja2VyXShodHRwczovL3d3dy5kaGlzMi5vcmcvaW5kaXZpZHVhbC1kYXRhLXJlY29yZHMpLg0KDQpUaGUgZm9sbGl3am5nIHR1dG9yaWFsIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvIGV4dHJhY3QgbG9uZ2l0dWRpbmFsLCBpbmRpdmlkdWFsLWxldmVsIGRhdGEgZnJvbSBESElTMiwgdGhlbiBkaXNwbGF5IGl0IHRocm91Z2ggaW50ZXJhY3RpdmUgSFRNTCB3aWRnZXRzLCB0byBwcm92aWRlIGFuIG92ZXJ2aWV3IG9mIHRyZW5kcyB3aGljaCBjYW4gYmUgImRyaWxsZWQgZG93biIgZm9yIGZ1cnRoZXIgYW5hbHlzaXMuDQoNCiMjIyBXaHkgaXMgUiBhIGdvb2QgZml0IGZvciBESElTMj8NCkluIHRoZSBwYXN0IGZpdmUgeWVhcnMsIFIgaGFzIGJlY29tZSBvbmUgb2YgdGhlIHRvcCBsYW5ndWFnZXMgZm9yIGhlYWx0aCBhbmFseXN0cyByZXNlYXJjaGVycywgaW4gcGFydGljdWxhciB0aG9zZSB0aG9zZSB3aXRoIGxlc3Mgdm9jYXRpb25hbCB0cmFpbmluZyBpbiBwcm9ncmFtbWluZy4gVmFyaW91cyBSIHBhY2thZ2VzIGhhdmUgYmVlbiBkZXZlbG9wZWQgdG8gcHVsbCBvbmxpbmUgZGF0YSBpbnRvIGFuIFIgZW52aXJvbm1lbnQgdGhyb3VnaCBBUElzLCBpbnRlcmZhY2UgUiB3aXRoIEphdmFzY3JpcHQgZGF0YSB2aXN1YWxpemF0aW9uIGxpYnJhcmllcywgYW5kIHJvbGwgdGhlIHJlc3VsdHMgaW50byBtb2Rlcm4sIGludGVyYWN0aXZlIEhUTUwgcmVwb3J0cy4gDQoNClNvIGl0cyBlYXNpZXIgdGhhbiBldmVyIHRvIGxpbmsgUiB3aXRoIERISVMyIGZvciByb2J1c3QgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24sIHlldCB0aGUgW2F2YWlsYWJsZSBkb2N1bWVudGF0aW9uXShodHRwczovL2RvY3MuZGhpczIub3JnL21hc3Rlci9lbi9kZXZlbG9wZXIvaHRtbC9yc2V0dXAuaHRtbCkgZG9lcyBub3QgYWRkcmVzcyBIVE1MIHdpZGdldHMgb3IgdHJhY2tlciBkYXRhLg0KDQojIyMgV2hvIFRoaXMgaXMgVHV0b3JpYWwgRm9yDQoqIFlvdSBoYXZlIHNvbWUgYmFzaWMgZ3Jhc3Agb2YgUiwgUHl0aG9uLCBTVEFUQSwgb3IgYW5vdGhlciBzY3JpcHRpbmcgbGFuZ3VhZ2UNCiogWW91IHdhbnQgcm91dGluZSBoaWdoLWxldmVsIHJlcG9ydHMgZnJvbSBESElTMiwgcGFydGljdWxhcmx5IGZyb20gcHJvZHVjdGlvbiBpbnN0YW5jZXMgd2hlcmUgeW91IGxhY2sgYWRtaW4gYWNjZXNzIChlLmcuIHRoZSBwYXRpZW50IGRhdGFiYXNlIGlzIGhvc3RlZCBieSBhbm90aGVyIGNvdW50cnkpLCBzbyB0aGUgcmVwb3J0cyBzaG91bGQgYmUgZnVsbHkgcmVwcm9kdWNpYmxlIGJ5IGEgY29sbGVhZ3VlDQoqIFlvdSdyZSBpbnRlcmVzdGVkIGluIGNvbWJpbmluZyBldmVudCBkYXRhIGluIHRyYWNrZXIgcHJvZ3JhbXMgZm9yIGJhc2ljIG1vZGVscyByZXF1aXJpbmcgbG9uZ2l0dWRpbmFsIGluZGl2aWR1YWwgZGF0YQ0KKiBZb3Ugd2FudCBzb21lIGludGVyYWN0aXZpdHkgYmV0d2VlbiBtYXBzLCB0YWJsZXMsIGFuZCBjaGFydHMgdG8gaW1wcm92ZSBkYXRhIGV4cGxvcmF0aW9uDQoNCg0KIyMjIFdobyBUaGlzIFR1dG9yaWFsIGlzICpOT1QqIEZvcg0KKiBZb3UgY29kZSBfYSBsb3RfIGluIEphdmFzY3JpcHQgYWxyZWFkeS4gWW91IGNhbiBkZXZlbG9wIGEgY3VzdG9tIGFwcCBmb3IgeW91ciBESElTMiB1c2UgY2FzZSwgYW5kIHNoYXJlIGl0IHdpdGggdGhlIGNvbW11bml0eSENCiogWW91IHdhbnQgdG8gZHVtcCBfYWxsXyBkYXRhIGZyb20gREhJUzIgaW50byBhbm90aGVyIHN5c3RlbSBmb3IgYWR2YW5jZWQgYW5hbHlzaXMuIFByb2JhYmx5IGVhc2llciB0byBwdWxsIGZyb20gdGhlIGRhdGFiYXNlIHRhYmxlcyBkaXJlY3RseS4NCiogWW91IHdhbnQgdG8gX3BhcmFtZXRlcml6ZV8geW91ciByZXBvcnRzLCBzbyBkYXRhIGNhbiBiZSBfcmVhbC10aW1lXyBhbmQgdGFpbG9yZWQgdG8gYSBkaXN0aW5jdCB1c2VyL29yZyB1bml0LiBZb3UgY2FuIHByb2JhYmx5IHVzZSBESElTMidzIG5hdGl2ZSBQdXNoIEFuYWx5dGljcyBhbmQgSFRNTCByZXBvcnRzLCBvciB5b3UgY2FuIHN0ZXAgeW91ciBSIHNraWxscyB3aXRoIGEgU2hpbnkgZGFzaGJvYXJkLg0KDQoNCg0KIyBHZXR0aW5nIHN0YXJ0ZWQNCkZpcnN0IHRoaW5ncyBmaXJzdDogV2VsY29tZSEgVGhpcyBpcyBhbiBbUk1hcmtkb3duIGRvY3VtZW50XShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS8pLiBJZiB5b3UgaGF2ZSBSIGFuZCBSIHN0dWRpbyBvbiB5b3VyIGxvY2FsIG1hY2hpbmUsIHlvdSBjYW4gZG93bmxvYWQgdGhlIGNvZGUgYnkgY2xpY2tpbmcgdGhlIGJ1dHRvbiBhdCB0b3AgcmlnaHQgYW5kIHRyeSBpdCB5b3Vyc2VsZi4gDQoNCldlJ2xsIHdvcmsgYSBiaXQgd2l0aCBob3cgdG8gZXh0cmFjdCBkYXRhIGZyb20gdGhlIERISVMyIEFQSS4gVG8gZG8gdGhhdCwgd2Ugd2lsbCBtYWtlIHN1cmUgdG8gc2V0IHVwIG91ciBSIHNlc3Npb24gd2l0aCB0aGUgcmVxdWlyZWQgUiBwYWNrYWdlcy4oVGhpcywgaXQgdHVybnMgb3V0LCBpcyBub3Qgc28gaW50dWl0aXZlLCBhbHRob3VnaCBhIGhhbmR5IG1ldGhvZCBmb3IgX3JlcHJvZHVjaWJsZV8gc2Vzc2lvbiBzZXQtdXAgaXMgW2ZvdW5kIGhlcmVdKGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tL2xvYWRpbmctYW5kb3ItaW5zdGFsbGluZy1wYWNrYWdlcy1wcm9ncmFtbWF0aWNhbGx5LykpIERvd25sb2FkaW5nIHBhY2thZ2VzIGZyZXNoIG1heSB0YWtlIHNvbWUgdGltZSwgZXNwZWNpYWxseSBpZiB5b3UgaGF2ZSBzbG93IGludGVybmV0Li4uDQoNCldlIGFsc28gbmVlZCB0byBhZGQgdGhlIGJhc2V1cmwgYW5kIHVzZXJuYW1lIHRvIGxvZy1pbiB0byB5b3VyIERISVMyIGluc3RhbmNlIHdpdGggdGhlIFtodHRyIHBhY2thZ2VdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9odHRyL3ZlcnNpb25zLzEuNC4xKSBhbmQgW2tleXJpbmcgcGFja2FnZV0oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vaG93LXRvLWhpZGUtYS1wYXNzd29yZC1pbi1yLXdpdGgtdGhlLWtleXJpbmctcGFja2FnZS8pDQoNCl9Ob3RlOiBJdCBpcyBiZXN0IHByYWN0aWNlIG5vdCB0byBzdG9yZSB0aGUgcGFzc3dvcmQgd2l0aGluIHRoZSBzY3JpcHQgYXMgaXQgY2FuIGNyZWF0ZSBzZWN1cml0eSB2dWxuZXJhYmlsaXRpZXMuIFRoZSB1c2VyIHdobyAia25pdHMiIHRoZSBzY3JpcHQgaW50byBIVE1MIHdpbGwgYmUgcHJvbXB0ZWQgZm9yIHRoZSBkaGlzMiBQbGF5IGRlbW8gcGFzc3dvcmQuIEl0IHdpbGwgdGhlbiBiZSBzdG9yZWQgYXMgYSBsb2NhbCBlbnZpcm9ubWVudCB2YXJpYWJsZSBvbiB0aGUgdXNlcidzIG1hY2hpbmVfDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRSwgZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFKQ0Kb3B0aW9ucyhlbmNvZGluZyA9ICJVVEYtOCIpDQoNCiNJTVBPUlRBTlQgTUFOVUFMIFNURVBTIEJFRk9SRSBSVU5OSU5HDQojY3JlZGVudGlhbHMgYW5kIGdsb2JhbCB2YXJpYWJsZXMjDQojKGluY2x1ZGUgaHR0cHM6Ly8gIGFuZCBsYXN0IGJhY2tzbGFzaCBvZiBESElTMiBVUkwsIGluIHF1b3RlcykNCmJhc2V1cmw8LSJodHRwczovL3BsYXkuZGhpczIub3JnLzIuMzEuNS8iDQp1c2VybmFtZTwtImFkbWluIg0KDQoNCiMjX19fX19fTm93IGxvYWQgcGFja2FnZXMgYW5kIHNldCB1cF9fX19fIyMNCiMjZmlyc3QgbG9hZCByZXF1aXJlZCBwYWNrYWdlcw0KDQpyZXF1aXJlZF9wYWNrYWdlcyA8LSBjKCJnZ3Bsb3QyIiwic3RyaW5nciIsImh0dHIiLCJqc29ubGl0ZSIsImFzc2VydHRoYXQiLCJyZWFkciIsInRpYmJsZSIsInBseXIiLCJ0aWR5ciIsImR5Z3JhcGhzIiwieHRzIiwibHVicmlkYXRlIiwia25pdHIiLCJhc2twYXNzIiwiRFQiLCJrZXlyaW5nIiwiZGV2dG9vbHMiLCJjcm9zc3RhbGsiLCJwbG90bHkiLCJrbml0ciIsImxlYWZsZXQiLCJwYXJjb29yZHMiLCJkaWZmciIsImxpc3R2aWV3ZXIiLCJoaWdoY2hhcnRlciIsICJzZiIpDQojaGludDogdXNlIHNlc3Npb25JbmZvKCkgdG8gZmluZCBsb2FkZWQgcGFja2FnZXMgYWZ0ZXIgZmluaXNoaW5nIGRldmxvcG1lbnQsIHRoZW4gY29weSB0aGVtIGFib3ZlDQoNCiNkZWZpbmUgdGhlIGZ1bmN0aW9uIHRvIGNoZWNrIHRoYXQgYSBwYWNrYWdlIGlzIGluc3RhbGxlZA0KaXNfaW5zdGFsbGVkIDwtIGZ1bmN0aW9uKG15cGtnKSBpcy5lbGVtZW50KG15cGtnLCBpbnN0YWxsZWQucGFja2FnZXMoKVssMV0pDQoNCiNjaGVjayBpZiBlYWNoIHJlcXVpcmVkIHBhY2thZ2UgaXMgaW5zdGFsbGVkLCBpZiBpdCBpc250IGluc3RhbGwgaXQgZnJvbSBDUkFOLCBpZiBpdCBpcywgdGhlbiBsb2FkIGl0DQpsb2FkX29yX2luc3RhbGw8LWZ1bmN0aW9uKHJlcXVpcmVkX3BhY2thZ2VzKSB7DQogIGZvcihwYWNrYWdlX25hbWUgaW4gcmVxdWlyZWRfcGFja2FnZXMpICB7DQogICAgaWYoIWlzX2luc3RhbGxlZChwYWNrYWdlX25hbWUpKSAgICAgew0KICAgICAgIGluc3RhbGwucGFja2FnZXMocGFja2FnZV9uYW1lLHJlcG9zPSJodHRwOi8vbGliLnN0YXQuY211LmVkdS9SL0NSQU4iKSAgfQ0KICAgIGxpYnJhcnkocGFja2FnZV9uYW1lLGNoYXJhY3Rlci5vbmx5PVRSVUUscXVpZXRseT1UUlVFLHZlcmJvc2U9RkFMU0UpDQogIH0NCn0NCg0KI3J1biB0aGUgZnVuY3Rpb24NCmxvYWRfb3JfaW5zdGFsbChyZXF1aXJlZF9wYWNrYWdlcykNCg0KI3lvdWxsIG5vdyBiZSBwcm9tcHRlZCBmb3IgcGFzc3dvcmQgKGRpc3RyaWN0KQ0KcmVxdWlyZShrZXlyaW5nKQ0Ka2V5cmluZzo6a2V5X3NldCgiUGFzc3dvcmQiLCB1c2VybmFtZT11c2VybmFtZSkNCg0KYGBgDQoNCg0KVGhpcyBmdW5jdGlvbiB3aWxsIGxvZyBpbiB0byB0aGUgREhJUzIgaW5zdGFuY2UuIE9uY2Ugd2UgbG9nIGluLCB3ZSBkbyBub3QgbmVlZCB0byBsb2cgaW4gYWdhaW4gZm9yIGFsbCBvdGhlciBxdWVyaWVzISAoQ3JlZGl0IGZvciB0aGlzIGZ1bmN0aW9uIHRvIEphc29uIFBpY2tlcmluZydzIFt2ZXJ5IHVzZWZ1bF0oaHR0cDovL3d3dy5ycHVicy5jb20vamFzb25fcF9waWNrZXJpbmcvMTM5NTg5KSBbUi1mb3ItREhJUzIgdHV0b3JpYWxzXShodHRwczovL3JwdWJzLmNvbS9qYXNvbl9wX3BpY2tlcmluZy8yOTgxMDYpDQoNCmBgYHtyIGxvZ2luLCBtZXNzYWdlPVRSVUUsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFfQ0KDQpsb2dpbkRISVMyPC1mdW5jdGlvbihiYXNldXJsLHVzZXJuYW1lKSB7DQogICAgdXJsPC1wYXN0ZTAoYmFzZXVybCwiYXBpL21lIikNCiAgICByPC1HRVQodXJsLGF1dGhlbnRpY2F0ZSh1c2VybmFtZSwga2V5X2dldCgiUGFzc3dvcmQiLCB1c2VybmFtZT11c2VybmFtZSkgKSkNCiAgICBhc3NlcnRfdGhhdChyJHN0YXR1c19jb2RlID09IDIwMEwpIH0NCg0KaWYobG9naW5ESElTMihiYXNldXJsLHVzZXJuYW1lKSl7DQogIHByaW50KCJzdWNjZXNzZnVsbHkgbG9nZ2VkIGluIikNCn0NCmBgYA0KDQoNCiMgUmVuZGVyaW5nIFBpdm90IFRhYmxlcyBhbmQgUmVwb3J0cyBmcm9tIERISVMyDQoNClRoaXMgaXMgaG93IGRhdGEgZnJvbSBESElTMiBhcmUgb2Z0ZW4gc2hhcmVkLiBPZnRlbiwgaXQncyB0aGUgZWFzaWVzdCBzb2x1dGlvbiBmb3IgdGhlIGNvbnRleHQuLi4NCg0KYGBge3IgZGF0YSBzaGFyZSwgZWNobz1GQUxTRSwgZmlnLmhlaWdodD01LGZpZy5jYXA9IlNvdXJjZTogTFNUSE0sIENTdG9jayIsZmlnLnNob3c9J2hvbGQnLGZpZy5hbGlnbj0nY2VudGVyJ30NCg0KI21jaCAtIG9uIHBhcGVyDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9pZGVhcy5sc2h0bS5hYy51ay93cC1jb250ZW50L3VwbG9hZHMvMjAxOS8wMS9QMTA2MDMwN19zbWFsbC0xMjUweDgzMy5qcGciKQ0KDQojQ3N0b2NrIC0gbW9iaWxlIGRhc2hib2FyZA0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vd3d3Lm9kZXNzLmlvL2Fzc2V0cy9pbWFnZXMvMS9jU3RvY2slMjAlMjgxJTI5LTI4ZTY0OWYxLmpwZyIpDQoNCmBgYA0KDQpCdXQgYnkgbGV2ZXJhZ2luZyB0aGUgREhJUzIgYW5hbHl0aWNzIEFQSSBpbiBSTWFya2Rvd24sIHdlIGNhbiBkb3dubG9hZCBhbmQgYW5hbHl6ZSBwaXZvdCB0YWJsZXMsIGV2ZW50IHJlcG9ydHMsIG1ldGFkYXRhLCBhbmQgZXZlbiByYXcgZGF0YSwgdGhlbiByZW5kZXIgdGhlIHJlc3VsdHMgaW50byBQREYsIHdvcmQsIGFuZCBIVE1MIGRvY3VtZW50cyB0byBzaGFyZS4gVGhlcmUgaXMgbm8gcmVhc29uIHRvIHNlbmQgZXhjZWwgZmlsZXMgb3Igc2NyZWVuc2hvdHMgYmFjayBhbmQgZm9ydGggd2l0aCB5b3VyIHBhcnRuZXJzLg0KDQpUaGUgZWFzaWVzdCB3YXkgdG8gY29uc3RydWN0IGFuIGFuYWx5dGljcyBBUEkgcXVlcnkgaXMgdXNpbmcgdGhlIHBpdm90IHRhYmxlIG9yIGV2ZW50IHJlcG9ydHMgYXBwcy4gVGhlbiwgImRvd25sb2FkIiBhbmQgImRvd25sb2FkIGFzIGh0bWwiIHdpbGwgZ2l2ZSB5b3UgdGhlIGxpbmsoIFt0aGUgdmlkZW8gYmVsb3ddKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9NHBWQV9EZWVYaHcpIGV4cGxhaW5zIGhvdyB0byBkb3dubG9hZCBhIHBpdm90IHRhYmxlIGFzIFhMUywgY2hvb3NlIGRvd25sb2FkIGFzIGh0bWwgaW5zdGVhZCBmb3IgdGhlIGFuYWx5dGljcyBsaW5rKS4gWW91IGNhbiBjaGFuZ2UgdGhlIGV4dGVuc2lvbiB0byAuanNvbiwgLmh0bWwrY3NzLCBvciAuY3N2LiBbU2VlIERISVMyIGRldmVsb3BlciBndWlkZSBmb3IgbW9yZSBkZXRhaWxzXShodHRwczovL2RvY3MuZGhpczIub3JnL21hc3Rlci9lbi9kZXZlbG9wZXIvaHRtbC9kaGlzMl9kZXZlbG9wZXJfbWFudWFsX2Z1bGwuaHRtbCkNCg0KPGlmcmFtZSB3aWR0aD0iNTYwIiBoZWlnaHQ9IjMxNSIgc3JjPSJodHRwczovL3d3dy55b3V0dWJlLmNvbS9lbWJlZC80cFZBX0RlZVhodyIgZnJhbWVib3JkZXI9IjAiIGFsbG93PSJhY2NlbGVyb21ldGVyOyBhdXRvcGxheTsgZW5jcnlwdGVkLW1lZGlhOyBneXJvc2NvcGU7IHBpY3R1cmUtaW4tcGljdHVyZSIgYWxsb3dmdWxsc2NyZWVuPjwvaWZyYW1lPg0KDQpUaGUgbmV4dCBxdWVyeSBzZWFyY2hlcyBmb3IgQU5DIGxhYiBldmVudHMgaW4gdGhlIGxhc3QgMyBtb250aHMgaW4gdGhlICJXSE8gUk1OQ0ggVHJhY2tlciIgcHJvZ3JhbSB0aGF0IGhhdmUgaGVtb2dsb2JpbiB2YWx1ZXMuIEluIGEgcmVhc2xpc3RpYyBzY2hlbmFyaW8sIGEgbG93IGhlbW9nbG9iaW4gdmFsdWUgc2hvdWxkIFtnbyB1cCBvdmVyIHRpbWUgZHVlIHRvIGlyb24gc3VwcGxlbWVudGF0aW9uIHRyZWF0bWVudF0oaHR0cHM6Ly93d3cubWF5b2NsaW5pYy5vcmcvaGVhbHRoeS1saWZlc3R5bGUvcHJlZ25hbmN5LXdlZWstYnktd2Vlay9pbi1kZXB0aC9hbmVtaWEtZHVyaW5nLXByZWduYW5jeS9hcnQtMjAxMTQ0NTUpLg0KDQpgYGB7ciBnZXQgZGF0YSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBwYWdlZC5wcmludD1GQUxTRX0NCg0KdXJsPC1wYXN0ZTAoYmFzZXVybCwNCiAgICAgICAgICAgICJhcGkvMjkvYW5hbHl0aWNzL2V2ZW50cy9xdWVyeS9XU0dBYjVYd0ozWS5jc3Y/IiwgI2V2ZW50IHF1ZXJ5IGZvciAiUk1DSCB0cmFja2VyIiBwcm9ncmFtDQogICAgICAgICAgICAiZGltZW5zaW9uPXBlOkxBU1RfM19NT05USFMiLCAjRXZlbnRzIGxhc3QgbW9udGgNCiAgICAgICAgICAgICImc3RhZ2U9ZWRxbGJ1a3dSZlEmZGltZW5zaW9uPW91Okltc3BUUVB3Q3FkIiwgI3N0YWdlIGFuZCBvcmcgdW5pdCAocm9vdCkNCiAgICAgICAgICANCiAgICAgICAgICAgICNkYXRhIGRpbWVuc2lvbnMgYW5kIG90aGVyIGRpc3BsYXkgcHJvcGVydGllcw0KICAgICAgICAgICAgIiZkaW1lbnNpb249dFlQSVpLRVZoMjMmZGltZW5zaW9uPXZBTkFYd3RMd2NUJmRpbWVuc2lvbj13NzVLSjJtYzR6eiYiLCANCiAgICAgICAgICAgICJkaXNwbGF5UHJvcGVydHk9TkFNRSZ0YWJsZUxheW91dD10cnVlJmNvbHVtbnM9cGU7b3U7dFlQSVpLRVZoMjM7dkFOQVh3dEx3Y1Q7dzc1S0oybWM0enomcm93cz1wZTtvdTt0WVBJWktFVmgyMzt2QU5BWHd0THdjVDt3NzVLSjJtYzR6eiIpIA0KDQojbm93IHdlIEdFVCBjb21tYW5kIHRvIGZldGNoIHRoZSBkYXRhLCBhbmQgdGhlbiByZWFkIHRoZSBjb250ZW50IGludG8gYSBkYXRhZnJhbWUNCm91dHB1dDwtaHR0cjo6R0VUKHVybCkNCg0KcmVwb3J0MSA8LXJlYWRfY3N2KGNvbnRlbnQob3V0cHV0KSkNCg0KI2xldHMgdGFrZSBhIGxvb2sgYXQgdGhlIGRhdGENCmhlYWQocmVwb3J0MSkNCg0KYGBgDQoNCiMgRGF0YSBUYWJsZXMNCg0KTW9kZXJuIHdlYiBwYWdlcyByZW5kZXIgZGF0YSBtb3JlIHVzZWZ1bGx5LCBhbmQgdXN1YWxseSBpbnRlcmFjdGl2ZWx5LiBXZSBjYW4gZG8gdGhhdCBpbiBSIHNpbXBseSB3aXRoIHRoZSBbRFQgcGFja2FnZV0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9EVC8pLg0KDQojIyBVZ2x5IEV4YW1wbGUNCg0KSGVyZXMgdGhlIGRlZmF1bHQgZGF0YXRhYmxlIGxheW91dA0KYGBge3Igc2ltcGxlIGRhdGEgdGFibGUgb3V0cHV0LCBmaWcud2lkdGg9OCwgZmlnLmhlaWdodD0xMH0NCg0KRFQ6OmRhdGF0YWJsZShyZXBvcnQxKQ0KYGBgDQoNCg0KTWF5YmUgdGhhdCB3YXMgZG9uZSBhIGJpdCB0b28gc2ltcGx5Li4uDQoNCiMjIFByZXR0eSBFeGFtcGxlDQoNCk5vdyBsZXQncyBtYWtlIHRoZSBwcmVzZW50YXRpb24gb2YgZGF0YSBhIGJpdCBuaWNlcg0KDQpgYGB7ciBwcmV0dHkgZGF0YSB0YWJsZSwgZWNobz1UUlVFLCBmaWcud2lkdGg9OH0NCg0KI25vdyB0cnkgYSAicHJldHR5IiBkYXRhIHRhYmxlDQoNCiN3ZWxsIGp1c3QgZ2V0IGFsbCBldmVudCBkYXRlLCBvdSBuYW1lLCBhbmQgZGF0YSBjb2x1bW5zLiANCiNUaGVuIHNvcnQgYnkgZXZlbnQgZGF0ZSBhbmQgb3UgbmFtZQ0KbGVuZ3RoY29scyA8LSBmdW5jdGlvbih4KSBsZW5ndGgoY29sbmFtZXMoeCkpDQoNCnByZXR0eV9yZXBvcnQxIDwtIHJlcG9ydDEgJT4lDQogIHNlbGVjdChjKDEsIDMsIDYsIDksIChsZW5ndGhjb2xzKHJlcG9ydDEpLTIpOmxlbmd0aGNvbHMocmVwb3J0MSkpKSAlPiUgI2dldCBvbmx5IHRoZSBuZWNlc3NhcnkgZGF0YSBjb2x1bW5zDQogIGFycmFuZ2UoZGVzYyhgRXZlbnQgZGF0ZWApKSAlPiUNCiAgbXV0YXRlKCJFdmVudCBkYXRlIj1hcy5EYXRlKGx1YnJpZGF0ZTo6eW1kX2htcyhgRXZlbnQgZGF0ZWAsIHR6ID0gIlVUQyIpKSkgJT4lDQogICNMZXRzIGFsc28gYWRkIHRoZSBldmVudCBJRCBhcyBhIGh5cGVybGluaw0KICBtdXRhdGUoIkV2ZW50IExpbmsiPXBhc3RlMCgiPGEgaHJlZj1cIiIsYmFzZXVybCwiYXBpL2V2ZW50cy8iLEV2ZW50LCJcIj4iLCJFdmVudCBMaW5rIiwiPC9hPiIpKQ0KDQoNCiN0aGUgZXh0ZW5zaW9uIHdpbGwgYWxsb3cgdXMgdG8gcHJldHR5IHByaW50LCB3aXRoIGEgaHlwZXJsaW5rDQojY3JlYXRlIHRoaXMgYXMgYSBmdW5jdGlvbiBzbyB3ZSBjYW4gY29tZSBiYWNrIHRvIGl0IGxhdGVyDQoNCm1ha2VEVCA8LWZ1bmN0aW9uKHgpew0KICAgICAgICAgICAgICBEVDo6ZGF0YXRhYmxlKHgsDQogICAgICAgICAgICAgIGZpbHRlciA9ICdib3R0b20nLA0KICAgICAgICAgICAgICBjYXB0aW9uID0gIkV2ZW50IHJlcG9ydCBleGFtcGxlIiwNCiAgICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdCdXR0b25zJywgDQogICAgICAgICAgICAgIGVzY2FwZSA9IEZBTFNFLA0KICAgICAgICAgICAgICByb3duYW1lcyA9IEZBTFNFLA0KICAgICAgICAgICAgICB3aWR0aD0gIjEwMCUiLA0KICAgICAgICAgICAgICBjbGFzcyA9ICJjb21wYWN0IiwNCiAgICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoDQogICAgICAgICAgICAgICAgcGFnZUxlbmd0aCA9IDUsDQogICAgICAgICAgICAgICAgZG9tID0gJ0JsZnJ0aXAnLCANCiAgICAgICAgICAgICAgICBidXR0b25zID0gbGlzdChJKCJjb2x2aXMiKSwNCiAgICAgICAgICAgICAgICBjKCdjb3B5JywgJ2NzdicsICdleGNlbCcsICdwZGYnKSkpKQ0KfQ0KDQptYWtlRFQocHJldHR5X3JlcG9ydDEpDQpgYGANCg0KKkFkdmFudGFnZXMgb2YgRFQgRm9ybWF0Kg0KDQoqIFNvcnQgYnkgY29sdW1uLCBhbmQgc3RhcnQgdHlwaW5nIGluIHRoZSBzZWFyY2ggYmFycyBhdCB0aGUgYm90dG9tIG9mIHRoZSBjb2x1bW5zIHRvIGZpbHRlcg0KKiBGaWx0ZXIgYnkgZGF0ZSByYW5nZQ0KKiBDaGFuZ2UgdGhlIHBhZ2Ugc2l6ZQ0KKiBVc2UgYSBnbG9iYWwgc2VhcmNoIGFjcm9zcyBjb2x1bW5zDQoqIENoYW5nZSB3aGF0IGNvbHVtbnMgYXJlIHZpc2libGUNCiogRXhwb3J0IHRoZSB0YWJsZSB0byBvdGhlciBjb21tb24gZm9ybWF0cw0KKiBDbGljayB0aGUgRXZlbnQgTGluayB0byBnbyB0byBhbiBBUEkgcXVlcnkgZm9yIHRoZSBldmVudA0KDQpZb3UgY2FuIGZpbmQgW21vcmUgb3B0aW9ucyBmb3IgdGhlIERUIHBhY2thZ2UgaGVyZV0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9EVC8pLg0KDQojIyMgRGV0ZWN0IFBhdHRlcm5zIDENCldoYXQgaWYgd2UgY2FuIHBsb3QgdGhlIGV2ZW50IGRhdGEgb3ZlciAiZGF5cyBlbnJvbGxlZCI/IERvIHdlIGRldGVjdCBhbnkgdHJlbmQ/DQoNCmBgYHtyIHByZXNzdXJlLCBmaWcuaGVpZ2h0PTN9DQpnZ3Bsb3QocmVwb3J0MSwgYWVzKHggPSBgV0hPIFJNTkNIIERheXMgZW5yb2xsZWRgLCB5ID0gYFdIT01DSCBIZW1vZ2xvYmluIHZhbHVlYCkpICsNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQoNCkxvb2tzIGxpa2UgdGhlcmVzIHZlcnkgbGl0dGxlIHBhdHRlcm4gYmV0d2VlbiBkYXlzIGVucm9sbGVkIGFuZCBoZW1vZ2xvYmluIHZhbHVlISBBdCB0aGUgdmVyeSBsZWFzdCBpbiBkZW1vLi4uDQoNCiMgTWVyZ2UgRXZlbnRzIGJ5IEVucm9sbG1lbnRzDQoNCkJ1dCBtYW55IG9mIHRoZXNlIGV2ZW50cyBjYW4gYmUgZ3JvdXBlZCBieSB0aGVpciBlbnJvbGxtZW50LiBMZXRzIG1lcmdlIHRoZXNlIGRhdGEgd2l0aCBlbnJvbGxtZW50IElEcyBmcm9tIHRoZSBwYXN0IDYgbW9udGhzLiAoRGVtbyBzZXJ2ZXIgbWF4ZXMgb3V0IGFmdGVyIDEwMDAgZW5yb2xsbWVudHMsIHNvIHdlJ2xsIGp1c3QgZ2V0IHRob3NlIGZvciBub3cuLi4pDQoNCmBgYHtyIGdldCBlbnJvbGxtZW50IElEcywgbWVzc2FnZT1GQUxTRX0NCg0KI2dldCBzdGFydCBkYXRlIGZvciAibGFzdCAzIG1vbnRocyINCmZvdXJtb250aHNhZ28gPC1yb3VuZF9kYXRlKHltZChTeXMuRGF0ZSgpKSwgdW5pdCA9ICJtb250aCIpICVtLSUgbW9udGhzKDQpIA0KDQoNCiNXZSB3aWxsIHVzZSB0aGUgRVZFTlRTIHJlc291cmNlLiANCiNUaGlzIGFsbG93cyB1cyB0byBxdWVyeSBkaXJlY3RseSBmb3IgYWxsIGV2ZW50cyBvZiBhIGNlcnRhaW4gcHJvZ3JhbSBhbmQgc3RhZ2UsIA0KI2J1dCBub3QgYmFzZWQgb24gZGF0YSB2YWx1ZXMuIA0KI0ZvciBzaW1wbGljaXR5IHdlJ2xsIGp1c3QgZ2V0IGZpcnN0IDEwMDAgcmVjb3Jkcy4NCg0KdXJsPC1wYXN0ZTAoYmFzZXVybCwiYXBpLzI5L2V2ZW50cy5qc29uP2ZpZWxkcz1lbnJvbGxtZW50LGV2ZW50LHRyYWNrZWRFbnRpdHlJbnN0YW5jZSIsDQogICAgICAgICAgICAgICAgICAgICImcHJvZ3JhbT1XU0dBYjVYd0ozWSZzdGFydERhdGU9Iixmb3VybW9udGhzYWdvLA0KICAgICAgICAgICAgICAgICAgICAiJnN0YWdlPWVkcWxidWt3UmZRIiwNCiAgICAgICAgICAgICAgICAgICAgIiZwYWdlU2l6ZT0iLDEwMDApDQoNCmVucm9sbG1lbnRfSURzIDwtZnJvbUpTT04oY29udGVudChHRVQodXJsKSwidGV4dCIpKQ0KDQoNCiNOb3cgd2Ugd2lsbCBqb2luIHRoZSBlbnJvbGxtZW50IElEcyB0byB0aGUgZXZlbnQgcmVwb3J0DQojV2Ugd2lsbCBhbHNvIGNyZWF0ZSBhIGxpbmsgdG8gdGhlIHJlY29yZCBpbiB0aGUgdHJhY2tlciBjYXB0dXJlIGFwcA0KZXZlbnRzX3dpdGhfZW5yb2xsbWVudHM8LSByZXBvcnQxICU+JQ0KICBkcGx5cjo6aW5uZXJfam9pbihlbnJvbGxtZW50X0lEcyRldmVudHMsIGJ5ID0gYygiRXZlbnQiID0gImV2ZW50IikpICU+JQ0KICAgIG11dGF0ZSgiRXZlbnQgZGF0ZSI9YXMuRGF0ZShsdWJyaWRhdGU6OnltZF9obXMoYEV2ZW50IGRhdGVgLCB0eiA9ICJVVEMiKSkpICU+JQ0KICBtdXRhdGUoIlJlY29yZCBMaW5rIj1wYXN0ZTAoIjxhIGhyZWY9XCIiLGJhc2V1cmwsIi9kaGlzLXdlYi10cmFja2VyLWNhcHR1cmUvaW5kZXguaHRtbCMvZGFzaGJvYXJkPyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICJ0ZWk9Iix0cmFja2VkRW50aXR5SW5zdGFuY2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICImcHJvZ3JhbT1XU0dBYjVYd0ozWSZvdT0iLGBPcmdhbmlzYXRpb24gdW5pdGAsDQogICAgICAgICAgICAgICAgICAgICAgICAgICJcIj5SZWNvcmQgTGluazwvYT4iKSkgJT4lDQogICNqdXN0IHNlbGVjdCBpbXBvcnRhbnQgdmFyaWFibGVzIGFuZCBhcnJhbmdlDQogIHNlbGVjdChjKDEsIDMsIGBPcmdhbmlzYXRpb24gdW5pdCBuYW1lYDpgUmVjb3JkIExpbmtgKSklPiUNCiAgc2VsZWN0KC1jKCJ0cmFja2VkRW50aXR5SW5zdGFuY2UiLCBgT3JnYW5pc2F0aW9uIHVuaXQgY29kZWAsIGBPcmdhbmlzYXRpb24gdW5pdGApKSAlPiUNCiAgYXJyYW5nZShkZXNjKGBFdmVudCBkYXRlYCkpDQoNCm1ha2VEVChldmVudHNfd2l0aF9lbnJvbGxtZW50cykNCmBgYA0KDQoNCiMjIERldGVjdCBQYXR0ZXJucyAyDQpNYXliZSB0aGVyZSBhcmUgdGVtcG9yYWwgdHJlbmRzIHdpdGhpbiBlbnJvbGxtZW50cz8NCg0KYGBge3IgdGVzdCBsaW5lIGNoYXJ0LCBmaWcuaGVpZ2h0ID0gM30NCg0KI0FkZCBhIGdncGxvdCBsaW5lIGNoYXJ0IGZvciBoZW1vZ2xvYmluIG92ZXIgZGF5cyBlbnJvbGxlZA0KZ2dwbG90KGV2ZW50c193aXRoX2Vucm9sbG1lbnRzLCANCiAgICAgICBhZXMoeD1gV0hPIFJNTkNIIERheXMgZW5yb2xsZWRgLCANCiAgICAgICAgICB5PWBXSE9NQ0ggSGVtb2dsb2JpbiB2YWx1ZWApKSArDQogICAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gZW5yb2xsbWVudCkpDQoNCmBgYA0KDQoNCk5lZ2F0aXZlIHJlc3VsdHMgaGVyZS4uIEJ1dCBtb3JlIHJlYWxpc3RpYyBkYXRhIHdvdWxkIHNob3cgaW5kaXZpZHVhbCB0cmVuZHMhIA0KDQoNCiMjIExvbmdpdHVkaW5hbCBkYXRhOiBHcm93dGggUGF0dGVybnMNCkluIHRoZSBleGFtcGxlIGJlbG93LCB3ZSBkb3dubG9hZCB0aGUgQ0RDJ3MgbGVuZ3RoLWZvci1hZ2UgWi1zY29yZSBkYXRhIGZvciBpbmZhbnQgZ2lybHMsIGFuZCByZW5kZXIgdGhlIGRhdGEgaW4gYW4gaW50ZXJhY3RpdmUgZ3Jvd3RoIGNoYXJ0Lg0KDQpSZW1lbWJlciwgYSBjaGlsZCByZWdpc3RlciBpbiBESElTMiBUcmFja2VyIGNvdWxkIHVzZSBhIHNpbWlsYXIgYXBwcm9hY2ggdG8gZGlzcGxheWluZyBncm93dGggb2YgYSBwb3B1bGF0aW9uIGNvaG9ydCBvdmVyIHRpbWUsIHdoZXJlIGVhY2ggc2VyaWVzIHJlcHJlc2VudHMgYW4gaW5kaXZpZHVhbCBpbmZhbnQncyB3ZWlnaHQgbWVhc3VyZW1lbnRzLg0KDQpgYGB7ciBncm93dGggY2hhcnRzLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQpiaXJ0aGRhdGU8LWZvdXJtb250aHNhZ28NCg0KY2RjX2RhdGE8LXJlYWRfY3N2KCJodHRwczovL3d3dy5jZGMuZ292L2dyb3d0aGNoYXJ0cy9kYXRhL3pzY29yZS96d3RhZ2VpbmYuY3N2IikNCmNkY19kYXRhPC0gY2RjX2RhdGEgJT4lDQojZm9yIHNpbXBsaWNpdHksIGp1c3QgbG9vayBhdCBnaXJscyBkYXRhDQogIHN1YnNldChTZXg9PTIpICU+JQ0KICBtdXRhdGUoIndlZWtzX2RpZmYiID0gNCAqIGFzLm51bWVyaWMoQWdlbW9zKSkgJT4lDQogIG11dGF0ZSgiZGF0ZSI9IGJpcnRoZGF0ZSArIGx1YnJpZGF0ZTo6d2Vla3MoKHdlZWtzX2RpZmYpKSkNCg0KDQojZ2F0aGVyIHVwIHRoZSBkYXRhIGFuZCBkaXNwbGF5IGFzIGxpbmUgY2hhcnQNCmhjIDwtIGNkY19kYXRhICU+JSBnYXRoZXIoenNjb3JlLCB3ZWlnaHQsIGAtMmA6YDJgKSAlPiUNCiAgaGNoYXJ0KHR5cGUgPSAibGluZSIsIGhjYWVzKHggPSAiQWdlbW9zIiwgeSA9ICJ3ZWlnaHQiLCBncm91cD0ienNjb3JlIikpICU+JQ0KICAJICBoY190aXRsZSh0ZXh0ID0gIlpzY29yZXMiKQ0KDQoNCiN3ZSBjYW4gZG8gdGhlIHNhbWUgYXMgYSB0aW1lIHNlcmllcyBjaGFydA0KeHRjaGFydDwtYXMueHRzKGNkY19kYXRhLCBvcmRlci5ieT1hcy5QT1NJWGN0KGNkY19kYXRhJGRhdGUpKSAlPiUgDQogIHN1YnNldChzZWxlY3QgPSBgMmA6YC0yYCkgJT4lDQogIGR5Z3JhcGgobWFpbiA9ICJaLVNjb3JlcyIsIHlsYWIgPSAiV2VpZ2h0IGluIEtnIikgJT4lDQogIGR5T3B0aW9ucyhjb2xvcnMgPSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoNywgIkRhcmsyIikpICU+JQ0KICAgIGR5SGlnaGxpZ2h0KGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuNiwNCiAgICAgICAgICAgICAgaGlkZU9uTW91c2VPdXQgPSBGQUxTRSwNCiAgICAgICAgICAgICAgaGlnaGxpZ2h0U2VyaWVzT3B0cyA9IGxpc3Qoc3Ryb2tlV2lkdGggPSAzKSkgJT4lDQogIGR5UmFuZ2VTZWxlY3RvcigpDQoNCiN0aGlzIG1ldGhvZCB1c2VzIGhpZ2hjaGFydGVyICANCmhjDQoNCiN0aGlzIG1ldGhvZCB1c2VzIGR5Z3JhcGhzDQp4dGNoYXJ0DQpgYGANCg0KDQojIEludGVyYWN0aXZlIENoYXJ0cyBpbiBDcm9zc3RhbGsNCk5vdyB3ZSBjYW4gYWRkIHNvbWUgbW9yZSAqaW50ZXJhY3Rpdml0eSohIFdlIHdpbGwgaW5jbHVkZSBfYnJ1c2hpbmdfIGNhcGFiaWxpdHkgdGhhdCBkeW5hbWljYWxseSBsaW5rcyBhIGNoYXJ0IGFuZCB0YWJsZS4gVGhpcyB3b3JrIGJ1aWxkcyBvbiB0aGUgW1IncyBDcm9zc3RhbGsgcGFja2FnZV0oaHR0cDovL3JzdHVkaW8uZ2l0aHViLmlvL2Nyb3NzdGFsay91c2luZy5odG1sKSwgd2hpY2ggam9pbnMgY2VydGFpbiB0eXBlcyBvZiBIVE1MIHdpZGdldHMgdG9nZXRoZXIgYmFzZWQgb24gYSBjb21tb24gInNoYXJlZCBkYXRhIiBvYmplY3QuIEltcG9ydGFudGx5LCB0aGUgbGlua2luZyBoYXBwZW5zIGRpcmVjdGx5IHdpdGhpbiB0aGUgYnJvd3NlciwgZWxpbWluYXRpbmcgdGhlIG5lZWQgZm9yIGEgU2hpbnkgc2VydmVyLiBIb3dldmVyLCBub3RlIHRoYXQgb25seSBzZWxlY3QgcGFja2FnZXMgYXJlIGF2YWlsYWJsZSB0byB3b3JrIHdpdGggY3Jvc3N0YWxrLCBhbmQgcGVyZm9ybWFuY2Ugd2lsbCBzbG93IHdpdGggbGFyZ2UgZGF0YXNldHMuDQoNCkZvciB0aGlzIGV4YW1wbGUsIHdlIHdpbGwgdXNlIFtEVCBwYWNrYWdlXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL0RULyksIFtQbG90bHkgcGFja2FnZV0oaHR0cHM6Ly93d3cuaHRtbHdpZGdldHMub3JnL3Nob3djYXNlX3Bsb3RseS5odG1sKSwgYW5kIFtMZWFmbGV0IHBhY2thZ2VdKGh0dHA6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9sZWFmbGV0LykuDQoNCmBgYHtyIGludGVyYWN0aXZpdHksIG1lc3NhZ2U9RkFMU0V9DQoNCiNmaXJzdCB3ZSBjcmVhdGVkIGEgInNoYXJlZCBkYXRhIG9iamVjdCINCnNkIDwtIFNoYXJlZERhdGEkbmV3KGV2ZW50c193aXRoX2Vucm9sbG1lbnRzKQ0KDQojY3JlYXRlIGEgcGxvdGx5IHNjYXR0ZXJwbG90DQpwPC1wbG90X2x5KHNkLCB4PSB+YFdITyBSTU5DSCBEYXlzIGVucm9sbGVkYCwgDQogICAgICAgICAgeT0gfmBXSE9NQ0ggSGVtb2dsb2JpbiB2YWx1ZWAsDQogICAgICAgICAgY29sb3IgPSB+YE9yZ2FuaXNhdGlvbiB1bml0IG5hbWVgLA0KICAgICAgICAgIHNpemUgPSAzLA0KICAgICAgICAgIHNob3dsZWdlbmQ9RkFMU0UpICU+JSANCiAgYWRkX21hcmtlcnMoYWxwaGEgPSAwLjUpICU+JQ0KICBoaWdobGlnaHQob24gPSAicGxvdGx5X3NlbGVjdGVkIiwgIG9mZiA9ICJwbG90bHlfZGVzZWxlY3QiKQ0KDQoNCiNXZSdsbCB1c2UgdGhlIGN1c3RvbSBkYXRhdGFibGUgZnVuY3Rpb24gZnJvbSBlYXJsaWVyIG9uIHRoZSBzaGFyZWQgZGF0YQ0KZHQ8LW1ha2VEVChzZCkNCg0KDQojY3Jvc3N0YWxrIGNvbWVzIHdpdGggYm9vdHN0cmFwIGZ1bmN0aW9ucyB0byBoZWxwIHVzIGFycmFuZ2UgdGhlIGludGVyYWN0aXZlIG9iamVjdHMNCiNwdXQgYSBvcmcgdW5pdCBmaWx0ZXIgb24gb25lIHNpZGUsIE9VIG9uIGFub3RoZXINCmJzY29scyh3aWR0aHMgPSBjKDQsTkEpLA0KICBsaXN0KA0KICAgIGZpbHRlcl9zZWxlY3QoIm9yZ1VuaXQiLCAiRmlsdGVyIGJ5IE9yZ1VuaXQiLCBzZCwgfmBPcmdhbmlzYXRpb24gdW5pdCBuYW1lYCksDQogIHApLA0KICBkdCkNCg0KYGBgDQoNCg0KKkhvdyB0byB1c2UgaW50ZXJhY3RpdmUgZmlsdGVyIGRvd246Kg0KDQoqIENsaWNrIG9uIG9uZSBvciBtdWx0aXBsZSBzY2F0dGVyIHBsb3QgZG90cyANCiogQ2xpY2sgYW5kIGRyYWcgYWNyb3NzIGFuIGFyZWEgb2YgZG90cyBvbiBzY2F0dGVyIHBsb3QNCiogU2VsZWN0IG9uZSBvciBtdWx0aXBsZSBldmVudCBmcm9tIHRhYmxlIA0KKiBDaG9vc2UgYSBzcGVjaWZpYyBvcmcgdW5pdA0KDQoNCiMgQWR2YW5jZWQgLSBMaW5rZWQgTWFwDQoNCk5vdyB3ZSB3aWxsIGxpbmsgYSBsZWFmbGV0IG1hcC4gRmlyc3QsIGxldCdzIGZldGNoIGNvb3JkaW5hdGVzIGZvciB0aGUgZXZlbnQgb3JnIHVuaXRzLCBhbmQgZGlzcGxheSB0aGVtIG9uIGEgc2ltcGxlIG1hcC4NCg0KYGBge3IgYmFzaWMgbWFwLCBlY2hvPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpsaWJyYXJ5KGxlYWZsZXQpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkcGx5cikNCg0KI2dldCBPVSBjb29yZGluYXRlcyBhbmQgbWVyZ2Ugd2l0aCB0aGUgZGF0YQ0KDQp1cmw8LXBhc3RlMChiYXNldXJsLCJhcGkvMjkvb3JnYW5pc2F0aW9uVW5pdHMuY3N2P3BhZ2luZz1mYWxzZSZmaWVsZHM9bmFtZSxjb29yZGluYXRlcyZmaWx0ZXI9Y29vcmRpbmF0ZXM6IW51bGwiKQ0Kb3V0cHV0IDwtcmVhZF9jc3YoY29udGVudChHRVQodXJsKSkpDQoNCiNtZXJnZSB0aGUgbmFtZSBvZiB0aGUgb3JnIHVuaXRzIHdpdGggdGhlIGV2ZW50IGRhdGENCm1hcG9iajwtZXZlbnRzX3dpdGhfZW5yb2xsbWVudHMgJT4lDQpkcGx5cjo6cmVuYW1lKCJuYW1lIj1gT3JnYW5pc2F0aW9uIHVuaXQgbmFtZWApICU+JQ0KZHBseXI6OmlubmVyX2pvaW4ob3V0cHV0KSAlPiUNCnNlcGFyYXRlKGNvb3JkaW5hdGVzLCBjKCJsb25naXR1ZGUiLCJsYXRpdHVkZSIpLCBzZXAgPSAiLCIsIGV4dHJhID0gImRyb3AiKQ0KbWFwb2JqJGxhdGl0dWRlPC1wYXJzZV9udW1iZXIobWFwb2JqJGxhdGl0dWRlKQ0KbWFwb2JqJGxvbmdpdHVkZTwtcGFyc2VfbnVtYmVyKG1hcG9iaiRsb25naXR1ZGUpDQoNCg0KDQojY3JlYXRlIG1hcCBvYmplY3QNCmxlYWZsZXQobWFwb2JqKSAlPiUgYWRkVGlsZXMoKSAlPiUNCiAgYWRkQ2lyY2xlcyhsYXQgPSB+bGF0aXR1ZGUsIGxuZyA9IH5sb25naXR1ZGUpDQoNCg0KYGBgDQoNCg0KVmVyeSBjb29sLiBOb3cgd2UgY2FuIGxpbmsgd2l0aCB0aGUgZGF0YSB0YWJsZSBhbmQgc2NhdHRlcnBsb3QuIElmIHlvdSBoaWdobGlnaHQgYW4gYXJlYSBvbiB0aGUgbWFwIG9yIHNjYXR0ZXJwbG90LCBpdCB3aWxsIGhpZ2hsaWdodCB0aGUgb3RoZXIgdmlzdWFsLCBhbmQgZmlsdGVyIHRoZSB0YWJsZSEgU2VsZWN0IGFueSBudW1iZXIgb2YgdGFibGUgaXRlbXMgdG8gaGlnaGxpZ2h0IG9uIHRoZSBjaGFydCBhbmQgbWFwLg0KDQpgYGB7ciBsaW5rZWQgbWFwLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUV9DQoNCiMjI3dlIG5lZWQgdG8gYWx0ZXIgb3V0IERUIGZ1bmN0aW9uIHRvIGhpZGUgdGhlIGxhdC9sb25nIGNvbHVtbnMNCiNldmVudHVhbGx5IHdlIHdpbGwgd2FudCB0byBoaWRlIGdlb2dyYXBoaWMgY29vcmRpbmF0ZXMgaW4gdGhlIGNvbHVtbnMNCiN0aGlzIGZ1bmN0aW9uIHJldHVybnMgdGhlIHBvc2l0aW9uIG9mIHRoZSBjb29yZGluYXRlcyBpbiB0aGUgdGFibGUgc28gd2UgY2FuIGV4Y2x1ZGUgdGhlbQ0KaGlkZWNvbHM8LWZ1bmN0aW9uKHgsIGEsIGIpIGlmIChpcy5TaGFyZWREYXRhKHgpICYmIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhICVpbiUgY29sbmFtZXMoeCRvcmlnRGF0YSgpKSAmJiANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYiAlaW4lIGNvbG5hbWVzKHgkb3JpZ0RhdGEoKSkpew0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92YWxzPC14JG9yaWdEYXRhKCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4oYyhncmVwKGEsIGNvbG5hbWVzKHJlbW92YWxzKSktMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXAoYiwgY29sbmFtZXMocmVtb3ZhbHMpKS0xKSkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBybShyZW1vdmFscykNCiAgfQ0KDQoNCiNyZW1lbWJlciB0aGF0IERUIGZ1bmN0aW9uIGVhcmxpZXI/IA0KbWFrZURUIDwtZnVuY3Rpb24oeCl7DQogICAgICAgICAgICAgIERUOjpkYXRhdGFibGUoeCwNCiAgICAgICAgICAgICAgZmlsdGVyID0gJ2JvdHRvbScsDQogICAgICAgICAgICAgIGNhcHRpb24gPSAiRXZlbnQgcmVwb3J0IGV4YW1wbGUiLA0KICAgICAgICAgICAgICBleHRlbnNpb25zID0gJ0J1dHRvbnMnLCANCiAgICAgICAgICAgICAgZXNjYXBlID0gRkFMU0UsDQogICAgICAgICAgICAgIHJvd25hbWVzID0gRkFMU0UsDQogICAgICAgICAgICAgIHdpZHRoPSAiMTAwJSIsDQogICAgICAgICAgICAgIGNsYXNzID0gImNvbXBhY3QiLA0KICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdCgNCiAgICAgICAgICAgICAgICBwYWdlTGVuZ3RoID0gNSwNCiAgICAgICAgICAgICAgICBkb20gPSAnQmxmcnRpcCcsIA0KICAgICAgICAgICAgICAgIGJ1dHRvbnMgPSBsaXN0KEkoImNvbHZpcyIpLA0KICAgICAgICAgICAgICAgIGMoJ2NvcHknLCAnY3N2JywgJ2V4Y2VsJywgJ3BkZicpKSwNCiAgICAgICAgICAgICAgICANCiAgICAgICAgI3dlIGp1c3QgaGlkZSB0aGVzZSB0d28gY29sdW1ucyBub3cNCiAgICAgICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KHZpc2libGU9RkFMU0UsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhcmdldHM9aGlkZWNvbHMoeCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAibGF0aXR1ZGUiLCJsb25naXR1ZGUiKSkpKSkNCg0KfQ0KDQoNCg0KI25vdyB3ZSBhcHBseSB0aGUgbWFwIGFzIGEgc2hhcmVkIGRhdGEgb2JqZWN0IQ0Kc2QxIDwtIFNoYXJlZERhdGEkbmV3KG1hcG9iaikNCg0KcDwtcGxvdF9seShzZDEsIHg9IH5gV0hPIFJNTkNIIERheXMgZW5yb2xsZWRgLCANCiAgICAgICAgICB5PSB+YFdIT01DSCBIZW1vZ2xvYmluIHZhbHVlYCwNCiAgICAgICAgICBjb2xvciA9IH5uYW1lLA0KICAgICAgICAgIHNpemUgPSAzLA0KICAgICAgICAgIHNob3dsZWdlbmQ9RkFMU0UpICU+JSANCiAgYWRkX21hcmtlcnMoYWxwaGEgPSAwLjMpICU+JQ0KICBoaWdobGlnaHQob24gPSAicGxvdGx5X3NlbGVjdGVkIiwgb2ZmID0gInBsb3RseV9kZXNlbGVjdCIpDQoNCg0KI1dlJ2xsIHVzZSB0aGUgY3VzdG9tIGRhdGF0YWJsZSBmdW5jdGlvbiBmcm9tIGVhcmxpZXIgb24gdGhlIHNoYXJlZCBkYXRhDQpkdDwtbWFrZURUKHNkMSkNCg0KDQojc3RpbGwgdHJ5aW5nIHRvIGZpZ3VyZSBvdXQgaG93IHRvIGhpZ2hsaWdodCBvbiB0aGUgbWFwIGJldHRlci4uLg0KDQpic2NvbHMod2lkdGhzID0gYyg0LE5BKSwNCiAgbGlzdChmaWx0ZXJfc2VsZWN0KCJvcmdVbml0IiwgIkZpbHRlciBieSBPcmdVbml0Iiwgc2QxLCB+bmFtZSksDQogIGxlYWZsZXQoc2QxLCB3aWR0aCA9ICIxMDAlIiwgaGVpZ2h0ID0gMzAwKSAlPiUNCiAgICBhZGRUaWxlcygpICU+JQ0KICAgIGFkZE1hcmtlcnMobGF0ID0gfmxhdGl0dWRlLCBsbmcgPSB+bG9uZ2l0dWRlKSksDQpwKQ0KZHQNCg0KYGBgDQoNCkhtbW0gdGhhdHMgYSBsb3Qgb2YgaW5mb3JtYXRpb24sIGFuZCB2ZXJ5IHRpZ2h0bHkgcGFja2VkLg0KDQpZb3UgY2FuIGRpc3BsYXkgaXQgaW4gYSBzZXBhcmF0ZSBwYWdlIGFzIGEgZmxleGRhc2hib2FyZC4gW1NlZSB0aGUgZmxleGRhc2hib2FyZCBkZW1vIG9mIHRoZXNlIGRhdGEgaGVyZS5dKGh0dHBzOi8vaWFtYm9kby5naXRodWIuaW8vcHJvamVjdHMvdHV0b3JpYWxzL2RlbW8yLmh0bWwpDQoNCg0KIyBBZHZhbmNlZCAtIFBhcmFsbGVsIENvb3JkaW5hdGVzDQoNCkFub3RoZXIgZ3JlYXQgd2F5IHRvIGRpc3BsYXkgY29ycmVsYXRpb25zIGFjcm9zcyBtYW55IGRhdGEgcG9pbnRzIGlzIGEgcGFyYWxsZWwgY29vcmRpbmF0ZXMgY2hhcnQuIFVzaW5nIHRoZSBbcGFyY29vcmRzXShodHRwczovL3RpbWVseXBvcnRmb2xpby5naXRodWIuaW8vcGFyY29vcmRzL2FydGljbGVzL2ludHJvZHVjdGlvbi10by1wYXJjb29yZHMtLmh0bWwpIHBhY2thZ2UsIHdlIGNhbiB2aXN1YWxpemUgdGhvdXNhbmRzIG9mIHBhdGllbnQgcmVjb3JkcyBhdCBvbmNlIHdpdGgganVzdCBhIGZldyBsaW5lcyBvZiBjb2RlLiANCg0KQXMgYW4gSFRNTCB3aWRnZXQsIHBhcmFsbGVsIGNvb3JkaW5hdGVzIGNhbiBiZSAiYnJ1c2hlZCIgLS0gY2xpY2sgYW5kIGRyYWcgb24gb25lIG1vcmUgb3IgYXhlcywgYW5kIHRoZSBncmFwaGljIGhpZ2hsaWdodHMgcm93cyB3aXRoaW4gdGhlIHNlbGVjdGVkIHJhbmdlLiBZb3UgY2FuIGFsc28gYXJyYW5nZSB0aGUgY29sdW1ucyBpbnRvIGEgYmV0dGVyIG9yZGVyLg0KDQpJbiB0aGUgZXhhbXBsZSBiZWxvdywgd2UgbG9nIGluIHRvIGFuIG9sZGVyIGRlbW8gaW5zdGFuY2UgKDIuMjkpIHdoaWNoIGhhcyBtb3JlIGRhdGEgZm9yIHRoZSAiSW5wYXRpZW50IE1vcmJpZGl0eSBNb3J0YWxpdHkiIHByb2dyYW0uIFRoZXNlIGRhdGEgYXJlIGNyb3NzLXNlY3Rpb25hbCwgc28gd2Ugb25seSBwdWxsIGZyb20gb25lIGV2ZW50IHJlcG9ydC4gQnV0IGl0IHdvdWxkIGFsc28gYmUgcG9zc2libGUgdG8gam9pbiBldmVudCByZXBvcnRzIGZyb20gbXVsdGlwbGUgc3RhZ2VzIGJ5IHRoZSBlbnJvbGxtZW50LCB3aGVyZSBldmVyeSByb3cgaXMgdGhlIHNhbWUgZGF0YSBwb2ludCBmcm9tIGEgc3RhZ2UuDQoNCg0KYGBge3IgbGlua2VkIGRhdGF0YWJsZSBhbmQgcGFyYWxsZWwgY29vcmRzLCB3YXJuaW5nPUZBTFNFfQ0KYmFzZXVybDwtImh0dHBzOi8vcGxheS5kaGlzMi5vcmcvMi4yOS8iDQp1c2VybmFtZTwtImFkbWluIg0KcGFzc3dvcmQ8LSJkaXN0cmljdCINCg0KbG9naW5ESElTMihiYXNldXJsLCB1c2VybmFtZSkNCg0KDQp1cmw8LXBhc3RlMChiYXNldXJsLCJhcGkvMjkvYW5hbHl0aWNzL2V2ZW50cy9xdWVyeS9lQkF5ZUd2MGV4Yy5jc3Y/ZGltZW5zaW9uPXBlOjIwMTgxMjsyMDE4MTEmZGltZW5zaW9uPW91Okltc3BUUVB3Q3FkJmRpbWVuc2lvbj14N1BhSEd2Z1dZMiZkaW1lbnNpb249dlY5VVdBWm9oU2YmZGltZW5zaW9uPUdpZVZrVHhwNEhIJmRpbWVuc2lvbj1xcnVyOUR2bnl0NSZkaW1lbnNpb249YjhoZDMzZFdqUjYmZGltZW5zaW9uPW9aZzMza2Q5dGF3JnN0YWdlPVpqN1VuQ0F1bEVrJmRpc3BsYXlQcm9wZXJ0eT1OQU1FJnRhYmxlTGF5b3V0PXRydWUmY29sdW1ucz1wZTtvdTt4N1BhSEd2Z1dZMjt2VjlVV0Fab2hTZjtHaWVWa1R4cDRISDtxcnVyOUR2bnl0NTtiOGhkMzNkV2pSNjtvWmczM2tkOXRhdyZyb3dzPXBlO291O3g3UGFIR3ZnV1kyO3ZWOVVXQVpvaFNmO0dpZVZrVHhwNEhIO3FydXI5RHZueXQ1O2I4aGQzM2RXalI2O29aZzMza2Q5dGF3IikgDQojbm93IHdlIEdFVCBjb21tYW5kIHRvIGZldGNoIHRoZSBkYXRhLCBhbmQgdGhlbiByZWFkIHRoZSBjb250ZW50IGludG8gYSBkYXRhZnJhbWUNCnJlcG9ydDIgPC1yZWFkX2Nzdihjb250ZW50KEdFVCh1cmwpKSkNCg0KY29sZW5ndGg8LWxlbmd0aChyZXBvcnQyKQ0KDQojaW5kZXggdGhlIG9ic2VydmF0aW9ucyBieSBldmVudCBkYXRlLCBvbmx5IGRpc3BsYXkgZGF0YSBjb2x1bW5zIChsYXN0IDUpDQpyMiA8LSByZXBvcnQyICU+JQ0KIGFycmFuZ2UoYEV2ZW50IGRhdGVgKSAlPiUNCiBzZWxlY3QoKGNvbGVuZ3RoLTUpOmNvbGVuZ3RoKQ0KDQoNCiNwYXJhbGxlbCBjb29yZGluYXRlcyB3aXRoIGNvbG9yIGJhc2VkIG9uIGdlbmRlcg0KcGFyY29vcmRzOjpwYXJjb29yZHMoZGF0YSA9IHIyLA0KICBjb2xvciA9IGxpc3QoDQogIyBkaXNjcmV0ZSBvciBjYXRlZ29yaWNhbCBjb2x1bW4NCiBjb2xvclNjYWxlID0gInNjYWxlT3JkaW5hbCIsDQogY29sb3JCeSA9ICJHZW5kZXIiLA0KIGNvbG9yU2NoZW1lID0gInNjaGVtZUNhdGVnb3J5MTAiKSwNCiB3aXRoRDMgPSBUUlVFLA0KICAgYnJ1c2hNb2RlID0gJzFELWF4ZXMnLA0KICAgYWxwaGFPbkJydXNoZWQgPSAwLjIsDQogICBxdWV1ZSA9IFRSVUUsDQogICByYXRlID0gNTAsDQogICByZW9yZGVyYWJsZSA9IFRSVUUpDQoNCmBgYA0KDQoNCg0KSXQgbG9va3MgbGlrZSBUcmFpbmluZ2xhbmQgaGFzIHNvbWUgdmVyeSBmYXQgYmFiaWVzLCBhbmQgc29tZSBncmF2aXR5LWRlZnlpbmcgc2VuaW9ycyEgQnV0IHdpdGggc28gbXVjaCBkYXRhIGl0IGNhbiBiZSBoYXJkIHRvIHJlYWQgY29ycmVsYXRpb25zIGluIHRoaXMgcGxvdC4gQW5kIG9uY2Ugd2UgaWRlbnRpZnkgdGhlc2Ugb3V0bGllcnMsIHdlIHdpbGwgbmVlZCB0byBpbnZlc3RpZ2F0ZSB0aGVpciBjYXNlcy4uLg0KDQpMZXQncyBmaWx0ZXIgaXQgZG93biB0byBmZXdlciBjYXNlcywgYW5kIGxpbmsgdGhlIHBsb3QgdG8gYSBkYXRhIHRhYmxlLg0KDQpUaGVzZSB2aXN1YWxzIGFyZSBiZXR0ZXIgZGlzcGxheWVkIHNpZGUtYnktc2lkZSBpbiB0aGUgY29tcGxlbWVudGFyeSBbZmxleGRhc2hib2FyZCBleGFtcGxlLl0oaHR0cHM6Ly9pYW1ib2RvLmdpdGh1Yi5pby9wcm9qZWN0cy9kZW1vMi5odG1sKQ0KDQoNCmBgYHtyIGxpbmsgd2l0aCBkYXRhIHRhYmxlLCB3YXJuaW5nPUZBTFNFLCBldmFsPVRSVUV9DQoNCiNpbmNsdWRlIG9yZ2FuaXphdGlvbiB1bml0IGluIG91ciBvdXRwdXQNCnIyIDwtIHJlcG9ydDIgJT4lDQogYXJyYW5nZShgRXZlbnQgZGF0ZWApICU+JQ0KIHNlbGVjdCgoY29sZW5ndGgtNSk6Y29sZW5ndGgpDQoNCg0KI3RvIGltcHJvdmUgcGVyZm9ybWFuY2UsIHdlJ2xsIG9ueSBzZWxlY3QgdGhlIG1vc3QgcmVjZW50IDEwMDAgb2JzZXJ2YXRpb25zDQpuZXdfcjI8LWhlYWQocjIsIDEwMDApDQoNCnNkMiA8LSBjcm9zc3RhbGs6OlNoYXJlZERhdGEkbmV3KG5ld19yMikNCg0KcGMgPC0gcGFyY29vcmRzKGRhdGEgPSBzZDIsDQogIGJydXNoTW9kZSA9ICcxZCcsDQogIGFscGhhT25CcnVzaGVkID0gMC4yLA0KICBxdWV1ZSA9IFRSVUUsDQogIHJhdGUgPSA1MCwNCiAgcmVvcmRlcmFibGUgPSBUUlVFKQ0KDQoNCg0KYnNjb2xzKHdpZHRocz1jKDEyKSwNCiAgICAgICAgICAgIHBjLA0KICAgICAgICAgICAgbWFrZURUKHNkMikpDQoNCg0KYGBgDQoNCg0KIyBSIFRvb2xzIGZvciBNZXRhZGF0YSBJbXBvcnQvRXhwb3J0IFN1cHBvcnQNCg0KV2UgY2FuIGFsc28gdXNlIFIgdG8gcGVyZm9ybSBtZXRhZGF0YSB1cGRhdGUgb3BlcmF0aW9ucy4gU29tZSBzaW1wbGUgdG9vbHMgaW4gUiBNYXJrZG93biBjYW4gaGVscCB1cyB1bmRlcnN0YW5kIHdoYXQgZGF0YSB3ZSBhcmUgYWN0dWFsbHkgYmVpbmcgdXBkYXRlZC4NCg0KSGVyZSBpcyBhIGJhc2ljIGV4YW1wbGU6IHVwZGF0aW5nIHlvdXIgdXNlciBuYW1lIQ0KDQpgYGB7ciBkaWZmZGVtbywgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD00fQ0KbGlicmFyeShkaWZmcikNCg0KdXJsPC1wYXN0ZTAoYmFzZXVybCwgImFwaS9tZSIpDQptZTwtZnJvbUpTT04oY29udGVudChHRVQodXJsKSwidGV4dCIpKQ0KDQojd2hhdCBpcyB0aGlzIGRhdGE/DQpsaXN0dmlld2VyOjpqc29uZWRpdChtZSkNCg0KI2hleSwgdGhhdHMgbm90IG1lIQ0KbmV3X21lPC1tZQ0KbmV3X21lJG5hbWU8LSJCcmlhbiBPJ0Rvbm5lbGwiDQojbm90ZSBKU09OIGRhdGEgaXMgcmVwcmVzZW50ZWQgYXMgbmVzdGVkIGxpc3RzIGluIFIhDQoNCndyaXRlX2pzb24obWUsICJtZS5qc29uIiwgYXV0b191bmJveD1UUlVFLCBwcmV0dHk9VFJVRSkNCndyaXRlX2pzb24obmV3X21lLCAibmV3X21lLmpzb24iLCBhdXRvX3VuYm94PVRSVUUsIHByZXR0eT1UUlVFKQ0KDQojc2VlIHRoZSBkaWZmZXJlbmNlDQpkaWZmcjo6ZGlmZnIoIm1lLmpzb24iLCAibmV3X21lLmpzb24iKQ0KYGBgDQoNCmBgYHtyIGVjaG89VFJVRSwgZmlnLmhlaWdodD0zfQ0KdXJsPC1wYXN0ZTAoYmFzZXVybCwgImFwaS91c2Vycy8iLG1lJGlkLCIuanNvbiIpDQpyPC1odHRyOjpQVVQodXJsPXVybCwgYm9keT11cGxvYWRfZmlsZSgibmV3X21lLmpzb24iKSwgY29udGVudF90eXBlX2pzb24oKSkNCg0KI2hvdyBkaWQgdGhlIGltcG9ydCB3b3JrIG91dD8NCmxpc3R2aWV3ZXI6Ompzb25lZGl0KGNvbnRlbnQocikpDQoNCmBgYA0KDQoNCiMgU3VtbWFyeQ0KVGhpcyBkZW1vIHNob3dlZCB1c2UgY2FzZXMgd2hlcmUgZGF0YSBjYW4gYmUgbWVhbmluZ2Z1bGx5IGV4dHJhY3RlZCBmcm9tIHRoZSBESElTMiBBUEkgaW50byBSLCBwcm92aWRpbmcgZXhhbXBsZXMgb2YgaG93IHlvdSBtaWdodCBhdXRvbWF0ZSBleGlzdGluZyByZXBvcnRpbmcgc3lzdGVtcywgb3IgaW1wcm92ZSB0aGUgYW5hbHlzaXMvdmlzdWFsaXphdGlvbiBvZiBleGlzdGluZyBUcmFja2VyIGRhdGEuDQoNCk5vdGUgdGhlcmUgYXJlIG90aGVyIHdheXMgeW91IGNhbiB1c2UgREhJUzIgKyBSTWFya2Rvd24gdG8gZG8gc29tZSB0aGluZ3MgREhJUzIgY2Fubm90IGN1cnJlbnRseSBkbywgbGlrZToNCg0KKiBBbmFseXplIHVzZXIgYWNjZXNzIGFuZCBhdWRpdCBsb2dzDQoqIFZpc3VhbGl6ZSByZWxhdGlvbnNoaXAgbmV0d29ya3MgYmV0d2VlbiB0cmFja2VyIGluc3RhbmNlcw0KKiBPcmdhbml6ZSBmYXZvcml0ZXMgYnkgbnVtYmVyIG9mIHZpZXdzDQoqIENoZWNrIG1ldGFkYXRhIGxpa2UgaW5kaWNhdG9ycyBvciBkYXRhIGVsZW1lbnRzIGZvciBpbmNvbnNpc3RlbmNpZXMNCg0KQnV0IG5vIG9uZSBsYW5ndWFnZSBvciB3b3JrZmxvdyBjYW4gc29sdmUgYWxsIHBvc3NpYmxlIGRhdGEgZXh0cmFjdGlvbiBhbmQgbXVuZ2luZyBwcm9ibGVtcy4gQW5kIGV2ZW50dWFsbHksIHRoZSBtZXRob2RzIGRlc2NyaWJlZCBhYm92ZSBbd2lsbCBhbGwgbmVlZCB0byBiZSByZXByb2R1Y2VkIGFuZCB1cGRhdGVkXShodHRwczovL2pvemVmLmlvL3I5MTAtcm1hcmtkb3duLXJlcHJvZHVjaWJpbGl0eS8pIHdpdGggc29tZXRoaW5nIGV2ZW4gYmV0dGVyLi4uIA0KDQpgYGB7ciB4a2NkLCBlY2hvPUZBTFNFLG91dC53aWR0aD0iMTAwJSIsIG91dC5oZWlnaHQ9IjYwJSIsZmlnLmNhcD0iaHR0cHM6Ly93d3cueGtjZC5jb20vMjA1NC8iLGZpZy5zaG93PSdob2xkJyxmaWcuYWxpZ249J2NlbnRlcid9DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2ltZy5kZXZyYW50LmNvbS9kZXZyYW50L3JhbnQvcl8xODEyNTE4X1NRZ2s1LmpwZyIpDQoNCmBgYCANCg0KDQpTbyBJIGhvcGUgdGhpcyB0dXRvcmlhbCBoZWxwcyBtb3JlIERISVMyIHVzZXJzIGZpbmQgYSBuZXcsIHN1c3RhaW5hYmxlIHdheSB0byB3b3JrIHdpdGggdGhlaXIgZGF0YSBpbiBSLg0KDQoNCmBgYHtyIGZvb3RlciwgZWNobz1GQUxTRSxvdXQud2lkdGg9IjQ5JSIsIG91dC5oZWlnaHQ9IjIwJSIsZmlnLmNhcD0iSW5zdGl0dXRpb25hbCBhZmZpbGlhdGlvbnMiLGZpZy5zaG93PSdob2xkJyxmaWcuYWxpZ249J2NlbnRlcid9DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGMoImh0dHBzOi8vZmhpLm5vL2NvbnRlbnRhc3NldHMvYzAzYWE3NmZlMzA5NDFmMDg0NzRlZTgyM2JhMmE5MjgvbG9nb19rb3J0LnBuZyIsImh0dHA6Ly9lcmVnaXN0cmllcy5vcmcvd3AtY29udGVudC91cGxvYWRzLzIwMTcvMDIvZVJlZ2lzdHJpZXMtTmV3LUxvZ28ucG5nIikpDQoNCmBgYCANCg0KQWRkaXRpb25hbCBSTWFya2Rvd24gc3R5bGVzIGFuZCBvcHRpb25zIGF2YWlsYWJsZSBhdCB0aGUgW1JNYXJrZG93biB3ZWJzaXRlXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKS4gTW9yZSBvcHRpb25zIGZvciBpbnRlcmFjdGl2ZSBIVE1MIHdpZGdldHMgYXJlIFthdmFpbGFibGUgaGVyZV0oaHR0cDovL2dhbGxlcnkuaHRtbHdpZGdldHMub3JnLykuDQoNClRlbXBsYXRlIGRvY3VtZW50IHByb2R1Y2VkIGJ5IEJyaWFuIE8nRG9ubmVsbCBmb3IgdGhlIGVSZWdpc3RyaWVzIEluaXRpYXRpdmUgYXQgdGhlIFtOb3J3ZWdpYW4gSW5zdGl0dXRlIG9mIFB1YmxpYyBIZWFsdGguXShodHRwczovL3d3dy5maGkubm8va2svaW50ZXJuYXNqb25hbHQvZXJlZ2lzdHJpZXMvKSANCg0KW1Zpc2l0IGVSZWdpc3RyaWVzIG9uIHRoZSBESElTMiBDb21tdW5pdHkgb2YgUHJhY3RpY2UuXShodHRwczovL2NvbW11bml0eS5kaGlzMi5vcmcvYy9zdWJjb21tdW5pdGllcy9lcmVnaXN0cmllcykNCg0K