Using R in hydrology (EGU2017 short course)

Instructors: Shaun Harrigan, Katie Smith, Berry Boessenkool and Daniel Klotz
Organizer: Berry Boessenkool
Contact: Questions and feedback are welcome via berry-b@gmx.de
Last updated: 2017-04-24

Please let us know your current R knowledge level by filling out the short survey at
bit.ly/knowR

These slides and all other course materials can be found at
github.com/brry/rhydro
Download the github course repository with all the materials including the datasets and presentation source code.
This is an R Markdown Notebook.
For discussions, please visit the Hydrology in R Facebook group.
Before running the code blocks below, we suggest to get package installation instructions by running:

source("https://raw.githubusercontent.com/brry/rhydro/master/checkpc.R")


Aim and contents of this workshop

We want to:

We can not:

We have prepared:


top

Report

Good coding practice, report generation (Rstudio, rmarkdown, R notebook) Daniel Klotz BOKU Vienna (daniel.klotz@boku.ac.at)

Why to use R

Why I did not use R:

Whats great about R:

  library(ggplot2)
  test_data <- mpg
  test_plot <- ggplot(test_data, aes(displ, hwy, colour = class)) +
    geom_point()
  test_plot

Why I decided to use R:

pipe

pipe

Previously:

  aggregation_function <- function(x) {
    round(mean(x),2)
  }
  mtcars_subset <- subset(mtcars,hp > 100)
  mtcars_aggregated <- aggregate(. ~ cyl, data = mtcars_subset, FUN = aggregation_function)
  car_data1 <- transform(mtcars_aggregated, kpl = mpg*0.4251)
  print(car_data1)

Now:

library(magrittr)
car_data2 <-
  mtcars %>%
  subset(hp > 100) %>%
  aggregate(. ~ cyl, data = ., FUN = . %>% mean %>% round(2)) %>%
  transform(kpl = mpg %>% multiply_by(0.4251)) %>%
  print

btw: You can integrate other programming languages with ease. Here an example from Yihui Xie for the use of Fortran in Rmarkdown:

  1. Compile Code:
```r
C Fortran test
      subroutine fexp(n, x)
      double precision x
C  output
      integer n, i
C  input value
      do 10 i=1,n
         x=dexp(dcos(dsin(dble(float(i)))))
  10  continue
      return
      end
```
  1. Run Code:
```r
res = .Fortran("fexp", n=100000L, x=0)
str(res)
```

Be happy with the result: > ## List of 2 > ## $ n: int 100000 > ## $ x: num 2.72


Markdown

HTML: HyperText Markdown Language

John Gruber:

John Gruber

Comparison: Markdown vs. Latex Comparison

Rstudio provides cheat-sheets with the most important informations about many of their “favorite” packages & software:

Cheat Sheet

** Rmarkdown **

In Rstudio:

Rmark1

Rmark2

Rmark2

“Native” Formats:

Much more possible if you adress pandoc directly: pandoc

Information in the text can be automatically updated with the rest of the document: [time for coffee

Examples

Small Websites

Cayman Theme

Cayman Theme

Books (blogdown)

bookdown1 bookdown2

Blogs (hugo)

hugo

Presentations

bookdown1

Widgets

cran-gauge

Apps (Shiny)

shiny shiny2 shiny2

shiny3 R based hydrology tools from the Intelligent Water Decisions Research Group of the University of Adelaide:


top

GIS

Using R as GIS (reading a rainfall shapefile + Kriging, sf + leaflet + mapview + OSMscale) Berry Boessenkool Potsdam University, Germany (berry-b@gmx.de)

Shapefiles

Reading shapefiles with maptools::readShapeSpatial and rgdal::readOGR is obsolete. Instead, use sf::st_read. sf is on CRAN since oct 2016. Main reaction when using sf: “Wow, that is fast!” Download the shapefile or better: download the whole github course repository

rain <- sf::st_read("data/PrecBrandenburg/niederschlag.shp")
Reading layer `niederschlag' from data source `/home/berry/Dropbox/R/rhydro/presentations/data/PrecBrandenburg/niederschlag.shp' using driver `ESRI Shapefile'
converted into: POLYGON
Simple feature collection with 277 features and 1 field
geometry type:  POLYGON
dimension:      XY
bbox:           xmin: 3250175 ymin: 5690642 xmax: 3483631 ymax: 5932731
epsg (SRID):    NA
proj4string:    +proj=tmerc +lat_0=0 +lon_0=15 +k=0.9996 +x_0=3500000 +y_0=0 +ellps=GRS80 +units=m +no_defs

Central points of rainfall Thiessen polygons

centroids <- sf::st_centroid(rain)
centroids <- sf::st_coordinates(centroids)
centroids <- as.data.frame(centroids)

top

Plotting, maps

Static plot:

plot(rain[,1])

Static map:

prj <- sf::st_crs(rain)$proj4string
#cent_ll <- OSMscale::projectPoints(Y,X, data=centroids, to=OSMscale::pll(), from=prj)
#map_static <- OSMscale::pointsMap(y,x, cent_ll, fx=0.08, type="maptoolkit-topo", zoom=6)
#save(map_static, file="data/map_static.Rdata")
#load("data/map_static.Rdata")
#OSMscale::pointsMap(y,x, cent_ll, map=map_static)

Interactive map:

library(leaflet)
cent_ll$info <- paste0(sample(letters,nrow(cent_ll),TRUE), ", ", round(cent_ll$x,2),
                       ", ", round(cent_ll$y,2))
leaflet(cent_ll) %>% addTiles() %>% addCircleMarkers(lng=~x, lat=~y, popup=~info)

Looks similar in style to rdwd interactive map

Interactive map of shapefile:

# make sure to have installed the development version of mapview:
# devtools::install_github("environmentalinformatics-marburg/mapview", ref = "develop")
library(berryFunctions) # classify, seqPal
col <- seqPal(n=100, colors=c("red","yellow","blue"))[classify(rain$P1)$index]
mapview::mapview(rain, col.regions=col)

top

Kriging

Plot original points colored by third dimension:

pcol <- colorRampPalette(c("red","yellow","blue"))(50)
x <- centroids$X # use cent_ll$x for projected data
y <- centroids$Y
berryFunctions::colPoints(x, y, rain$P1, add=FALSE, col=pcol, y1=0.8)

Calculate the variogram and fit a semivariance curve

library(geoR)
geoprec <- as.geodata(cbind(x,y,rain$P1))
vario <- variog(geoprec, max.dist=130000) # other maxdist for lat-lon data
variog: computing omnidirectional variogram
fit <- variofit(vario)
variofit: covariance model used is matern 
variofit: weights used: npairs 
variofit: minimisation function used: optim 
initial values not provided - running the default search
variofit: searching for best initial value ... selected values:
              sigmasq   phi        tausq kappa
initial.value "1325.81" "19999.05" "0"   "0.5"
status        "est"     "est"      "est" "fix"
loss value: 104819060.429841 
plot(vario)
lines(fit)

Determine a useful resolution (keep in mind that computing time rises exponentially with grid size)

# distance to closest other point:
d <- sapply(1:length(x), function(i)
            min(berryFunctions::distance(x[i], y[i], x[-i], y[-i])) )
# for lat-long data use (2017-Apr only available in github version of OSMscale)
# d <- OSMscale::maxEarthDist(y,x, data=cent_ll, fun=min)
hist(d/1000, breaks=20, main="distance to closest gauge [km]")
mean(d/1000) # 8 km

Perform kriging on a grid with that resolution

res <- 1000 # 1 km, since stations are 8 km apart on average
grid <- expand.grid(seq(min(x),max(x),res),
                    seq(min(y),max(y),res))
krico <- krige.control(type.krige="OK", obj.model=fit)
#krobj <- krige.conv(geoprec, loc=grid, krige=krico)
#save(krobj, file="data/krobj.Rdata")
load("data/krobj.Rdata") # line above is too slow for recreation each time

Plot the interpolated values with image or an equivalent function (see Rclick 4.15) and add contour lines.

par(mar=c(0,3,0,3))
geoR:::image.kriging(krobj, col=pcol)
colPoints(x, y, rain$P1, col=pcol, legargs=list(horiz=F, title="Prec",y1=0.1,x1=0.9))
points(x,y)
plot(rain, col=NA, add=TRUE)


top

Discharge

River discharge time-series visualisation and extreme value statistics (animation + extremeStat)
Berry Boessenkool

Read, plot and aggregate data

Datasets from the UK National River Flow Archive http://nrfa.ceh.ac.uk/data/station/meanflow/39072
Download discharge csv or better: download the whole github course repository

Read and transform data

Q <- read.table("data/discharge39072.csv", skip=19, header=TRUE, sep=",", fill=TRUE)[,1:2]
colnames(Q) <- c("date","discharge")
Q$date <- as.Date(Q$date, format="%Y-%m-%d")

Examine data

head(Q)
str(Q)
'data.frame':   13222 obs. of  2 variables:
 $ date     : Date, format: "1979-07-20" ...
 $ discharge: num  33.4 32.5 33.1 30.6 30.1 ...

Simple time series plot

plot(Q, type="l", col="blue")

Publication-ready graphics

png("DischargeVis.png", width=20, height=10, units="cm", res=500)
#pdf("DischargeVis.pdf", width=20/2.5, height=10/2.5) # vector graph
par(mar=c(3.5,3.5,2.5,0.2), mgp=c(2.3,0.7,0), las=1)
plot(Q, type="l", col="blue", main="NRFA: Thames\nRoyal Windsor Park",
     xlab="Date", ylab="Discharge  [m\U{00B3}/s]")
dev.off()
null device 
          1 

Annual maxima, German hydrological year split at Oct 31

Q$hydyear <- as.numeric(format(Q$date+61, "%Y"))
annmax <- tapply(Q$discharge, Q$hydyear, max, na.rm=TRUE)
annmax <- annmax[-1]
hydyear <- as.numeric(names(annmax)) 
plot(hydyear, annmax, type="o", las=1)

Extreme value statistics

library(extremeStat)
dlf <- distLfit(annmax)
Note in distLfit: dat was not a vector.
distLfit execution took 0.1 seconds.
plotLfit(dlf)

plotLfit(dlf, cdf=TRUE)

dle <- distLextreme(dlf=dlf, RPs=c(5,10,50,100), gpd=FALSE)
plotLextreme(dle)

Logarithmic plot with many fitted distribution functions

plotLextreme(dle, nbest=16, log=TRUE)

Return values (discharge estimates for given return periods)

dle$returnlev

In reality, please use non-stationary EVS!

Uncertainty band for Wakeby distribution fit estimate

dle_boot <- distLexBoot(dle, n=10, nbest=1)

   |+++++                                             | 10% ~00s          
   |++++++++++                                        | 20% ~00s          
   |+++++++++++++++                                   | 30% ~00s          
   |++++++++++++++++++++                              | 40% ~00s          
   |+++++++++++++++++++++++++                         | 50% ~00s          
   |++++++++++++++++++++++++++++++                    | 60% ~00s          
   |+++++++++++++++++++++++++++++++++++               | 70% ~00s          
   |++++++++++++++++++++++++++++++++++++++++         | 80% ~00s          
   |+++++++++++++++++++++++++++++++++++++++++++++    | 90% ~00s          
   |++++++++++++++++++++++++++++++++++++++++++++++++++| 100% elapsed = 01s
plotLexBoot(dle_boot, distcol="green")

More details in the package vignette

vignette("extremeStat")

Animated movie

Download data from several discharge stations

http://nrfa.ceh.ac.uk/data/search Filter: River = Thames
http://environment.data.gov.uk/flood-monitoring/doc/reference#stations for lat-lon coordinates:

url <- "http://environment.data.gov.uk/flood-monitoring/id/stations?riverName=Thames"
json <- jsonlite::fromJSON(url)
str(json$items, max.level=1)
metajson <- json$items[,c("label","lat","long","dateOpened")]
meta <- read.table(header=T, as.is=T, sep=",", text="
name                , lat, lon
Kingston            , 51.415005,-0.308869
Days_Weir           , 51.638206,-1.179444
Eynsham             , 51.774692,-1.356854
West_Mill_Cricklade , 51.646694,-1.865536
Ewen                , 51.674733,-1.9904
Royal_Windsor_Park  , 51.485795,-0.589124
Reading             , 51.461325,-0.967884
")
#library(leaflet)
#leaflet(meta) %>% addTiles() %>% addCircleMarkers(~lon, ~lat, popup=~name)
#map_thames <- OSMscale::pointsMap(lat,lon,meta, fx=1, fy=0.2, plot=FALSE, zoom=6, 
#                           type="maptoolkit-topo", quiet=TRUE)

Read datasets

files <- dir("data", pattern="^Thames_", full=TRUE)
thames <- lapply(files, function(f) {
 dis <- read.table(f, skip=19, header=TRUE, sep=",", fill=TRUE)[,1:2]
 name <- readLines(f, n=5)[5]
 name <- sub("station,name,Thames at ", "", name)
 name <- gsub(" ", "_", name)
 colnames(dis) <- c("date",name)
 dis$date <- as.Date(dis$date, format="%Y-%m-%d")
 dis
})
rm(files)

Merge datasets

dis <- Reduce(function(...) merge(..., all=T), thames)

Code to generate one movie slice

library(berryFunctions) # for lim0, monthAxis, textField, etc

scene <- function(i, vlcnote=TRUE, map=TRUE, cex=1.2)
{
 sel <- 0:120
 dis2 <- dis[i + sel, ]
 stat <- colnames(dis)[-1]
 col <- RColorBrewer::brewer.pal(ncol(dis)-1, name="Set1")
 names(col) <- stat
 plot(dis2$date,dis2[,2], type="n", ylim=lim0(300),  las=1, 
      xaxt="n", yaxt="n", cex.lab=cex, xlab="", 
      ylab="Discharge  [m\U{00B3}/s]", xaxs="i")
 axis(2, cex.axis=cex, las=1)
 Sys.setlocale("LC_TIME", "C")
 monthAxis(midmonth=TRUE, format="%b\n%Y", cex=cex, mgp=c(3,3.5,0))
 abline(h=1:6*100, v=dis2$date[format(dis2$date,"%d")=="01"], col=8)
 for(s in stat) lines(dis2$date, dis2[,s], lwd=4, col=col[s])
 xi <- seqR(sel,len=length(stat)+2)[-1]
 xi <- head(xi, -1)
 textField(dis2$date[xi], diag(as.matrix(dis2[xi,stat])), stat, cex=cex, col=col)
 box()
 if(map) berryFunctions::smallPlot(
    {
    OpenStreetMap::plot.OpenStreetMap(map_thames, removeMargin=FALSE)
    pts <- OSMscale::projectPoints(lat,lon,meta, 
           to=map_thames$tiles[[1]]$projection)
   points(pts, pch=3, cex=4, lwd=5, col=col)
   },
   mar=0, bg=NA, border=NA, x1=0, x2=0.5, y1=0.8, y2=1)
 if(vlcnote) mtext("VLC: 'e': single frame forward\n'SHIFT+LEFT': few seconds back",
                   side=3, line=-9, outer=TRUE, adj=0.95, cex=cex)
}

par(mar=c(5,5,0.5,0.5), mgp=c(3,0.7,0))
scene(47200)

Actual movie

library(animation) ; library(pbapply)
saveVideo({par(mar=c(6,8,1,1), mgp=c(5.5,0.7,0))
 dummy <- pbsapply(seq(47000, by=3, len=100), scene, cex=2); rm(dummy)
}, video.name="Qmovie.mp4", interval=0.1, ffmpeg="/usr/bin/ffmpeg", 
ani.width=7*200, ani.height=5*200)

Open video within browser


top

Hydmod

Hydrological modelling with airGR
Katie Smith Centre for Ecology & Hydrology (k.a.smith@ceh.ac.uk)

This is an demonstration of how to use the airGR package of hydrological models in R, as well as how to plot interactive timeseries graphs with the dygraphs package.

First we need to load some packages

library(xts)
library(dygraphs)
library(RColorBrewer)

Data

Now we’ll load in some observational flow data from the River Thames (naturalised) in England - with thanks to the National River Flow Archive: http://nrfa.ceh.ac.uk/data/search

observed_data <- read.csv("data/Qobs_390010.csv")
head(observed_data)
observed_data$DATE <- strptime(observed_data$DATE, format = "%d/%m/%Y")

In order to plot this as an interactive dygraph we need to change it to xts format

observed_data_xts <- as.xts(observed_data$Qobs, order.by = observed_data$DATE)
dygraph(observed_data_xts, main="Naturalised Streamflow Observations for the Thames at Kingston")%>%
  dyAxis("y", label="Streamflow (m3/s")%>%
  dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1")[2])%>%
  dyRangeSelector()

Now lets read in some precipitation data - this is from CEH-GEAR: https://data.gov.uk/dataset/gridded-estimates-of-daily-and-monthly-areal-rainfall-for-the-united-kingdom-1890-2012-ceh-gear

precip_data <- read.csv("data/rain_1961_2014_390010.csv")
head(precip_data)

and some potential evapotranspiration data - this is from CHESS-PE:https://data.gov.uk/dataset/climate-hydrology-and-ecology-research-support-system-potential-evapotranspiration-dataset-for-1

PET_data <- read.csv("data/CHESS_PET_1961_2015_390010.csv")
head(PET_data)

Note that our starting dates are not the same as our observational data, so we need to make a dataframe that matches the dates up. There are a lot of ways to do this. First we’ll convert them to the same date format.

precip_data$DATE <- strptime(precip_data$DATE, "%Y-%m-%d")
PET_data$DATE <- strptime(PET_data$DATE, "%Y-%m-%d")

now we’ll find the common period

first_date <- max(observed_data$DATE[1], precip_data$DATE[1], PET_data$DATE[1])
last_date <- min(observed_data$DATE[length(observed_data$DATE)], precip_data$DATE[length(precip_data$DATE)], PET_data$DATE[length(PET_data$DATE)])

and make a data frame of that length

# make an empty data frame
thames_data <- as.data.frame(matrix(NA,nrow=as.numeric((last_date-first_date)+1), ncol=4))
colnames(thames_data) <-c ("date","PET","precip","obs")
# make the date timeseries
thames_data$date <- seq(first_date, last_date, by="days")
# populate the data frame with the data
thames_data$obs <- observed_data$Qobs[which(observed_data$DATE==thames_data$date[1]):which(observed_data$DATE==thames_data$date[length(thames_data$date)])]
thames_data$precip <- precip_data$Mean_rainfall[which(precip_data$DATE==thames_data$date[1]):which(precip_data$DATE==thames_data$date[length(thames_data$date)])]
thames_data$PET <- PET_data$PET[which(PET_data$DATE==thames_data$date[1]):which(PET_data$DATE==thames_data$date[length(thames_data$date)])]

Interactive time series plot

plot the observed streamflow with the precipitation data

#  convert the observed discharge to runoff (so its in the same units as the precip)
# divide by catchment area (m2) and mulitply by 86.4
thames_data$obs <- (thames_data$obs/9948.0)*86.4
thames_data_xts <- as.xts(thames_data[,3:4], order.by=thames_data$date)
# initiate the dygraph
dygraph(thames_data_xts)%>%
# define the first axis  
dyAxis("obs", name = "y", label = "runoff (mm/day)",
       valueRange = range(thames_data_xts[, "obs"],
                          na.rm = TRUE)* c(0.01, 1.59))%>%
# define the second axis
dyAxis("precip", name = "y2", label = "precip (mm/day)",
                   valueRange = rev(range(thames_data_xts[, "precip"], 
                   na.rm = TRUE)* c(0.01, 2.99)))%>%
# plot the data
dySeries("obs",axis = 'y')%>%
dySeries("precip", axis = 'y2', stepPlot = TRUE,
         fillGraph = TRUE)%>%
dyOptions(colors = RColorBrewer::brewer.pal(3, "Set1")[2:3])%>%
dyRangeSelector()

Hydrological modeling

OK, enough messing with data, lets do some modelling. We are using the GR4J model, from the airGR package. This was developed by IRSTEA, France. See Perrin et al. (2003)

See this website for a good guide through the model: https://hydrogr.github.io/airGR/page_1_get_started.html

load the GR package

require(airGR, quietly=TRUE)

prepare the input data in the correct format

BasinObs <- thames_data
colnames(BasinObs) <- c('DatesR','E','P', 'Qobs')

create the InputsModel object - this defines which model we want to run, and defines the variables for the models input data

InputsModel <- CreateInputsModel(FUN_MOD = RunModel_GR4J,DatesR = BasinObs$DatesR,
                                   Precip = BasinObs$P,PotEvap = BasinObs$E)
str(InputsModel)
List of 3
 $ DatesR : POSIXlt[1:19723], format: "1961-01-01" ...
 $ Precip : num [1:19723] 8.6376 4.571 2.1015 0.0201 7.1859 ...
 $ PotEvap: num [1:19723] 0.367 0.353 0.412 0.32 0.612 ...
 - attr(*, "class")= chr [1:3] "InputsModel" "daily" "GR"
# note NA values of precip and PET are NOT ALLOWED

create the RunOptions object - this defines options for the RunModel_GR4J function

# first define the period to run the model over
Ind_Run <- seq(which(BasinObs$DatesR=="1981-01-01"),
             which(BasinObs$DatesR=="2014-12-31"))
RunOptions <- CreateRunOptions(FUN_MOD = RunModel_GR4J,
                               InputsModel = InputsModel,
                               IndPeriod_Run = Ind_Run,
                               IndPeriod_WarmUp = NULL)
     Model warm-up period not defined -> default configuration used 
        The year preceding the run period is used 
     Model states initialisation not defined -> default configuration used 
str(RunOptions)
List of 6
 $ IndPeriod_WarmUp: int [1:365] 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 ...
 $ IndPeriod_Run   : int [1:12418] 7306 7307 7308 7309 7310 7311 7312 7313 7314 7315 ...
 $ IniStates       : num [1:67] 0 0 0 0 0 0 0 0 0 0 ...
 $ IniResLevels    : num [1:2] 0.3 0.5
 $ Outputs_Cal     : chr "Qsim"
 $ Outputs_Sim     : chr [1:16] "DatesR" "PotEvap" "Precip" "Prod" ...
 - attr(*, "class")= chr [1:3] "RunOptions" "GR" "daily"

create the InputsCrit object - define the error metric (choose from RMSE, NSE, KGE or modified KGE (KGE2))

InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_NSE,
                               InputsModel = InputsModel,
                               RunOptions = RunOptions,
                               Qobs = BasinObs$Qobs[Ind_Run])
str(InputsCrit)
List of 5
 $ BoolCrit  : logi [1:12418] TRUE TRUE TRUE TRUE TRUE TRUE ...
 $ Qobs      : num [1:12418] 0.635 0.623 0.587 0.571 0.566 ...
 $ transfo   : chr ""
 $ Ind_zeroes: NULL
 $ epsilon   : NULL
 - attr(*, "class")= chr "InputsCrit"

create the CalibOptions object - choose the calibration algorithm

CalibOptions <- CreateCalibOptions(FUN_MOD = RunModel_GR4J,
                                   FUN_CALIB = Calibration_Michel)
str(CalibOptions)
List of 3
 $ FixedParam       : logi [1:4] NA NA NA NA
 $ SearchRanges     : num [1:2, 1:4] 4.59e-05 2.18e+04 -1.09e+04 1.09e+04 4.59e-05 ...
 $ StartParamDistrib: num [1:3, 1:4] 169.017 247.151 432.681 -2.376 -0.649 ...
 - attr(*, "class")= chr [1:3] "CalibOptions" "GR4J" "HBAN"

run the calibration

OutputsCalib <- Calibration_Michel(InputsModel = InputsModel, 
                                   RunOptions = RunOptions,
                                   InputsCrit = InputsCrit,
                                   CalibOptions = CalibOptions,
                                   FUN_MOD = RunModel_GR4J,
                                   FUN_CRIT = ErrorCrit_NSE)
Grid-Screening in progress (0% 20% 40% 60% 80% 100%)
     Screening completed (81 runs)
         Param =  432.681 ,   -0.649 ,   83.096 ,    2.384
         Crit NSE[Q]       = 0.8923
Steepest-descent local search in progress
     Calibration completed (19 iterations, 216 runs)
         Param =  607.894 ,   -0.734 ,   87.357 ,    2.315
         Crit NSE[Q]       = 0.9246

NSE of 0.9246 - not bad at all!

define the parameters found by the calibration routine

Param <- OutputsCalib$ParamFinalR
Param
[1] 607.8936811  -0.7336304  87.3567230   2.3153153

RUN THE MODEL!

OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
                             RunOptions = RunOptions,
                             Param= Param)
str(OutputsModel)
List of 16
 $ DatesR  : POSIXlt[1:12418], format: "1981-01-01 00:00:00" ...
 $ PotEvap : num [1:12418] 1.347 0.38 0.795 0.57 0.454 ...
 $ Precip  : num [1:12418] 0.0972 0.1211 0.0523 0.1147 0.3248 ...
 $ Prod    : num [1:12418] 352 351 350 350 349 ...
 $ AE      : num [1:12418] 1.127 0.334 0.663 0.488 0.43 ...
 $ Perc    : num [1:12418] 0.387 0.383 0.378 0.374 0.372 ...
 $ PR      : num [1:12418] 0.387 0.383 0.378 0.374 0.372 ...
 $ Q9      : num [1:12418] 0.355 0.35 0.345 0.341 0.338 ...
 $ Q1      : num [1:12418] 0.0398 0.0393 0.0387 0.0382 0.0378 ...
 $ Rout    : num [1:12418] 42.2 42 41.7 41.4 41.2 ...
 $ Exch    : num [1:12418] -0.0592 -0.0577 -0.0564 -0.0551 -0.0539 ...
 $ AExch   : num [1:12418] -0.099 -0.097 -0.0951 -0.0933 -0.0917 ...
 $ QR      : num [1:12418] 0.599 0.578 0.559 0.542 0.526 ...
 $ QD      : num [1:12418] 0 0 0 0 0 ...
 $ Qsim    : num [1:12418] 0.599 0.578 0.559 0.542 0.526 ...
 $ StateEnd: num [1:67] 374.4 45.5 NA NA NA ...
 - attr(*, "class")= chr [1:3] "OutputsModel" "daily" "GR"

use the inbuilt plot function to look at the results

plot(OutputsModel, Qobs=BasinObs$Qobs[Ind_Run])

looking good - but we’ve got some discrepancy at the low flows end. NSE is notorious for this, it is based on the square of the flows, so over-weights the calibration to the high flows. I wonder if the modified KGE can do any better?

# make a few changes to the calibration criteria
InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_KGE2,
                               InputsModel = InputsModel,
                               RunOptions = RunOptions,
                               Qobs = BasinObs$Qobs[Ind_Run])
# rerun the calibration
OutputsCalib <- Calibration_Michel(InputsModel = InputsModel, 
                                   RunOptions = RunOptions,
                                   InputsCrit = InputsCrit,
                                   CalibOptions = CalibOptions,
                                   FUN_MOD = RunModel_GR4J,
                                   FUN_CRIT = ErrorCrit_KGE2)
Grid-Screening in progress (0% 20% 40% 60% 80% 100%)
     Screening completed (81 runs)
         Param =  432.681 ,   -0.649 ,   83.096 ,    2.384
         Crit KGE'[Q]      = 0.8589
Steepest-descent local search in progress
     Calibration completed (51 iterations, 501 runs)
         Param =  598.748 ,   -0.690 ,   93.679 ,    2.297
         Crit KGE'[Q]      = 0.9621
# redefine the parameters
Param <- OutputsCalib$ParamFinalR
# rerun the model
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
                             RunOptions = RunOptions,
                             Param= Param)
# plot again
plot(OutputsModel, Qobs=BasinObs$Qobs[Ind_Run])

not much different. Oh well, we can be happy with either of those metric scores. - pause for thought - which parameter set would you choose to use?!

Validation

Let’s do some validation

# go back to the beginning, redefine the period to run on (the period we haven't used for calibration, minus the first year needed for warm up)
Ind_Run <- seq(which(BasinObs$DatesR=="1962-01-01"),
             which(BasinObs$DatesR=="1980-12-31"))
RunOptions <- CreateRunOptions(FUN_MOD = RunModel_GR4J,
                               InputsModel = InputsModel,
                               IndPeriod_Run = Ind_Run,
                               IndPeriod_WarmUp = NULL)
     Model warm-up period not defined -> default configuration used 
        The year preceding the run period is used 
     Model states initialisation not defined -> default configuration used 
InputsCrit <- CreateInputsCrit(FUN_CRIT = ErrorCrit_KGE2,
                               InputsModel = InputsModel,
                               RunOptions = RunOptions,
                               Qobs = BasinObs$Qobs[Ind_Run])
Param <- OutputsCalib$ParamFinalR
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
                             RunOptions = RunOptions,
                             Param= Param)
OutputsCrit <- ErrorCrit_KGE2(InputsCrit = InputsCrit, 
                              OutputsModel = OutputsModel)
Crit. KGE'[Q] = 0.9039
    SubCrit. KGE'[Q] cor(sim, obs, "pearson") = 0.9466 
    SubCrit. KGE'[Q] sd(sim)/sd(obs)          = 0.9253 
    SubCrit. KGE'[Q] mean(sim)/mean(obs)      = 1.0282 

slightly worse than the calibration period (0.9621) but not bad at all

finally, let’s run the model for the whole time period and plot a dygraph so we can look at the timeseries in more detail

Ind_Run <- seq(which(BasinObs$DatesR=="1962-01-01"),
             which(BasinObs$DatesR=="2014-12-31"))
RunOptions <- CreateRunOptions(FUN_MOD = RunModel_GR4J,
                               InputsModel = InputsModel,
                               IndPeriod_Run = Ind_Run,
                               IndPeriod_WarmUp = NULL)
     Model warm-up period not defined -> default configuration used 
        The year preceding the run period is used 
     Model states initialisation not defined -> default configuration used 
Param <- OutputsCalib$ParamFinalR
OutputsModel <- RunModel_GR4J(InputsModel = InputsModel,
                             RunOptions = RunOptions,
                             Param= Param)
plot_output_data <- as.data.frame(matrix(NA, ncol = 3,
                                         nrow = length(OutputsModel$DatesR)))
colnames(plot_output_data) <- c("Date", "Qsim", "Qobs")
plot_output_data$Date <- OutputsModel$DatesR
plot_output_data$Qsim <- OutputsModel$Qsim
plot_output_data$Qobs <- BasinObs$Qobs[Ind_Run]
plot_output_data_xts <- as.xts(plot_output_data, order.by = plot_output_data$Date)
dygraph(plot_output_data_xts, main="Observed and Simulated Runoff for the Thames at Kingston (Naturalised)")%>%
  dyOptions(colors = RColorBrewer::brewer.pal(3,"Set1")[2:1])%>%
  dyAxis("y", label="Runoff (mm/day)")%>%
  dyRangeSelector()

Thanks for listening! Hope you get to try it out.

Any general questions? Please feel free to post them on facebook page: https://www.facebook.com/groups/1130214777123909/?ref=bookmarks

GR specific questions? Email airGR@irstea.fr


top

Trend

Exploratory Data Analysis including flow duration curve and trend analysis on time-series Shaun Harrigan Centre for Ecology & Hydrology (shauhar@ceh.ac.uk)


1. Introduction

There is no ‘one best’ programming language, each has individual benefits. R’s strong point is importing messy ‘real-world’ data, cleaning it up, and exploring it with powerful statistical tools!

In this short ‘Trend Analysis taster’ we’re going to:

  • Read and clean up streamflow data from the Boyne catchment in Ireland
  • Run an Exploratory Data Analysis (EDA)
    • Flow Duration Curve (FDC)
    • Check statistical assumptions
  • Plot time-series
  • Perform a Mann-Kendall trend test for evidence of gradual change
  • Perform a Pettitt point test for evidence of abrupt change

The Boyne Case Study

We will use the Boyne catchment in Ireland as a case study. This catchment has a well known abrupt change in its time-series (Harrigan et al., 2014 HESS) and this case study now features in “the Dirty Dozen of freshwater science” by Wilby et al., 2017 WIREs: Water.

OK, lets go get the data…


2. Read in data

Data taken from HESS paper:

  • Obs = Observed daily mean flow (m3/s) data from 01/01/1952 to 31/12/2009
  • Sim = Simulated daily mean flow (m3/s) data from 01/01/1952 to 31/12/2009 using ensemble mean across 3 hydrological models and 328 parameter sets

Observed streamflow data provided by the Office of Public Works (OPW) in Ireland. Please refer to their Ts&Cs before re-use.

A .csv of the data used in this tutorial can be found here.

# Load required packages
library(trend) # Non-Parametric trend tests and change point detection (by Thorsten Pohlert)
library(hydroTSM) # Time series analysis for hydrological modelling (by Mauricio Zambrano Bigiarini )
# Read data
myData <- read.csv("data/Boyne_HESS_data_EGU2017.csv")
# Take a peak
head(myData) # Fist 6 rows
tail(myData) # Last 6 rows
# Quick line plot of Obs and Sim (type = 'l')
plot(myData$Obs, type = 'l')
lines(myData$Sim, col = "red") # 'lines' will add a line to active plot device


3. Flow Duration Curves (FDC)

We can create a FDC easily using the fdc function from the hydroTSM package. Here we will have a look at FDCs from both observed and simulated Boyne streamflow series, pre and post the known period of channel drainage (1969-1986):

# Subset data pre- and post- drainage period
preDrain <- subset(myData, myData$Year >= 1952 & myData$Year <= 1968)
postDrain <- subset(myData, myData$Year >= 1987 & myData$Year <= 2009)
# Select 'Obs' and 'Sim' columns and convert to 'matrix' class (required in 'fdc' function)
preDrainFDC <- as.matrix(preDrain[ ,c(4, 5)])
postDrainFDC <- as.matrix(postDrain[ ,c(4, 5)])
# Plot pre-drainage FDC and print first 10 values
fdc(preDrainFDC, main = "FDC Pre-drainage (1952-1968)", verbose = FALSE)[c(1:10)]
 [1] 0.04750403 0.01272142 0.01417069 0.01867955 0.01578100
 [6] 0.02898551 0.04573269 0.06392915 0.04637681 0.03220612

# Plot post-drainage FDC and print first 10 values
fdc(postDrainFDC, main = "FDC Post-drainage (1987-2009)", verbose = FALSE)[1:10]
 [1] 0.08608081 0.08680014 0.12264716 0.14542621 0.14926268
 [6] 0.17623786 0.21376334 0.24505455 0.24085841 0.20441194


4. Exploratory Data Analysis (EDA)

EDA is the first step in any trend analysis, and its importance is often under appreciated. It serves 3 key purposes:

  1. Visually identifying interesting patterns in the data
  2. Detecting data quality problems (missing data, outliers etc.)
  3. Testing key assumptions for statistical tests used later (e.g. Normality and serial correlation)

We will run a number of standard EDA tests and plots using R’s built in functions:

  • Extract desired hydrological indicators (aggregate, mean)
  • Time-series plots (plot with linear regression line [lm] and lowess smoothing)
  • Histograms (hist)
  • Q-Q plot to check normality (qqnorm; qqline)
  • Shapiro-Wilk test for normality (shapiro.test)
  • Autocorrelation Function (acf)

4.1 Extract hydrological indicators

We will keep it simple and extract Annual Mean Flow (AMF) from both observed and simulated series using aggregate with the mean function.

# Annual mean flow series - Obs (Note: na.rm = T is used to deal with NAs)
obsAMF <- aggregate(myData$Obs, by = list(myData$Year), FUN = function(x) { mean(x, na.rm = T)})
colnames(obsAMF) <- c("Year", "Flow")
# Annual mean flow series - Sim
simAMF <- aggregate(myData$Sim, by = list(myData$Year), FUN = mean)
colnames(simAMF) <- c("Year", "Flow")
# Have a look!
head(obsAMF)
str(obsAMF)
'data.frame':   58 obs. of  2 variables:
 $ Year: int  1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 ...
 $ Flow: num  25.2 19.6 47.7 24.9 22.4 ...

4.2 Plot both observed and simulated series with linear and lowess lines

# Timie-series plot of observed and simulated AMF
plot(obsAMF$Year, obsAMF$Flow, type = 'l', main = "Observed & simulated Boyne AMF 1952-2009", xlab = "Year", ylab = "cumecs", panel.first = grid(), lwd = 1.5)
lines(simAMF$Year, simAMF$Flow, col = "red", lwd = 1.5)
# Linear regression
linRegOutObs <- lm(obsAMF$Flow~obsAMF$Year)
linRegOutSim <- lm(simAMF$Flow~simAMF$Year)
# Add linear regression line
abline(linRegOutObs, col = "black", lty = 2, lwd = 2)
abline(linRegOutSim, col = "red", lty = 2, lwd = 2)
# Add LOWESS line (f = smoother span paramter, i.e. proportion of data influencing smooth)
lines(lowess(obsAMF$Flow~obsAMF$Year, f = 0.2), col = "black", lty = 3, lwd = 2)
lines(lowess(simAMF$Flow~simAMF$Year, f = 0.2), col = "red", lty = 3, lwd = 2)
# Add legend to plot
legend("topleft",
       legend=c("Obs", "Sim"),
       col= c("black", "red"), lty=1,lwd=1, bty="n")
# Add text
text(2000, 20, paste("Trend slope = ", round(linRegOutObs$coefficients[2],2), sep = ""), col = "black")
text(2000, 17, paste("Trend slope = ", round(linRegOutSim$coefficients[2],2), sep = ""), col = "red")

4.3 Check for normality

Many statistical tests require that the data follow normal (or Gaussian) distribution, for example linear regression.

However, often hydroclimatic data violate this assumption, so non-parametric tests are more widely applied (e.g. Mann-Kendall, Pettitt), as will be used below. However, it is still good practice to know the distribution of your data.

We’ll use histograms, Quantile-Quantile-plots (QQ plots), and the shapiro-Wilk test to examine the null hypothesis of normality.

Histograms

# First have a look at the historgram (prob = TRUE is for density)
# Obs histogram
hist(obsAMF$Flow, col = "darkblue", prob = TRUE, main = "Obs")
lines(density(obsAMF$Flow), lwd = 2)

# Sim histogram
hist(simAMF$Flow, col = "darkblue", prob = TRUE, main = "Sim")
lines(density(simAMF$Flow), lwd = 2)

QQ-plots

# QQplot for normality - Obs AMF
qqnorm(obsAMF$Flow, col = "darkblue", main = "Obs"); qqline(obsAMF$Flow, distribution = qnorm)

# QQplot for normality - Sim AMF
qqnorm(simAMF$Flow, col = "darkblue", main = "Sim"); qqline(simAMF$Flow, distribution = qnorm)

Shapiro-Wilk test for normality

We will reject the null hypothesis of normality for p-value < 0.1 (according to Royston (1995) - R help page for shapiro-test).

# Shapiro-Wilk - Obs
shapiro.test(obsAMF$Flow)

    Shapiro-Wilk normality test

data:  obsAMF$Flow
W = 0.96334, p-value = 0.07707
# Shapiro-Wilk - Sim
shapiro.test(simAMF$Flow)

    Shapiro-Wilk normality test

data:  simAMF$Flow
W = 0.97729, p-value = 0.3464

Conclusion on Normality: The simulated series can be considered broadly normally distributed, but the observed series fails the Shapiro-Wilk test and we can see from the histogram is bimodal.

4.4 Check for serial correlation

Presence of positive serial correlation (or autocorrelation) within a series can inflate Type 1 errors. That is, the flow from last year is somehow related to this year. This is common in slow responding groundwater catchments, or in regions that are strongly affected by large-scale climate process over multi-year periods (e.g. North Atlantic Oscillation (NAO)) in Europe. This tends to return too many “statistically significant” results, and thus misleading results.

We’ll use the built in autocorrelation function acf. Typically we’re only concerned if there is evidence of statistically significant positive lag-1 serial correlation at the 5% level (see dashed horizontal line in plots):

# ACF plot for testing for autocorrelation - Obs AMF
acf(obsAMF$Flow, main = "Obs")

# ACF plot for testing for autocorrelation - Sim AMF
acf(simAMF$Flow, main = "Sim")

Conclusion on Serial Correlation: There is no statistically significant lag-1 serial correlation, although some evidence at lag-2 in observed Annual Mean Flow.


5. Statistical trend and change point analysis

Our EDA has revealed a large apparent difference in pattern of change in observations compared to simulated. However, we can test if these patterns of change are statistically significant.

We’ll use the freely available trend package for 3 tasks:

  1. Calculatinig the relative % change in flow over the period-of-record using the robust Thei-Sen approach (TSA) (sens.slope)
  2. Testing for gradual change in annual mean observed and simulated flow with the Mann-Kendall test (mk.test)
  3. Testing for abrupt change in annual mean observed and simulated flow with the Pettitt test (pettitt.test)

5.1 Calculate trend magnitude using the relative Theil-Sen Approach (TSArel)

The non-parametric Theil-Sen estimator is a more robust measure of trend slope than linear regression and will give a much more accurate value in the presence of outliers.

The relative Theil-Sen Approach (TSArel) for calculation of trend magnitude (%) is given:

TSArel = (\(\beta * n\)) / \(\mu\) * 100

where \(\beta\) is the slope from Theil-Sen, \(n\) is the series length, and \(\mu\) is the mean of the series.

# First the 'trend' package requires data are in 'ts' (time-series) format
obsAMFts <- as.ts(obsAMF)
simAMFts <- as.ts(simAMF)
# Calcualte Theil-Sean slope - Obs & Sim
obsSenSlope <- sens.slope(obsAMFts[ ,2])[1] # obs
simSenSlope <- sens.slope(simAMFts[ ,2])[1] # Sim
# Calculate TSArel:
obsTSArel <- (as.numeric(obsSenSlope) * length(obsAMF$Year)) / mean(obsAMF$Flow) * 100
simTSArel <- (as.numeric(simSenSlope) * length(simAMF$Year)) / mean(simAMF$Flow) * 100
# Trend magnitude in Obs (%)
obsTSArel
[1] 37.69257
# Trend magnitude in Sim (%)
simTSArel
[1] 12.19653

Trend magnitude’s in annual mean flow are TSArel = +38% and TSArel = +12% for observed and simulated, respectively!

WOW… an almost 40% increase in observed mean flow in the Boyne. Let’s see what the statistical trend tests say…

5.2 Mann-Kendall test for (gradual) change

We’ll test for gradual (monotonic) trend with the non-parametric Mann-Kendall test.

The standardised MK statistic (MKZs) follows the standard normal distribution with a mean of zero and variance of one. A positive (negative) value of MKZs indicates an increasing (decreasing) trend. Statistical significance was evaluated with probability of type 1 error set at the 5% significance level. We will use a two-tailed MK test (as we won’t assume the direction of change beforehand).

The null hypothesis of no trend (increasing or decreasing) is rejected when |MKZs|>1.96.

# Obs MKZs
obsMKOut <- mk.test(obsAMFts)
obsMKZs  <- obsMKOut$Zg[2]
obsMKZs
[1] 3.105797
# Sim MKZs
simMKOut <- mk.test(simAMFts)
simMKZs  <- simMKOut$Zg[2]
simMKZs
[1] 1.079986

MK Results: MKZs for obs is 3.1 which is much higher than our significance threshold of |1.96| so there is statistically significant increasing trend in observed mean flow.

However, MKZs for simulated flow is 1.1, so while the trend is increasing, the change is not statistically significant.

5.3 Pettitt test for (abrupt) change

We’ll use the Pettitt statistic to identify a single (abrupt) change point in observed and simulated series.

The Pettitt test is nonparametric and relative to other tests is less sensitive to outliers and skewed data. The null hypothesis (no step change in time series) against the alternative (an upward or downward change point in a given year) is tested at the 5% significance level.

# Pettitt test
obsPettittOut <- pettitt.test(obsAMFts[ ,2])
simPettittOut <- pettitt.test(simAMFts[ ,2])
# Find p-value of change points
obs_PettP_val <- obsPettittOut$p.value
sim_PettP_val <- simPettittOut$p.value
# Find year of change points
# Obs
obs_PettYear  <- obsPettittOut$estimate
obsChangeYear <- obsAMF$Year[obs_PettYear]
obsChangeYear # Year
[1] 1978
obs_PettP_val # p-value
[1] 0.001013138
attr(,"Csingle")
[1] TRUE
# Sim
sim_PettYear  <- simPettittOut$estimate
simChangeYear <- simAMF$Year[sim_PettYear]
simChangeYear # Year
[1] 1978
sim_PettP_val # p-value
[1] 0.3985291
attr(,"Csingle")
[1] TRUE

Pettitt Results: There is a statistically significant upward change point in Boyne mean flow and this occurred in 1978 (p = 0.001). While the Pettitt test returns 1978 as the year of change, it’s much too weak to be considered an abrupt change point as the p-value is 0.4.


6. Summary of results

Test Observed Modelled
TSArel 38% 12%
MKZs 3.1 1.1
Pettitt 1978 (p = 0.001) 1978 (p = 0.4)
# Timie-series plot of observed and simulated AMF
plot(obsAMF$Year, obsAMF$Flow, type = 'l', main = "Observed & simulated Boyne AMF 1952-2009", xlab = "Year", ylab = "cumecs", panel.first = grid(), lwd = 1.5)
lines(simAMF$Year, simAMF$Flow, col = "red", lwd = 1.5)
# Linear regression
linRegOutObs <- lm(obsAMF$Flow~obsAMF$Year)
linRegOutSim <- lm(simAMF$Flow~simAMF$Year)
# Add linear regression line
abline(linRegOutObs, col = "black", lty = 2, lwd = 2)
abline(linRegOutSim, col = "red", lty = 2, lwd = 2)
# Add LOWESS line (f = smoother span paramter, i.e. proportion of data influencing smooth)
lines(lowess(obsAMF$Flow~obsAMF$Year, f = 0.2), col = "black", lty = 3, lwd = 2)
lines(lowess(simAMF$Flow~simAMF$Year, f = 0.2), col = "red", lty = 3, lwd = 2)
# Add vertical change point line
abline(v = 1978, col = "darkblue", lwd = 2)
text(1976, 45, "Change Point 1978", col = "darkblue", srt = 90) # 'srt' rotates text 90 degrees
# Add legend to plot
legend("topleft",
       legend=c("Obs", "Sim"),
       col= c("black", "red"), lty=1,lwd=1, bty="n")
# Add text
text(2000, 20, paste("Trend mag. (%) = ", round(obsTSArel,0), sep = ""), col = "black")
text(2000, 17, paste("Trend mag. (%) = ", round(simTSArel,0), sep = ""), col = "red")

7. Further topics

7.1 Block-bootstrapping (BBS) statistics

If the serial correlation assumption is severely violated, block-bootstrapping (BBS) offers a viable method - example below:

# Need 'boot' package
library(boot) # bootstrapping
library(Kendall) # Another package with the 'Mann-Kendall' test - but this works better with 'tsboot'
# My MKZs Function ---------------------------------------------------------------
myMKZsFun <- function(x) {
  S <- MannKendall(x)$S
  varS <- MannKendall(x)$varS
  #Calculate the Zs statistic
  if (S > 0) {
    Zs <- (S-1)/sqrt(varS)
  } else if (S < 0) {
    Zs <- (S+1)/sqrt(varS)
  } else {
    Zs <- 0
  }
  return(Zs)  # MkZs
}
# end function --------------------------------------------------------------------
# Set.seed() for reproducability
set.seed(50)
# BBS with block length = 4 and 1000 resamples
b4 <- tsboot((obsAMF[ ,2]), myMKZsFun, R=1000,  sim="fixed", l=3)
# sort boot output
bs4 <- sort(b4$t)
# BBS critical values
bs4[25] # lower
[1] -2.174693
bs4[975] # upper
[1] 2.32295
# Plot bootstrap output
plot(b4)

7.2 Multiple Mann-Kendall tests

R is much more powerful than just a statistical tool - it’s also a capable programming environment so we’re able to perform individual tasks, such as calculating a trend statistic, for many iterations using loops etc.

Here is a quick example of how the dependence of the Mann-Kendall Zs statistic on the period-of-record used. We test for changes in observed and modelled flow from all possible start years up to 1985: 1952-2009, 1953-2009,…,1985-2009.

# Create output matrix to store trend results
mat <- matrix(NA, ncol = 3, nrow = 34)
colnames(mat) <- c("StartYear", "obsMKZs", "simMKZs")
mat[ ,1] <- 1952:1985
# Loop Mann-Kendall test for all possible start years in both obs and sim
for(i in 1:34) {
  mat[i,2] <- myMKZsFun(obsAMF[ ,2][i:58]) # Obs
  mat[i,3] <- myMKZsFun(simAMF[ ,2][i:58]) # Sim
} # end i
# Plot
plot( mat[ ,1], mat[ ,2], type = 'l', col = "darkgray", lty = 1, lwd = 2, ylim = c(-3, 3), main = "Persistence of trend - Obs & Sim", xlab = "Start Year", ylab = "MKZs")
lines(mat[ ,1], mat[ ,3], col = "darkgray", lty = 3, lwd = 2)
# Add lines to show statistically significant +/- trends and zero trend
abline(h =  1.96, col = "red", lty = 2, lwd = 2)
abline(h = -1.96, col = "red", lty = 2, lwd = 2)
abline(h = 0, col = "black", lty = 2, lwd = 2)
# First legend
legend.text <- c("No trend (MKZs = 0)", "Sig. trend (MKZs > |1.96|)")
legend("bottomleft",                   # Set location of the legend
       legend = legend.text,           # Specify text
       col = c("black","red"),         # Set colors for legend
       lty = c(2,2),                   # Set type of lines in legend
       merge = TRUE, bty = 'n', lwd = c(2, 2))
# Second legend
legend("topright",
       legend=c("Obs", "Sim"),
       col= c("darkgray", "darkgray"), lty=c(1, 3),lwd=c(2,2), bty="n")


END of tutorial - I hope you’ll agree R is a nice (free!) alternative to e.g. Matlab :)



top

Discussion

Please give us feedback at bit.ly/feedbackR

For discussions, please use the Hydrology in R Facebook group.

Further resources can be found on the github page: https://github.com/brry/rhydro#resources

LS0tCnRpdGxlOiAiUmh5ZHJvIgpvdXRwdXQ6CiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogJzMnCiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogbm9uZQogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogMwogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IG5vCmFsd2F5c19hbGxvd19odG1sOiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KaWYgKGtuaXRyOjo6aXNfaHRtbF9vdXRwdXQoKSkgewogIGtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gRkFMU0UpCn0KCiNrbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGU9VFJVRSkKYGBgCgojIFVzaW5nIFIgaW4gaHlkcm9sb2d5IChFR1UyMDE3IHNob3J0IGNvdXJzZSkKCipJbnN0cnVjdG9ycyo6IFNoYXVuIEhhcnJpZ2FuLCBLYXRpZSBTbWl0aCwgQmVycnkgQm9lc3Nlbmtvb2wgYW5kIERhbmllbCBLbG90eiAgCipPcmdhbml6ZXIqOiBCZXJyeSBCb2Vzc2Vua29vbCAgCipDb250YWN0KjogUXVlc3Rpb25zIGFuZCBmZWVkYmFjayBhcmUgd2VsY29tZSB2aWEgPGJlcnJ5LWJAZ214LmRlPiAgCipMYXN0IHVwZGF0ZWQqOiAyMDE3LTA0LTI0CgpQbGVhc2UgbGV0IHVzIGtub3cgeW91ciBjdXJyZW50IFIga25vd2xlZGdlIGxldmVsIGJ5IGZpbGxpbmcgb3V0IHRoZSBzaG9ydCBzdXJ2ZXkgYXQgIAo8Zm9udCBzaXplPSI2Ij5bYml0Lmx5L2tub3dSXShodHRwczovL2JpdC5seS9rbm93Uik8L2ZvbnQ+IAoKVGhlc2Ugc2xpZGVzIGFuZCBhbGwgb3RoZXIgY291cnNlIG1hdGVyaWFscyBjYW4gYmUgZm91bmQgYXQgIAo8Zm9udCBzaXplPSI2Ij5bZ2l0aHViLmNvbS9icnJ5L3JoeWRyb10oaHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvKTwvZm9udD4gICAKW0Rvd25sb2FkIHRoZSBnaXRodWIgY291cnNlIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRyby9hcmNoaXZlL21hc3Rlci56aXApCndpdGggYWxsIHRoZSBtYXRlcmlhbHMgaW5jbHVkaW5nIHRoZSBkYXRhc2V0cyBhbmQgcHJlc2VudGF0aW9uIHNvdXJjZSBjb2RlLiAgClRoaXMgaXMgYW4gW1IgTWFya2Rvd24gTm90ZWJvb2tdKGh0dHA6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vcl9ub3RlYm9va3MuaHRtbCkuICAKRm9yIGRpc2N1c3Npb25zLCBwbGVhc2UgdmlzaXQgdGhlIApbSHlkcm9sb2d5IGluIFIgRmFjZWJvb2sgZ3JvdXBdKGh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9ncm91cHMvMTEzMDIxNDc3NzEyMzkwOS8pLiAgCkJlZm9yZSBydW5uaW5nIHRoZSBjb2RlIGJsb2NrcyBiZWxvdywgd2Ugc3VnZ2VzdCB0byBnZXQgcGFja2FnZSBpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zIGJ5IHJ1bm5pbmc6CmBgYFIKc291cmNlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYnJyeS9yaHlkcm8vbWFzdGVyL2NoZWNrcGMuUiIpCmBgYAoKXAoKKipBaW0gYW5kIGNvbnRlbnRzIG9mIHRoaXMgd29ya3Nob3AqKgoKV2Ugd2FudCB0bzogIAoKKiBTaG93IG9mZiBob3cgYXdlc29tZSBSIGlzIGZvciBoeWRyb2xvZ3kgKGl0J3MgUi1zb21lIV5eKSAgCiogQ29udmluY2UgeW91IHRvIHN0YXJ0IG9yIGNvbnRpbnVlIHVzaW5nIFIgIAoqIFByb3ZpZGUgYWxsIHRoZSBjb2RlIGZvciB5b3UgYXMgYSBzdGFydGluZyBwb2ludAoKV2UgY2FuIG5vdDogIAoKKiBUZWFjaCB5b3UgYWN0dWFsIFIgY29kaW5nICg5MCBtaW5zIGlzIHRvbyBzaG9ydCBmb3IgYSB0dXRvcmlhbCkKCldlIGhhdmUgcHJlcGFyZWQ6CgoqIFtHb29kIGNvZGluZyBwcmFjdGljZSwgcmVwb3J0IGdlbmVyYXRpb25dKCNyZXBvcnQpIChSc3R1ZGlvLCBgcm1hcmtkb3duYCwgUiBub3RlYm9vaykKKiBbVXNpbmcgUiBhcyBHSVNdKCNnaXMpIChyZWFkaW5nIGEgcmFpbmZhbGwgc2hhcGVmaWxlICsgS3JpZ2luZywgYHNmYCArIGBsZWFmbGV0YCArIGBtYXB2aWV3YCArIGBPU01zY2FsZWApCiogW1JpdmVyIGRpc2NoYXJnZSB0aW1lLXNlcmllc10oI2Rpc2NoYXJnZSkgdmlzdWFsaXNhdGlvbiBhbmQgZXh0cmVtZSB2YWx1ZSBzdGF0aXN0aWNzIChgYW5pbWF0aW9uYCArIGBleHRyZW1lU3RhdGApCiogW0h5ZHJvbG9naWNhbCBtb2RlbGxpbmddKCNoeWRtb2QpIHdpdGggYGFpckdSYAoqIFtUcmVuZCBhbmF5c2lzXSgjdHJlbmQpIGluY2x1ZGluZyBmbG93IGR1cmF0aW9uIGN1cnZlIChgdHJlbmRgICsgYGh5ZHJvVFNNYCkKCgpcCgpbdG9wXSgjdG9wKQoKIyBSZXBvcnQKR29vZCBjb2RpbmcgcHJhY3RpY2UsIHJlcG9ydCBnZW5lcmF0aW9uIChSc3R1ZGlvLCBgcm1hcmtkb3duYCwgUiBub3RlYm9vaykKKipEYW5pZWwgS2xvdHoqKiBCT0tVIFZpZW5uYSAoPGRhbmllbC5rbG90ekBib2t1LmFjLmF0PikKCiMjIyBXaHkgdG8gdXNlIFIKCiFbXShkYW5pZWwvaW50cm8vZ29hbHMuanBlZykKCldoeSBJIGRpZCBub3QgdXNlIFI6CgohW10oZGFuaWVsL2ludHJvL2VxdWFscy5qcGVnKQoKCldoYXRzIGdyZWF0IGFib3V0IFI6CmBgYHtyfQogIGxpYnJhcnkoZ2dwbG90MikKICB0ZXN0X2RhdGEgPC0gbXBnCiAgdGVzdF9wbG90IDwtIGdncGxvdCh0ZXN0X2RhdGEsIGFlcyhkaXNwbCwgaHd5LCBjb2xvdXIgPSBjbGFzcykpICsKICAgIGdlb21fcG9pbnQoKQogIHRlc3RfcGxvdApgYGAKCldoeSBJIGRlY2lkZWQgdG8gdXNlIFI6CgohW3BpcGVdKGRhbmllbC9pbnRyby9tYWdyaXR0ci5qcGVnKQoKUHJldmlvdXNseToKYGBge3J9CiAgYWdncmVnYXRpb25fZnVuY3Rpb24gPC0gZnVuY3Rpb24oeCkgewogICAgcm91bmQobWVhbih4KSwyKQogIH0KICBtdGNhcnNfc3Vic2V0IDwtIHN1YnNldChtdGNhcnMsaHAgPiAxMDApCiAgbXRjYXJzX2FnZ3JlZ2F0ZWQgPC0gYWdncmVnYXRlKC4gfiBjeWwsIGRhdGEgPSBtdGNhcnNfc3Vic2V0LCBGVU4gPSBhZ2dyZWdhdGlvbl9mdW5jdGlvbikKICBjYXJfZGF0YTEgPC0gdHJhbnNmb3JtKG10Y2Fyc19hZ2dyZWdhdGVkLCBrcGwgPSBtcGcqMC40MjUxKQogIHByaW50KGNhcl9kYXRhMSkKYGBgCgpOb3c6CmBgYHtyfQpsaWJyYXJ5KG1hZ3JpdHRyKQpjYXJfZGF0YTIgPC0KICBtdGNhcnMgJT4lCiAgc3Vic2V0KGhwID4gMTAwKSAlPiUKICBhZ2dyZWdhdGUoLiB+IGN5bCwgZGF0YSA9IC4sIEZVTiA9IC4gJT4lIG1lYW4gJT4lIHJvdW5kKDIpKSAlPiUKICB0cmFuc2Zvcm0oa3BsID0gbXBnICU+JSBtdWx0aXBseV9ieSgwLjQyNTEpKSAlPiUKICBwcmludApgYGAKCgoqKmJ0dzoqKgpZb3UgY2FuIGludGVncmF0ZSBvdGhlciBwcm9ncmFtbWluZyBsYW5ndWFnZXMgd2l0aCBlYXNlLiBIZXJlIGFuIGV4YW1wbGUgZnJvbQpbWWlodWkgWGllXShodHRwczovL3lpaHVpLm5hbWUva25pdHIvZGVtby9lbmdpbmVzLykgZm9yIHRoZSB1c2Ugb2YgYEZvcnRyYW5gCmluIFJtYXJrZG93bjoKCjEuIENvbXBpbGUgQ29kZToKICAgIGBgYHtyIGNvbXBmb3J0LCBlbmdpbmU9J2ZvcnRyYW4nLCByZXN1bHRzPSdoaWRlJywgZXZhbD1GQUxTRX0KICAgIEMgRm9ydHJhbiB0ZXN0CiAgICAgICAgICBzdWJyb3V0aW5lIGZleHAobiwgeCkKICAgICAgICAgIGRvdWJsZSBwcmVjaXNpb24geAogICAgQyAgb3V0cHV0CiAgICAgICAgICBpbnRlZ2VyIG4sIGkKICAgIEMgIGlucHV0IHZhbHVlCiAgICAgICAgICBkbyAxMCBpPTEsbgogICAgICAgICAgICAgeD1kZXhwKGRjb3MoZHNpbihkYmxlKGZsb2F0KGkpKSkpKQogICAgICAxMCAgY29udGludWUKICAgICAgICAgIHJldHVybgogICAgICAgICAgZW5kCiAgICBgYGAKCjIuIFJ1biBDb2RlOgogICAgYGBge3IgdGVzdGZvcnQsIGNvbGxhcHNlPVRSVUUsIGV2YWwgPSBGQUxTRX0KICAgIHJlcyA9IC5Gb3J0cmFuKCJmZXhwIiwgbj0xMDAwMDBMLCB4PTApCiAgICBzdHIocmVzKQogICAgYGBgCgpCZSBoYXBweSB3aXRoIHRoZSByZXN1bHQ6Cj4gICAgIGAjIyBMaXN0IG9mIDJgCj4gICAgIGAjIyAgJCBuOiBpbnQgMTAwMDAwYAo+ICAgICBgIyMgICQgeDogbnVtIDIuNzJgCgotLS0KCiMjIyBNYXJrZG93bgpIVCoqTSoqTDogSHlwZXJUZXh0ICoqTWFya2Rvd24qKiBMYW5ndWFnZQoKSm9obiBHcnViZXI6CgpbIVtKb2huIEdydWJlcl0oZGFuaWVsL2ludHJvL0pvaG5fR3J1YmVyX3dpa2kuanBlZyldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0pvaG5fR3J1YmVyKQoKCkNvbXBhcmlzb246IE1hcmtkb3duIHZzLiBMYXRleApbIVtDb21wYXJpc29uXShkYW5pZWwvaW50cm8veWlodWlfbGF0ZXgtdnMtbWFya2Rvd24ucG5nKV0oaHR0cHM6Ly95b3V0dS5iZS8yeXZXME9fN3hPZykKCgpSc3R1ZGlvIHByb3ZpZGVzIGNoZWF0LXNoZWV0cyB3aXRoIHRoZSBtb3N0IGltcG9ydGFudCBpbmZvcm1hdGlvbnMgYWJvdXQgbWFueQpvZiB0aGVpciAiZmF2b3JpdGUiIHBhY2thZ2VzICYgc29mdHdhcmU6CgpbIVtDaGVhdCBTaGVldF0oZGFuaWVsL2ludHJvL1JtYXJrZG93bl9jaGVhdHNoZWV0LnBuZyldKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pCgoqKiBSbWFya2Rvd24gKioKCkluIFJzdHVkaW86Cgo8ZGl2IGFsaWduPSJjZW50ZXIiPgogIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9yZXBvcnRzL1JtYXJrMS5wbmciIGFsdD0iUm1hcmsxIiAvPgo8L2Rpdj4KCjxkaXYgYWxpZ249ImNlbnRlciI+CiAgPGltZyB3aWR0aD0iNjAwcHgiIHNyYz0iZGFuaWVsL3JlcG9ydHMvUm1hcmsyLnBuZyIgYWx0PSJSbWFyazIiIC8+CjwvZGl2PgoKPGRpdiBhbGlnbj0iY2VudGVyIj4KICA8aW1nIHdpZHRoPSI2MDBweCIgc3JjPSJkYW5pZWwvcmVwb3J0cy9SbWFyazMucG5nIiBhbHQ9IlJtYXJrMiIgLz4KPC9kaXY+CgoiTmF0aXZlIiBGb3JtYXRzOgoKLSBbaHRtbF0oZGFuaWVsL3Nob3cvUm1hcmsuaHRtbCkKLSBbcGRmXShkYW5pZWwvc2hvdy9SbWFyay5wZGYpCi0gW3dvcmRdKGRhbmllbC9zaG93L1JtYXJrLmRvY3MpCgpNdWNoIG1vcmUgcG9zc2libGUgaWYgeW91IGFkcmVzcyBwYW5kb2MgZGlyZWN0bHk6ClshW3BhbmRvY10oZGFuaWVsL3JlcG9ydHMvcGFuZG9jX2RpYWdyYW0uanBnKV0oaHR0cDovL3BhbmRvYy5vcmcvKQoKSW5mb3JtYXRpb24gaW4gdGhlIHRleHQgY2FuIGJlIGF1dG9tYXRpY2FsbHkgdXBkYXRlZCB3aXRoIHRoZSByZXN0IG9mIHRoZQpkb2N1bWVudDoKWyFbdGltZSBmb3IgY29mZmVlXShkYW5pZWwvcmVwb3J0cy9jb2ZmZWUucG5nKQoKIyMjIEV4YW1wbGVzCgojIyMjIFNtYWxsIFdlYnNpdGVzCjxkaXYgYWxpZ249ImNlbnRlciI+CiAgPGEgaHJlZiA9ICJodHRwOi8vcnN0dWRpby5naXRodWIuaW8vdHVmdGUvIj4KICAgIDxpbWcgd2lkdGg9IjYwMHB4IiBzcmM9ImRhbmllbC9leGFtcGxlcy9zd190dWZ0ZS5wbmciIGFsdD0iQ2F5bWFuIFRoZW1lIiAvPgogIDwvYT4KPC9kaXY+Cgo8ZGl2IGFsaWduPSJjZW50ZXIiPgogIDxhIGhyZWYgPSAiaHR0cDovL3lpeHVhbi5jb3MubmFtZS9wcmV0dHlkb2MvY2F5bWFuLmh0bWwiPgogICAgPGltZyB3aWR0aD0iNjAwcHgiIHNyYz0iZGFuaWVsL2V4YW1wbGVzL3N3X2NheW1hbi5wbmciIGFsdD0iQ2F5bWFuIFRoZW1lIiAvPgogIDwvYT4KPC9kaXY+CgojIyMjIEJvb2tzIChibG9nZG93bikKWyFbYm9va2Rvd24xXShkYW5pZWwvZXhhbXBsZXMvYm9va2Rvd25fMS5wbmcpXShodHRwczovL2Jvb2tkb3duLm9yZy8pClshW2Jvb2tkb3duMl0oZGFuaWVsL2V4YW1wbGVzL2Jvb2tkb3duXzIucG5nKV0oaHR0cHM6Ly9ib29rZG93bi5vcmcvKQoKIyMjIyBCbG9ncyAoaHVnbykKWyFbaHVnb10oZGFuaWVsL2V4YW1wbGVzL2Jsb2dzX2h1Z28xLnBuZyldKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL2Jsb2dkb3duLykKCiMjIyMgUHJlc2VudGF0aW9ucwpbIVtib29rZG93bjFdKGRhbmllbC9leGFtcGxlcy9wcmVzZW50YXRpb25zXzEucG5nKV0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9yZXZlYWxqc19wcmVzZW50YXRpb25fZm9ybWF0Lmh0bWwpCgojIyMjIFdpZGdldHMKWyFbY3Jhbi1nYXVnZV0oZGFuaWVsL2V4YW1wbGVzL3dpZGdldHNfMS5wbmcpXShodHRwczovL2dhbGxlcnkuc2hpbnlhcHBzLmlvL2NyYW4tZ2F1Z2UvKQoKIyMjIyBBcHBzIChTaGlueSkKWyFbc2hpbnldKGRhbmllbC9leGFtcGxlcy9zaGlueV8xLmpwZWcpXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2F1dGhvcmluZ19zaGlueS5odG1sKQpbIVtzaGlueTJdKGRhbmllbC9leGFtcGxlcy9zaGlueV8yX2dhbGxlcnkucG5nKV0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5L3N1cGVyemlwLWV4YW1wbGUuaHRtbCkKIVtzaGlueTJdKGRhbmllbC9leGFtcGxlcy9zaGlueV8zX2pvaGFubmVzLnBuZykKCiFbc2hpbnkzXShkYXRhL3NoaW55d3JpZ2h0LnBuZykKUiBiYXNlZCBoeWRyb2xvZ3kgdG9vbHMgZnJvbSB0aGUgW0ludGVsbGlnZW50IFdhdGVyIERlY2lzaW9ucyBSZXNlYXJjaCBHcm91cF0oaHR0cDovL3dhdGVyZGVjaXNpb25zLm9yZy8pIG9mIHRoZSBVbml2ZXJzaXR5IG9mIEFkZWxhaWRlOgoKKiBSU2hpbnkgYGluZmx1ZW5jZSByZWdyZXNzaW9uIGFwcGAgYnkgIERhdmlkIFdyaWdodDoKPGh0dHBzOi8vZGF2aWRwd3JpZ2h0LnNoaW55YXBwcy5pby9MaW5lYXJSZWdyZXNzaW9uSW5mbHVlbmNlRXhhbXBsZS8+CiogYEV2YXBvdHJhbnNwaXJhdGlvbmAgUiBwYWNrYWdlIHRoYXQgZW5hYmxlcyB0aGUgdXNlIG9mIDE3IHdlbGwta25vd24gRVQgbW9kZWxzIGluIGEgY29uc2lzdGVudCBtYW5uZXIuCgoKClwKW3RvcF0oI3RvcCkKCiMgR0lTClVzaW5nIFIgYXMgR0lTIChyZWFkaW5nIGEgcmFpbmZhbGwgc2hhcGVmaWxlICsgS3JpZ2luZywgYHNmYCArIGBsZWFmbGV0YCArIGBtYXB2aWV3YCArIGBPU01zY2FsZWApCioqQmVycnkgQm9lc3Nlbmtvb2wqKiBQb3RzZGFtIFVuaXZlcnNpdHksIEdlcm1hbnkgKDxiZXJyeS1iQGdteC5kZT4pCgojIyMgU2hhcGVmaWxlcwoKUmVhZGluZyBzaGFwZWZpbGVzIHdpdGggYG1hcHRvb2xzOjpyZWFkU2hhcGVTcGF0aWFsYCBhbmQgYHJnZGFsOjpyZWFkT0dSYCBpcyBvYnNvbGV0ZS4KSW5zdGVhZCwgdXNlIGBzZjo6c3RfcmVhZGAuIGBzZmAgaXMgb24gQ1JBTiBzaW5jZSBvY3QgMjAxNi4KTWFpbiByZWFjdGlvbiB3aGVuIHVzaW5nIHNmOiAiV293LCB0aGF0IGlzIGZhc3QhIgpbRG93bmxvYWQgdGhlIHNoYXBlZmlsZV0oaHR0cHM6Ly9taW5oYXNrYW1hbC5naXRodWIuaW8vRG93bkdpdC8jL2hvbWU/dXJsPWh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRyby90cmVlL21hc3Rlci9wcmVzZW50YXRpb25zL2RhdGEvUHJlY0JyYW5kZW5idXJnKQpvciBiZXR0ZXI6IFtkb3dubG9hZCB0aGUgd2hvbGUgZ2l0aHViIGNvdXJzZSByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vYnJyeS9yaHlkcm8vYXJjaGl2ZS9tYXN0ZXIuemlwKQoKYGBge3J9CnJhaW4gPC0gc2Y6OnN0X3JlYWQoImRhdGEvUHJlY0JyYW5kZW5idXJnL25pZWRlcnNjaGxhZy5zaHAiKQpgYGAKCkNlbnRyYWwgcG9pbnRzIG9mIHJhaW5mYWxsIFRoaWVzc2VuIHBvbHlnb25zCmBgYHtyfQpjZW50cm9pZHMgPC0gc2Y6OnN0X2NlbnRyb2lkKHJhaW4pCmNlbnRyb2lkcyA8LSBzZjo6c3RfY29vcmRpbmF0ZXMoY2VudHJvaWRzKQpjZW50cm9pZHMgPC0gYXMuZGF0YS5mcmFtZShjZW50cm9pZHMpCmBgYAoKW3RvcF0oI3RvcCkKCiMjIyBQbG90dGluZywgbWFwcwoKU3RhdGljIHBsb3Q6CmBgYHtyfQpwbG90KHJhaW5bLDFdKQpgYGAKClN0YXRpYyBtYXA6CmBgYHtyfQpwcmogPC0gc2Y6OnN0X2NycyhyYWluKSRwcm9qNHN0cmluZwojY2VudF9sbCA8LSBPU01zY2FsZTo6cHJvamVjdFBvaW50cyhZLFgsIGRhdGE9Y2VudHJvaWRzLCB0bz1PU01zY2FsZTo6cGxsKCksIGZyb209cHJqKQojbWFwX3N0YXRpYyA8LSBPU01zY2FsZTo6cG9pbnRzTWFwKHkseCwgY2VudF9sbCwgZng9MC4wOCwgdHlwZT0ibWFwdG9vbGtpdC10b3BvIiwgem9vbT02KQojc2F2ZShtYXBfc3RhdGljLCBmaWxlPSJkYXRhL21hcF9zdGF0aWMuUmRhdGEiKQojbG9hZCgiZGF0YS9tYXBfc3RhdGljLlJkYXRhIikKI09TTXNjYWxlOjpwb2ludHNNYXAoeSx4LCBjZW50X2xsLCBtYXA9bWFwX3N0YXRpYykKYGBgCgo8aW1nIHNyYz0iZGF0YS9vc21zY2FsZV9maWcucG5nIiBhbHQ9IiIgc3R5bGU9IndpZHRoOiA0MDBweDsiLz4KCkludGVyYWN0aXZlIG1hcDoKYGBge3IsZXZhbD1GfQpsaWJyYXJ5KGxlYWZsZXQpCmNlbnRfbGwkaW5mbyA8LSBwYXN0ZTAoc2FtcGxlKGxldHRlcnMsbnJvdyhjZW50X2xsKSxUUlVFKSwgIiwgIiwgcm91bmQoY2VudF9sbCR4LDIpLAogICAgICAgICAgICAgICAgICAgICAgICIsICIsIHJvdW5kKGNlbnRfbGwkeSwyKSkKbGVhZmxldChjZW50X2xsKSAlPiUgYWRkVGlsZXMoKSAlPiUgYWRkQ2lyY2xlTWFya2Vycyhsbmc9fngsIGxhdD1+eSwgcG9wdXA9fmluZm8pCmBgYApMb29rcyBzaW1pbGFyIGluIHN0eWxlIHRvCltyZHdkIGludGVyYWN0aXZlIG1hcF0oZGF0YS9tYXBEV0QuaHRtbCkKCkludGVyYWN0aXZlIG1hcCBvZiBzaGFwZWZpbGU6CmBgYHtyfQojIG1ha2Ugc3VyZSB0byBoYXZlIGluc3RhbGxlZCB0aGUgZGV2ZWxvcG1lbnQgdmVyc2lvbiBvZiBtYXB2aWV3OgojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiZW52aXJvbm1lbnRhbGluZm9ybWF0aWNzLW1hcmJ1cmcvbWFwdmlldyIsIHJlZiA9ICJkZXZlbG9wIikKbGlicmFyeShiZXJyeUZ1bmN0aW9ucykgIyBjbGFzc2lmeSwgc2VxUGFsCmNvbCA8LSBzZXFQYWwobj0xMDAsIGNvbG9ycz1jKCJyZWQiLCJ5ZWxsb3ciLCJibHVlIikpW2NsYXNzaWZ5KHJhaW4kUDEpJGluZGV4XQptYXB2aWV3OjptYXB2aWV3KHJhaW4sIGNvbC5yZWdpb25zPWNvbCkKYGBgCgpbdG9wXSgjdG9wKQoKIyMjIEtyaWdpbmcKClBsb3Qgb3JpZ2luYWwgcG9pbnRzIGNvbG9yZWQgYnkgdGhpcmQgZGltZW5zaW9uOgpgYGB7cn0KcGNvbCA8LSBjb2xvclJhbXBQYWxldHRlKGMoInJlZCIsInllbGxvdyIsImJsdWUiKSkoNTApCnggPC0gY2VudHJvaWRzJFggIyB1c2UgY2VudF9sbCR4IGZvciBwcm9qZWN0ZWQgZGF0YQp5IDwtIGNlbnRyb2lkcyRZCmJlcnJ5RnVuY3Rpb25zOjpjb2xQb2ludHMoeCwgeSwgcmFpbiRQMSwgYWRkPUZBTFNFLCBjb2w9cGNvbCwgeTE9MC44KQpgYGAKCkNhbGN1bGF0ZSB0aGUgdmFyaW9ncmFtIGFuZCBmaXQgYSBzZW1pdmFyaWFuY2UgY3VydmUKYGBge3J9CmxpYnJhcnkoZ2VvUikKZ2VvcHJlYyA8LSBhcy5nZW9kYXRhKGNiaW5kKHgseSxyYWluJFAxKSkKdmFyaW8gPC0gdmFyaW9nKGdlb3ByZWMsIG1heC5kaXN0PTEzMDAwMCkgIyBvdGhlciBtYXhkaXN0IGZvciBsYXQtbG9uIGRhdGEKZml0IDwtIHZhcmlvZml0KHZhcmlvKQpwbG90KHZhcmlvKQpsaW5lcyhmaXQpCmBgYAoKRGV0ZXJtaW5lIGEgdXNlZnVsIHJlc29sdXRpb24KKGtlZXAgaW4gbWluZCB0aGF0IGNvbXB1dGluZyB0aW1lIHJpc2VzIGV4cG9uZW50aWFsbHkgd2l0aCBncmlkIHNpemUpCmBgYHtyLGV2YWw9Rn0KIyBkaXN0YW5jZSB0byBjbG9zZXN0IG90aGVyIHBvaW50OgpkIDwtIHNhcHBseSgxOmxlbmd0aCh4KSwgZnVuY3Rpb24oaSkKICAgICAgICAgICAgbWluKGJlcnJ5RnVuY3Rpb25zOjpkaXN0YW5jZSh4W2ldLCB5W2ldLCB4Wy1pXSwgeVstaV0pKSApCiMgZm9yIGxhdC1sb25nIGRhdGEgdXNlICgyMDE3LUFwciBvbmx5IGF2YWlsYWJsZSBpbiBnaXRodWIgdmVyc2lvbiBvZiBPU01zY2FsZSkKIyBkIDwtIE9TTXNjYWxlOjptYXhFYXJ0aERpc3QoeSx4LCBkYXRhPWNlbnRfbGwsIGZ1bj1taW4pCmhpc3QoZC8xMDAwLCBicmVha3M9MjAsIG1haW49ImRpc3RhbmNlIHRvIGNsb3Nlc3QgZ2F1Z2UgW2ttXSIpCm1lYW4oZC8xMDAwKSAjIDgga20KYGBgCgpQZXJmb3JtIGtyaWdpbmcgb24gYSBncmlkIHdpdGggdGhhdCByZXNvbHV0aW9uCmBgYHtyfQpyZXMgPC0gMTAwMCAjIDEga20sIHNpbmNlIHN0YXRpb25zIGFyZSA4IGttIGFwYXJ0IG9uIGF2ZXJhZ2UKZ3JpZCA8LSBleHBhbmQuZ3JpZChzZXEobWluKHgpLG1heCh4KSxyZXMpLAogICAgICAgICAgICAgICAgICAgIHNlcShtaW4oeSksbWF4KHkpLHJlcykpCmtyaWNvIDwtIGtyaWdlLmNvbnRyb2wodHlwZS5rcmlnZT0iT0siLCBvYmoubW9kZWw9Zml0KQoja3JvYmogPC0ga3JpZ2UuY29udihnZW9wcmVjLCBsb2M9Z3JpZCwga3JpZ2U9a3JpY28pCiNzYXZlKGtyb2JqLCBmaWxlPSJkYXRhL2tyb2JqLlJkYXRhIikKbG9hZCgiZGF0YS9rcm9iai5SZGF0YSIpICMgbGluZSBhYm92ZSBpcyB0b28gc2xvdyBmb3IgcmVjcmVhdGlvbiBlYWNoIHRpbWUKYGBgCgpQbG90IHRoZSBpbnRlcnBvbGF0ZWQgdmFsdWVzIHdpdGggYGltYWdlYCBvciBhbiBlcXVpdmFsZW50IGZ1bmN0aW9uCihzZWUgW1JjbGlja10oaHR0cHM6Ly9naXRodWIuY29tL2JycnkvcmNsaWNrKSA0LjE1KSBhbmQgYWRkIGNvbnRvdXIgbGluZXMuCmBgYHtyfQpwYXIobWFyPWMoMCwzLDAsMykpCmdlb1I6OjppbWFnZS5rcmlnaW5nKGtyb2JqLCBjb2w9cGNvbCkKY29sUG9pbnRzKHgsIHksIHJhaW4kUDEsIGNvbD1wY29sLCBsZWdhcmdzPWxpc3QoaG9yaXo9RiwgdGl0bGU9IlByZWMiLHkxPTAuMSx4MT0wLjkpKQpwb2ludHMoeCx5KQpwbG90KHJhaW4sIGNvbD1OQSwgYWRkPVRSVUUpCmBgYApcClt0b3BdKCN0b3ApCgojIERpc2NoYXJnZQpSaXZlciBkaXNjaGFyZ2UgdGltZS1zZXJpZXMgdmlzdWFsaXNhdGlvbiBhbmQgZXh0cmVtZSB2YWx1ZSBzdGF0aXN0aWNzIChgYW5pbWF0aW9uYCArIGBleHRyZW1lU3RhdGApICAKKipCZXJyeSBCb2Vzc2Vua29vbCoqCgojIyMgUmVhZCwgcGxvdCBhbmQgYWdncmVnYXRlIGRhdGEKCkRhdGFzZXRzIGZyb20gdGhlIFVLIE5hdGlvbmFsIFJpdmVyIEZsb3cgQXJjaGl2ZQo8aHR0cDovL25yZmEuY2VoLmFjLnVrL2RhdGEvc3RhdGlvbi9tZWFuZmxvdy8zOTA3Mj4gIApEb3dubG9hZCBbZGlzY2hhcmdlIGNzdl0oaHR0cHM6Ly9naXRodWIuY29tL2Jycnkvcmh5ZHJvL3RyZWUvbWFzdGVyL3ByZXNlbnRhdGlvbnMvZGF0YS9kaXNjaGFyZ2UzOTA3Mi5jc3YpCm9yIGJldHRlcjogW2Rvd25sb2FkIHRoZSB3aG9sZSBnaXRodWIgY291cnNlIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRyby9hcmNoaXZlL21hc3Rlci56aXApCgpSZWFkIGFuZCB0cmFuc2Zvcm0gZGF0YQpgYGB7cn0KUSA8LSByZWFkLnRhYmxlKCJkYXRhL2Rpc2NoYXJnZTM5MDcyLmNzdiIsIHNraXA9MTksIGhlYWRlcj1UUlVFLCBzZXA9IiwiLCBmaWxsPVRSVUUpWywxOjJdCmNvbG5hbWVzKFEpIDwtIGMoImRhdGUiLCJkaXNjaGFyZ2UiKQpRJGRhdGUgPC0gYXMuRGF0ZShRJGRhdGUsIGZvcm1hdD0iJVktJW0tJWQiKQpgYGAKCkV4YW1pbmUgZGF0YQpgYGB7cn0KaGVhZChRKQpgYGAKCmBgYHtyfQpzdHIoUSkKYGBgCgpTaW1wbGUgdGltZSBzZXJpZXMgcGxvdApgYGB7cn0KcGxvdChRLCB0eXBlPSJsIiwgY29sPSJibHVlIikKYGBgCgpQdWJsaWNhdGlvbi1yZWFkeSBncmFwaGljcwpgYGB7cn0KcG5nKCJEaXNjaGFyZ2VWaXMucG5nIiwgd2lkdGg9MjAsIGhlaWdodD0xMCwgdW5pdHM9ImNtIiwgcmVzPTUwMCkKI3BkZigiRGlzY2hhcmdlVmlzLnBkZiIsIHdpZHRoPTIwLzIuNSwgaGVpZ2h0PTEwLzIuNSkgIyB2ZWN0b3IgZ3JhcGgKcGFyKG1hcj1jKDMuNSwzLjUsMi41LDAuMiksIG1ncD1jKDIuMywwLjcsMCksIGxhcz0xKQpwbG90KFEsIHR5cGU9ImwiLCBjb2w9ImJsdWUiLCBtYWluPSJOUkZBOiBUaGFtZXNcblJveWFsIFdpbmRzb3IgUGFyayIsCiAgICAgeGxhYj0iRGF0ZSIsIHlsYWI9IkRpc2NoYXJnZSAgW21cVXswMEIzfS9zXSIpCmRldi5vZmYoKQpgYGAKCkFubnVhbCBtYXhpbWEsIEdlcm1hbiBoeWRyb2xvZ2ljYWwgeWVhciBzcGxpdCBhdCBPY3QgMzEKYGBge3J9ClEkaHlkeWVhciA8LSBhcy5udW1lcmljKGZvcm1hdChRJGRhdGUrNjEsICIlWSIpKQphbm5tYXggPC0gdGFwcGx5KFEkZGlzY2hhcmdlLCBRJGh5ZHllYXIsIG1heCwgbmEucm09VFJVRSkKYW5ubWF4IDwtIGFubm1heFstMV0KaHlkeWVhciA8LSBhcy5udW1lcmljKG5hbWVzKGFubm1heCkpIApwbG90KGh5ZHllYXIsIGFubm1heCwgdHlwZT0ibyIsIGxhcz0xKQpgYGAKCiMjIyBFeHRyZW1lIHZhbHVlIHN0YXRpc3RpY3MKCmBgYHtyfQpsaWJyYXJ5KGV4dHJlbWVTdGF0KQpkbGYgPC0gZGlzdExmaXQoYW5ubWF4KQpwbG90TGZpdChkbGYpCnBsb3RMZml0KGRsZiwgY2RmPVRSVUUpCmRsZSA8LSBkaXN0TGV4dHJlbWUoZGxmPWRsZiwgUlBzPWMoNSwxMCw1MCwxMDApLCBncGQ9RkFMU0UpCnBsb3RMZXh0cmVtZShkbGUpCmBgYAoKTG9nYXJpdGhtaWMgcGxvdCB3aXRoIG1hbnkgZml0dGVkIGRpc3RyaWJ1dGlvbiBmdW5jdGlvbnMKYGBge3J9CnBsb3RMZXh0cmVtZShkbGUsIG5iZXN0PTE2LCBsb2c9VFJVRSkKYGBgCgpSZXR1cm4gdmFsdWVzIChkaXNjaGFyZ2UgZXN0aW1hdGVzIGZvciBnaXZlbiByZXR1cm4gcGVyaW9kcykKYGBge3J9CmRsZSRyZXR1cm5sZXYKYGBgCkluIHJlYWxpdHksIHBsZWFzZSB1c2Ugbm9uLXN0YXRpb25hcnkgRVZTIQoKVW5jZXJ0YWludHkgYmFuZCBmb3IgV2FrZWJ5IGRpc3RyaWJ1dGlvbiBmaXQgZXN0aW1hdGUKYGBge3J9CmRsZV9ib290IDwtIGRpc3RMZXhCb290KGRsZSwgbj0xMCwgbmJlc3Q9MSkKcGxvdExleEJvb3QoZGxlX2Jvb3QsIGRpc3Rjb2w9ImdyZWVuIikKYGBgCgoKTW9yZSBkZXRhaWxzIGluIHRoZSBwYWNrYWdlIHZpZ25ldHRlCmBgYHtyLCBldmFsPUZBTFNFfQp2aWduZXR0ZSgiZXh0cmVtZVN0YXQiKQpgYGAKCgojIyMgQW5pbWF0ZWQgbW92aWUKCkRvd25sb2FkIGRhdGEgZnJvbSBzZXZlcmFsIGRpc2NoYXJnZSBzdGF0aW9ucwoKPGh0dHA6Ly9ucmZhLmNlaC5hYy51ay9kYXRhL3NlYXJjaD4gRmlsdGVyOiBSaXZlciA9IFRoYW1lcyAgCjxodHRwOi8vZW52aXJvbm1lbnQuZGF0YS5nb3YudWsvZmxvb2QtbW9uaXRvcmluZy9kb2MvcmVmZXJlbmNlI3N0YXRpb25zPiBmb3IgbGF0LWxvbiBjb29yZGluYXRlczoKCmBgYHtyLCBldmFsPUZBTFNFfQp1cmwgPC0gImh0dHA6Ly9lbnZpcm9ubWVudC5kYXRhLmdvdi51ay9mbG9vZC1tb25pdG9yaW5nL2lkL3N0YXRpb25zP3JpdmVyTmFtZT1UaGFtZXMiCmpzb24gPC0ganNvbmxpdGU6OmZyb21KU09OKHVybCkKc3RyKGpzb24kaXRlbXMsIG1heC5sZXZlbD0xKQptZXRhanNvbiA8LSBqc29uJGl0ZW1zWyxjKCJsYWJlbCIsImxhdCIsImxvbmciLCJkYXRlT3BlbmVkIildCmBgYAoKYGBge3J9Cm1ldGEgPC0gcmVhZC50YWJsZShoZWFkZXI9VCwgYXMuaXM9VCwgc2VwPSIsIiwgdGV4dD0iCm5hbWUgICAgICAgICAgICAgICAgLCBsYXQsIGxvbgpLaW5nc3RvbiAgICAgICAgICAgICwgNTEuNDE1MDA1LC0wLjMwODg2OQpEYXlzX1dlaXIgICAgICAgICAgICwgNTEuNjM4MjA2LC0xLjE3OTQ0NApFeW5zaGFtICAgICAgICAgICAgICwgNTEuNzc0NjkyLC0xLjM1Njg1NApXZXN0X01pbGxfQ3JpY2tsYWRlICwgNTEuNjQ2Njk0LC0xLjg2NTUzNgpFd2VuICAgICAgICAgICAgICAgICwgNTEuNjc0NzMzLC0xLjk5MDQKUm95YWxfV2luZHNvcl9QYXJrICAsIDUxLjQ4NTc5NSwtMC41ODkxMjQKUmVhZGluZyAgICAgICAgICAgICAsIDUxLjQ2MTMyNSwtMC45Njc4ODQKIikKI2xpYnJhcnkobGVhZmxldCkKI2xlYWZsZXQobWV0YSkgJT4lIGFkZFRpbGVzKCkgJT4lIGFkZENpcmNsZU1hcmtlcnMofmxvbiwgfmxhdCwgcG9wdXA9fm5hbWUpCiNtYXBfdGhhbWVzIDwtIE9TTXNjYWxlOjpwb2ludHNNYXAobGF0LGxvbixtZXRhLCBmeD0xLCBmeT0wLjIsIHBsb3Q9RkFMU0UsIHpvb209NiwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPSJtYXB0b29sa2l0LXRvcG8iLCBxdWlldD1UUlVFKQpgYGAKClJlYWQgZGF0YXNldHMKCmBgYHtyfQpmaWxlcyA8LSBkaXIoImRhdGEiLCBwYXR0ZXJuPSJeVGhhbWVzXyIsIGZ1bGw9VFJVRSkKdGhhbWVzIDwtIGxhcHBseShmaWxlcywgZnVuY3Rpb24oZikgewogZGlzIDwtIHJlYWQudGFibGUoZiwgc2tpcD0xOSwgaGVhZGVyPVRSVUUsIHNlcD0iLCIsIGZpbGw9VFJVRSlbLDE6Ml0KIG5hbWUgPC0gcmVhZExpbmVzKGYsIG49NSlbNV0KIG5hbWUgPC0gc3ViKCJzdGF0aW9uLG5hbWUsVGhhbWVzIGF0ICIsICIiLCBuYW1lKQogbmFtZSA8LSBnc3ViKCIgIiwgIl8iLCBuYW1lKQogY29sbmFtZXMoZGlzKSA8LSBjKCJkYXRlIixuYW1lKQogZGlzJGRhdGUgPC0gYXMuRGF0ZShkaXMkZGF0ZSwgZm9ybWF0PSIlWS0lbS0lZCIpCiBkaXMKfSkKcm0oZmlsZXMpCmBgYAoKTWVyZ2UgZGF0YXNldHMKYGBge3J9CmRpcyA8LSBSZWR1Y2UoZnVuY3Rpb24oLi4uKSBtZXJnZSguLi4sIGFsbD1UKSwgdGhhbWVzKQpgYGAKCkNvZGUgdG8gZ2VuZXJhdGUgb25lIG1vdmllIHNsaWNlCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KGJlcnJ5RnVuY3Rpb25zKSAjIGZvciBsaW0wLCBtb250aEF4aXMsIHRleHRGaWVsZCwgZXRjCgpzY2VuZSA8LSBmdW5jdGlvbihpLCB2bGNub3RlPVRSVUUsIG1hcD1UUlVFLCBjZXg9MS4yKQp7CiBzZWwgPC0gMDoxMjAKIGRpczIgPC0gZGlzW2kgKyBzZWwsIF0KIHN0YXQgPC0gY29sbmFtZXMoZGlzKVstMV0KIGNvbCA8LSBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmNvbChkaXMpLTEsIG5hbWU9IlNldDEiKQogbmFtZXMoY29sKSA8LSBzdGF0CiBwbG90KGRpczIkZGF0ZSxkaXMyWywyXSwgdHlwZT0ibiIsIHlsaW09bGltMCgzMDApLCAgbGFzPTEsIAogICAgICB4YXh0PSJuIiwgeWF4dD0ibiIsIGNleC5sYWI9Y2V4LCB4bGFiPSIiLCAKICAgICAgeWxhYj0iRGlzY2hhcmdlICBbbVxVezAwQjN9L3NdIiwgeGF4cz0iaSIpCiBheGlzKDIsIGNleC5heGlzPWNleCwgbGFzPTEpCiBTeXMuc2V0bG9jYWxlKCJMQ19USU1FIiwgIkMiKQogbW9udGhBeGlzKG1pZG1vbnRoPVRSVUUsIGZvcm1hdD0iJWJcbiVZIiwgY2V4PWNleCwgbWdwPWMoMywzLjUsMCkpCiBhYmxpbmUoaD0xOjYqMTAwLCB2PWRpczIkZGF0ZVtmb3JtYXQoZGlzMiRkYXRlLCIlZCIpPT0iMDEiXSwgY29sPTgpCiBmb3IocyBpbiBzdGF0KSBsaW5lcyhkaXMyJGRhdGUsIGRpczJbLHNdLCBsd2Q9NCwgY29sPWNvbFtzXSkKIHhpIDwtIHNlcVIoc2VsLGxlbj1sZW5ndGgoc3RhdCkrMilbLTFdCiB4aSA8LSBoZWFkKHhpLCAtMSkKIHRleHRGaWVsZChkaXMyJGRhdGVbeGldLCBkaWFnKGFzLm1hdHJpeChkaXMyW3hpLHN0YXRdKSksIHN0YXQsIGNleD1jZXgsIGNvbD1jb2wpCiBib3goKQogaWYobWFwKSBiZXJyeUZ1bmN0aW9uczo6c21hbGxQbG90KAogICAgewogICAgT3BlblN0cmVldE1hcDo6cGxvdC5PcGVuU3RyZWV0TWFwKG1hcF90aGFtZXMsIHJlbW92ZU1hcmdpbj1GQUxTRSkKICAgIHB0cyA8LSBPU01zY2FsZTo6cHJvamVjdFBvaW50cyhsYXQsbG9uLG1ldGEsIAogICAgICAgICAgIHRvPW1hcF90aGFtZXMkdGlsZXNbWzFdXSRwcm9qZWN0aW9uKQogICBwb2ludHMocHRzLCBwY2g9MywgY2V4PTQsIGx3ZD01LCBjb2w9Y29sKQogICB9LAogICBtYXI9MCwgYmc9TkEsIGJvcmRlcj1OQSwgeDE9MCwgeDI9MC41LCB5MT0wLjgsIHkyPTEpCiBpZih2bGNub3RlKSBtdGV4dCgiVkxDOiAnZSc6IHNpbmdsZSBmcmFtZSBmb3J3YXJkXG4nU0hJRlQrTEVGVCc6IGZldyBzZWNvbmRzIGJhY2siLAogICAgICAgICAgICAgICAgICAgc2lkZT0zLCBsaW5lPS05LCBvdXRlcj1UUlVFLCBhZGo9MC45NSwgY2V4PWNleCkKfQoKcGFyKG1hcj1jKDUsNSwwLjUsMC41KSwgbWdwPWMoMywwLjcsMCkpCnNjZW5lKDQ3MjAwKQpgYGAKCkFjdHVhbCBtb3ZpZQpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShhbmltYXRpb24pIDsgbGlicmFyeShwYmFwcGx5KQpzYXZlVmlkZW8oe3BhcihtYXI9Yyg2LDgsMSwxKSwgbWdwPWMoNS41LDAuNywwKSkKIGR1bW15IDwtIHBic2FwcGx5KHNlcSg0NzAwMCwgYnk9MywgbGVuPTEwMCksIHNjZW5lLCBjZXg9Mik7IHJtKGR1bW15KQp9LCB2aWRlby5uYW1lPSJRbW92aWUubXA0IiwgaW50ZXJ2YWw9MC4xLCBmZm1wZWc9Ii91c3IvYmluL2ZmbXBlZyIsIAphbmkud2lkdGg9NyoyMDAsIGFuaS5oZWlnaHQ9NSoyMDApCmBgYAoKPGEgaHJlZj0iUW1vdmllLm1wNCI+T3BlbiB2aWRlbyB3aXRoaW4gYnJvd3NlcjwvYT4KClwKW3RvcF0oI3RvcCkKCiMgSHlkbW9kCkh5ZHJvbG9naWNhbCBtb2RlbGxpbmcgd2l0aCBgYWlyR1JgICAgCioqS2F0aWUgU21pdGgqKiBDZW50cmUgZm9yIEVjb2xvZ3kgJiBIeWRyb2xvZ3kgKDxrLmEuc21pdGhAY2VoLmFjLnVrPikKClRoaXMgaXMgYW4gZGVtb25zdHJhdGlvbiBvZiBob3cgdG8gdXNlIHRoZSBhaXJHUiBwYWNrYWdlIG9mIGh5ZHJvbG9naWNhbCBtb2RlbHMgaW4gUiwgYXMgd2VsbCBhcyBob3cgdG8gcGxvdCBpbnRlcmFjdGl2ZSB0aW1lc2VyaWVzIGdyYXBocyB3aXRoIHRoZSBkeWdyYXBocyBwYWNrYWdlLgoKRmlyc3Qgd2UgbmVlZCB0byBsb2FkIHNvbWUgcGFja2FnZXMKYGBge3J9CmxpYnJhcnkoeHRzKQpsaWJyYXJ5KGR5Z3JhcGhzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKYGBgCgojIyMgRGF0YQoKTm93IHdlJ2xsIGxvYWQgaW4gc29tZSBvYnNlcnZhdGlvbmFsIGZsb3cgZGF0YSBmcm9tIHRoZSBSaXZlciBUaGFtZXMgKG5hdHVyYWxpc2VkKSBpbiBFbmdsYW5kIC0gd2l0aCB0aGFua3MgdG8gdGhlIE5hdGlvbmFsIFJpdmVyIEZsb3cgQXJjaGl2ZTogaHR0cDovL25yZmEuY2VoLmFjLnVrL2RhdGEvc2VhcmNoCmBgYHtyfQpvYnNlcnZlZF9kYXRhIDwtIHJlYWQuY3N2KCJkYXRhL1FvYnNfMzkwMDEwLmNzdiIpCmhlYWQob2JzZXJ2ZWRfZGF0YSkKb2JzZXJ2ZWRfZGF0YSREQVRFIDwtIHN0cnB0aW1lKG9ic2VydmVkX2RhdGEkREFURSwgZm9ybWF0ID0gIiVkLyVtLyVZIikKYGBgCgpJbiBvcmRlciB0byBwbG90IHRoaXMgYXMgYW4gaW50ZXJhY3RpdmUgZHlncmFwaCB3ZSBuZWVkIHRvIGNoYW5nZSBpdCB0byB4dHMgZm9ybWF0CmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQpvYnNlcnZlZF9kYXRhX3h0cyA8LSBhcy54dHMob2JzZXJ2ZWRfZGF0YSRRb2JzLCBvcmRlci5ieSA9IG9ic2VydmVkX2RhdGEkREFURSkKZHlncmFwaChvYnNlcnZlZF9kYXRhX3h0cywgbWFpbj0iTmF0dXJhbGlzZWQgU3RyZWFtZmxvdyBPYnNlcnZhdGlvbnMgZm9yIHRoZSBUaGFtZXMgYXQgS2luZ3N0b24iKSU+JQogIGR5QXhpcygieSIsIGxhYmVsPSJTdHJlYW1mbG93IChtMy9zIiklPiUKICBkeU9wdGlvbnMoY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsICJTZXQxIilbMl0pJT4lCiAgZHlSYW5nZVNlbGVjdG9yKCkKYGBgCk5vdyBsZXRzIHJlYWQgaW4gc29tZSBwcmVjaXBpdGF0aW9uIGRhdGEgLSB0aGlzIGlzIGZyb20gQ0VILUdFQVI6IGh0dHBzOi8vZGF0YS5nb3YudWsvZGF0YXNldC9ncmlkZGVkLWVzdGltYXRlcy1vZi1kYWlseS1hbmQtbW9udGhseS1hcmVhbC1yYWluZmFsbC1mb3ItdGhlLXVuaXRlZC1raW5nZG9tLTE4OTAtMjAxMi1jZWgtZ2VhcgoKYGBge3J9CnByZWNpcF9kYXRhIDwtIHJlYWQuY3N2KCJkYXRhL3JhaW5fMTk2MV8yMDE0XzM5MDAxMC5jc3YiKQpoZWFkKHByZWNpcF9kYXRhKQpgYGAKYW5kIHNvbWUgcG90ZW50aWFsIGV2YXBvdHJhbnNwaXJhdGlvbiBkYXRhIC0gdGhpcyBpcyBmcm9tIENIRVNTLVBFOmh0dHBzOi8vZGF0YS5nb3YudWsvZGF0YXNldC9jbGltYXRlLWh5ZHJvbG9neS1hbmQtZWNvbG9neS1yZXNlYXJjaC1zdXBwb3J0LXN5c3RlbS1wb3RlbnRpYWwtZXZhcG90cmFuc3BpcmF0aW9uLWRhdGFzZXQtZm9yLTEKCmBgYHtyfQpQRVRfZGF0YSA8LSByZWFkLmNzdigiZGF0YS9DSEVTU19QRVRfMTk2MV8yMDE1XzM5MDAxMC5jc3YiKQpoZWFkKFBFVF9kYXRhKQpgYGAKCk5vdGUgdGhhdCBvdXIgc3RhcnRpbmcgZGF0ZXMgYXJlIG5vdCB0aGUgc2FtZSBhcyBvdXIgb2JzZXJ2YXRpb25hbCBkYXRhLCBzbyB3ZSBuZWVkIHRvIG1ha2UgYSBkYXRhZnJhbWUgdGhhdCBtYXRjaGVzIHRoZSBkYXRlcyB1cC4gVGhlcmUgYXJlIGEgbG90IG9mIHdheXMgdG8gZG8gdGhpcy4gRmlyc3Qgd2UnbGwgY29udmVydCB0aGVtIHRvIHRoZSBzYW1lIGRhdGUgZm9ybWF0LgpgYGB7cn0KcHJlY2lwX2RhdGEkREFURSA8LSBzdHJwdGltZShwcmVjaXBfZGF0YSREQVRFLCAiJVktJW0tJWQiKQpQRVRfZGF0YSREQVRFIDwtIHN0cnB0aW1lKFBFVF9kYXRhJERBVEUsICIlWS0lbS0lZCIpCmBgYApub3cgd2UnbGwgZmluZCB0aGUgY29tbW9uIHBlcmlvZApgYGB7cn0KZmlyc3RfZGF0ZSA8LSBtYXgob2JzZXJ2ZWRfZGF0YSREQVRFWzFdLCBwcmVjaXBfZGF0YSREQVRFWzFdLCBQRVRfZGF0YSREQVRFWzFdKQpsYXN0X2RhdGUgPC0gbWluKG9ic2VydmVkX2RhdGEkREFURVtsZW5ndGgob2JzZXJ2ZWRfZGF0YSREQVRFKV0sIHByZWNpcF9kYXRhJERBVEVbbGVuZ3RoKHByZWNpcF9kYXRhJERBVEUpXSwgUEVUX2RhdGEkREFURVtsZW5ndGgoUEVUX2RhdGEkREFURSldKQpgYGAKYW5kIG1ha2UgYSBkYXRhIGZyYW1lIG9mIHRoYXQgbGVuZ3RoCmBgYHtyfQojIG1ha2UgYW4gZW1wdHkgZGF0YSBmcmFtZQp0aGFtZXNfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKG1hdHJpeChOQSxucm93PWFzLm51bWVyaWMoKGxhc3RfZGF0ZS1maXJzdF9kYXRlKSsxKSwgbmNvbD00KSkKY29sbmFtZXModGhhbWVzX2RhdGEpIDwtYyAoImRhdGUiLCJQRVQiLCJwcmVjaXAiLCJvYnMiKQojIG1ha2UgdGhlIGRhdGUgdGltZXNlcmllcwp0aGFtZXNfZGF0YSRkYXRlIDwtIHNlcShmaXJzdF9kYXRlLCBsYXN0X2RhdGUsIGJ5PSJkYXlzIikKIyBwb3B1bGF0ZSB0aGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBkYXRhCnRoYW1lc19kYXRhJG9icyA8LSBvYnNlcnZlZF9kYXRhJFFvYnNbd2hpY2gob2JzZXJ2ZWRfZGF0YSREQVRFPT10aGFtZXNfZGF0YSRkYXRlWzFdKTp3aGljaChvYnNlcnZlZF9kYXRhJERBVEU9PXRoYW1lc19kYXRhJGRhdGVbbGVuZ3RoKHRoYW1lc19kYXRhJGRhdGUpXSldCnRoYW1lc19kYXRhJHByZWNpcCA8LSBwcmVjaXBfZGF0YSRNZWFuX3JhaW5mYWxsW3doaWNoKHByZWNpcF9kYXRhJERBVEU9PXRoYW1lc19kYXRhJGRhdGVbMV0pOndoaWNoKHByZWNpcF9kYXRhJERBVEU9PXRoYW1lc19kYXRhJGRhdGVbbGVuZ3RoKHRoYW1lc19kYXRhJGRhdGUpXSldCnRoYW1lc19kYXRhJFBFVCA8LSBQRVRfZGF0YSRQRVRbd2hpY2goUEVUX2RhdGEkREFURT09dGhhbWVzX2RhdGEkZGF0ZVsxXSk6d2hpY2goUEVUX2RhdGEkREFURT09dGhhbWVzX2RhdGEkZGF0ZVtsZW5ndGgodGhhbWVzX2RhdGEkZGF0ZSldKV0KYGBgCgojIyMgSW50ZXJhY3RpdmUgdGltZSBzZXJpZXMgcGxvdAoKcGxvdCB0aGUgb2JzZXJ2ZWQgc3RyZWFtZmxvdyB3aXRoIHRoZSBwcmVjaXBpdGF0aW9uIGRhdGEKYGBge3IsIGZpZy53aWR0aD03LCBmaWcuaGVpZ2h0PTV9CiMgIGNvbnZlcnQgdGhlIG9ic2VydmVkIGRpc2NoYXJnZSB0byBydW5vZmYgKHNvIGl0cyBpbiB0aGUgc2FtZSB1bml0cyBhcyB0aGUgcHJlY2lwKQojIGRpdmlkZSBieSBjYXRjaG1lbnQgYXJlYSAobTIpIGFuZCBtdWxpdHBseSBieSA4Ni40CnRoYW1lc19kYXRhJG9icyA8LSAodGhhbWVzX2RhdGEkb2JzLzk5NDguMCkqODYuNAp0aGFtZXNfZGF0YV94dHMgPC0gYXMueHRzKHRoYW1lc19kYXRhWywzOjRdLCBvcmRlci5ieT10aGFtZXNfZGF0YSRkYXRlKQojIGluaXRpYXRlIHRoZSBkeWdyYXBoCmR5Z3JhcGgodGhhbWVzX2RhdGFfeHRzKSU+JQojIGRlZmluZSB0aGUgZmlyc3QgYXhpcyAgCmR5QXhpcygib2JzIiwgbmFtZSA9ICJ5IiwgbGFiZWwgPSAicnVub2ZmIChtbS9kYXkpIiwKICAgICAgIHZhbHVlUmFuZ2UgPSByYW5nZSh0aGFtZXNfZGF0YV94dHNbLCAib2JzIl0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFKSogYygwLjAxLCAxLjU5KSklPiUKIyBkZWZpbmUgdGhlIHNlY29uZCBheGlzCmR5QXhpcygicHJlY2lwIiwgbmFtZSA9ICJ5MiIsIGxhYmVsID0gInByZWNpcCAobW0vZGF5KSIsCiAgICAgICAgICAgICAgICAgICB2YWx1ZVJhbmdlID0gcmV2KHJhbmdlKHRoYW1lc19kYXRhX3h0c1ssICJwcmVjaXAiXSwgCiAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKiBjKDAuMDEsIDIuOTkpKSklPiUKIyBwbG90IHRoZSBkYXRhCmR5U2VyaWVzKCJvYnMiLGF4aXMgPSAneScpJT4lCmR5U2VyaWVzKCJwcmVjaXAiLCBheGlzID0gJ3kyJywgc3RlcFBsb3QgPSBUUlVFLAogICAgICAgICBmaWxsR3JhcGggPSBUUlVFKSU+JQpkeU9wdGlvbnMoY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsICJTZXQxIilbMjozXSklPiUKZHlSYW5nZVNlbGVjdG9yKCkKYGBgCgojIyMgSHlkcm9sb2dpY2FsIG1vZGVsaW5nCgpPSywgZW5vdWdoIG1lc3Npbmcgd2l0aCBkYXRhLCBsZXRzIGRvIHNvbWUgbW9kZWxsaW5nLgpXZSBhcmUgdXNpbmcgdGhlIEdSNEogbW9kZWwsIGZyb20gdGhlIGFpckdSIHBhY2thZ2UuIApUaGlzIHdhcyBkZXZlbG9wZWQgYnkgSVJTVEVBLCBGcmFuY2UuIApTZWUgUGVycmluIGV0IGFsLiAoMjAwMykKCjxpbWcgc3JjPSJkYXRhL0lNR18yMDAxLlBORyIgYWx0PSIiIHN0eWxlPSJ3aWR0aDogNjAwcHg7Ii8+CgoKU2VlIHRoaXMgd2Vic2l0ZSBmb3IgYSBnb29kIGd1aWRlIHRocm91Z2ggdGhlIG1vZGVsOiAKPGh0dHBzOi8vb2RlbGFpZ3VlLmdpdGh1Yi5pby9haXJHUi90dXRvcmlhbF8xX2dldHRpbmdfc3RhcnRlZC5odG1sPgoKbG9hZCB0aGUgR1IgcGFja2FnZQpgYGB7cn0KcmVxdWlyZShhaXJHUiwgcXVpZXRseT1UUlVFKQpgYGAKCnByZXBhcmUgdGhlIGlucHV0IGRhdGEgaW4gdGhlIGNvcnJlY3QgZm9ybWF0CmBgYHtyfQpCYXNpbk9icyA8LSB0aGFtZXNfZGF0YQpjb2xuYW1lcyhCYXNpbk9icykgPC0gYygnRGF0ZXNSJywnRScsJ1AnLCAnUW9icycpCmBgYAoKY3JlYXRlIHRoZSBJbnB1dHNNb2RlbCBvYmplY3QgLSB0aGlzIGRlZmluZXMgd2hpY2ggbW9kZWwgd2Ugd2FudCB0byBydW4sIGFuZCBkZWZpbmVzIHRoZSB2YXJpYWJsZXMgZm9yIHRoZSBtb2RlbHMgaW5wdXQgZGF0YQoKYGBge3J9CklucHV0c01vZGVsIDwtIENyZWF0ZUlucHV0c01vZGVsKEZVTl9NT0QgPSBSdW5Nb2RlbF9HUjRKLERhdGVzUiA9IEJhc2luT2JzJERhdGVzUiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQcmVjaXAgPSBCYXNpbk9icyRQLFBvdEV2YXAgPSBCYXNpbk9icyRFKQpzdHIoSW5wdXRzTW9kZWwpCiMgbm90ZSBOQSB2YWx1ZXMgb2YgcHJlY2lwIGFuZCBQRVQgYXJlIE5PVCBBTExPV0VECmBgYAoKY3JlYXRlIHRoZSBSdW5PcHRpb25zIG9iamVjdCAtIHRoaXMgZGVmaW5lcyBvcHRpb25zIGZvciB0aGUgUnVuTW9kZWxfR1I0SiBmdW5jdGlvbgpgYGB7cn0KIyBmaXJzdCBkZWZpbmUgdGhlIHBlcmlvZCB0byBydW4gdGhlIG1vZGVsIG92ZXIKSW5kX1J1biA8LSBzZXEod2hpY2goQmFzaW5PYnMkRGF0ZXNSPT0iMTk4MS0wMS0wMSIpLAogICAgICAgICAgICAgd2hpY2goQmFzaW5PYnMkRGF0ZXNSPT0iMjAxNC0xMi0zMSIpKQpSdW5PcHRpb25zIDwtIENyZWF0ZVJ1bk9wdGlvbnMoRlVOX01PRCA9IFJ1bk1vZGVsX0dSNEosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5kUGVyaW9kX1J1biA9IEluZF9SdW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmRQZXJpb2RfV2FybVVwID0gTlVMTCkKc3RyKFJ1bk9wdGlvbnMpCmBgYAoKY3JlYXRlIHRoZSBJbnB1dHNDcml0IG9iamVjdCAtIGRlZmluZSB0aGUgZXJyb3IgbWV0cmljIChjaG9vc2UgZnJvbSBSTVNFLCBOU0UsIEtHRSBvciBtb2RpZmllZCBLR0UgKEtHRTIpKQpgYGB7cn0KSW5wdXRzQ3JpdCA8LSBDcmVhdGVJbnB1dHNDcml0KEZVTl9DUklUID0gRXJyb3JDcml0X05TRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIElucHV0c01vZGVsID0gSW5wdXRzTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFFvYnMgPSBCYXNpbk9icyRRb2JzW0luZF9SdW5dKQpzdHIoSW5wdXRzQ3JpdCkKYGBgCgpjcmVhdGUgdGhlIENhbGliT3B0aW9ucyBvYmplY3QgLSBjaG9vc2UgdGhlIGNhbGlicmF0aW9uIGFsZ29yaXRobQpgYGB7cn0KQ2FsaWJPcHRpb25zIDwtIENyZWF0ZUNhbGliT3B0aW9ucyhGVU5fTU9EID0gUnVuTW9kZWxfR1I0SiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGVU5fQ0FMSUIgPSBDYWxpYnJhdGlvbl9NaWNoZWwpCnN0cihDYWxpYk9wdGlvbnMpCmBgYAoKcnVuIHRoZSBjYWxpYnJhdGlvbgpgYGB7cn0KT3V0cHV0c0NhbGliIDwtIENhbGlicmF0aW9uX01pY2hlbChJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnB1dHNDcml0ID0gSW5wdXRzQ3JpdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDYWxpYk9wdGlvbnMgPSBDYWxpYk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOX01PRCA9IFJ1bk1vZGVsX0dSNEosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOX0NSSVQgPSBFcnJvckNyaXRfTlNFKQpgYGAKCk5TRSBvZiAwLjkyNDYgLSBub3QgYmFkIGF0IGFsbCEKCmRlZmluZSB0aGUgcGFyYW1ldGVycyBmb3VuZCBieSB0aGUgY2FsaWJyYXRpb24gcm91dGluZQpgYGB7cn0KUGFyYW0gPC0gT3V0cHV0c0NhbGliJFBhcmFtRmluYWxSClBhcmFtCmBgYAoKKipSVU4gVEhFIE1PREVMISoqCmBgYHtyfQpPdXRwdXRzTW9kZWwgPC0gUnVuTW9kZWxfR1I0SihJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ1bk9wdGlvbnMgPSBSdW5PcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBhcmFtPSBQYXJhbSkKc3RyKE91dHB1dHNNb2RlbCkKYGBgCgp1c2UgdGhlIGluYnVpbHQgcGxvdCBmdW5jdGlvbiB0byBsb29rIGF0IHRoZSByZXN1bHRzCmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQpwbG90KE91dHB1dHNNb2RlbCwgUW9icz1CYXNpbk9icyRRb2JzW0luZF9SdW5dKQpgYGAKCmxvb2tpbmcgZ29vZCAtIGJ1dCB3ZSd2ZSBnb3Qgc29tZSBkaXNjcmVwYW5jeSBhdCB0aGUgbG93IGZsb3dzIGVuZC4gTlNFIGlzIG5vdG9yaW91cyBmb3IgdGhpcywgaXQgaXMgYmFzZWQgb24gdGhlIHNxdWFyZSBvZiB0aGUgZmxvd3MsIHNvIG92ZXItd2VpZ2h0cyB0aGUgY2FsaWJyYXRpb24gdG8gdGhlIGhpZ2ggZmxvd3MuIEkgd29uZGVyIGlmIHRoZSBtb2RpZmllZCBLR0UgY2FuIGRvIGFueSBiZXR0ZXI/CmBgYHtyLCBmaWcud2lkdGg9NywgZmlnLmhlaWdodD01fQojIG1ha2UgYSBmZXcgY2hhbmdlcyB0byB0aGUgY2FsaWJyYXRpb24gY3JpdGVyaWEKSW5wdXRzQ3JpdCA8LSBDcmVhdGVJbnB1dHNDcml0KEZVTl9DUklUID0gRXJyb3JDcml0X0tHRTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuT3B0aW9ucyA9IFJ1bk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBRb2JzID0gQmFzaW5PYnMkUW9ic1tJbmRfUnVuXSkKIyByZXJ1biB0aGUgY2FsaWJyYXRpb24KT3V0cHV0c0NhbGliIDwtIENhbGlicmF0aW9uX01pY2hlbChJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnB1dHNDcml0ID0gSW5wdXRzQ3JpdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDYWxpYk9wdGlvbnMgPSBDYWxpYk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOX01PRCA9IFJ1bk1vZGVsX0dSNEosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRlVOX0NSSVQgPSBFcnJvckNyaXRfS0dFMikKIyByZWRlZmluZSB0aGUgcGFyYW1ldGVycwpQYXJhbSA8LSBPdXRwdXRzQ2FsaWIkUGFyYW1GaW5hbFIKIyByZXJ1biB0aGUgbW9kZWwKT3V0cHV0c01vZGVsIDwtIFJ1bk1vZGVsX0dSNEooSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdW5PcHRpb25zID0gUnVuT3B0aW9ucywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQYXJhbT0gUGFyYW0pCiMgcGxvdCBhZ2FpbgpwbG90KE91dHB1dHNNb2RlbCwgUW9icz1CYXNpbk9icyRRb2JzW0luZF9SdW5dKQpgYGAKCm5vdCBtdWNoIGRpZmZlcmVudC4gT2ggd2VsbCwgd2UgY2FuIGJlIGhhcHB5IHdpdGggZWl0aGVyIG9mIHRob3NlIG1ldHJpYyBzY29yZXMuIC0gcGF1c2UgZm9yIHRob3VnaHQgLSB3aGljaCBwYXJhbWV0ZXIgc2V0IHdvdWxkIHlvdSBjaG9vc2UgdG8gdXNlPyEKCiMjIyBWYWxpZGF0aW9uCgpMZXQncyBkbyBzb21lIHZhbGlkYXRpb24KYGBge3J9CiMgZ28gYmFjayB0byB0aGUgYmVnaW5uaW5nLCByZWRlZmluZSB0aGUgcGVyaW9kIHRvIHJ1biBvbiAodGhlIHBlcmlvZCB3ZSBoYXZlbid0IHVzZWQgZm9yIGNhbGlicmF0aW9uLCBtaW51cyB0aGUgZmlyc3QgeWVhciBuZWVkZWQgZm9yIHdhcm0gdXApCkluZF9SdW4gPC0gc2VxKHdoaWNoKEJhc2luT2JzJERhdGVzUj09IjE5NjItMDEtMDEiKSwKICAgICAgICAgICAgIHdoaWNoKEJhc2luT2JzJERhdGVzUj09IjE5ODAtMTItMzEiKSkKUnVuT3B0aW9ucyA8LSBDcmVhdGVSdW5PcHRpb25zKEZVTl9NT0QgPSBSdW5Nb2RlbF9HUjRKLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEluZFBlcmlvZF9SdW4gPSBJbmRfUnVuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5kUGVyaW9kX1dhcm1VcCA9IE5VTEwpCklucHV0c0NyaXQgPC0gQ3JlYXRlSW5wdXRzQ3JpdChGVU5fQ1JJVCA9IEVycm9yQ3JpdF9LR0UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5wdXRzTW9kZWwgPSBJbnB1dHNNb2RlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ1bk9wdGlvbnMgPSBSdW5PcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUW9icyA9IEJhc2luT2JzJFFvYnNbSW5kX1J1bl0pClBhcmFtIDwtIE91dHB1dHNDYWxpYiRQYXJhbUZpbmFsUgpPdXRwdXRzTW9kZWwgPC0gUnVuTW9kZWxfR1I0SihJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ1bk9wdGlvbnMgPSBSdW5PcHRpb25zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBhcmFtPSBQYXJhbSkKT3V0cHV0c0NyaXQgPC0gRXJyb3JDcml0X0tHRTIoSW5wdXRzQ3JpdCA9IElucHV0c0NyaXQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPdXRwdXRzTW9kZWwgPSBPdXRwdXRzTW9kZWwpCmBgYAoKc2xpZ2h0bHkgd29yc2UgdGhhbiB0aGUgY2FsaWJyYXRpb24gcGVyaW9kICgwLjk2MjEpIGJ1dCBub3QgYmFkIGF0IGFsbAoKZmluYWxseSwgbGV0J3MgcnVuIHRoZSBtb2RlbCBmb3IgdGhlIHdob2xlIHRpbWUgcGVyaW9kIGFuZCBwbG90IGEgZHlncmFwaCBzbyB3ZSBjYW4gbG9vayBhdCB0aGUgdGltZXNlcmllcyBpbiBtb3JlIGRldGFpbApgYGB7ciwgZmlnLndpZHRoPTcsIGZpZy5oZWlnaHQ9NX0KSW5kX1J1biA8LSBzZXEod2hpY2goQmFzaW5PYnMkRGF0ZXNSPT0iMTk2Mi0wMS0wMSIpLAogICAgICAgICAgICAgd2hpY2goQmFzaW5PYnMkRGF0ZXNSPT0iMjAxNC0xMi0zMSIpKQpSdW5PcHRpb25zIDwtIENyZWF0ZVJ1bk9wdGlvbnMoRlVOX01PRCA9IFJ1bk1vZGVsX0dSNEosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbnB1dHNNb2RlbCA9IElucHV0c01vZGVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSW5kUGVyaW9kX1J1biA9IEluZF9SdW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbmRQZXJpb2RfV2FybVVwID0gTlVMTCkKUGFyYW0gPC0gT3V0cHV0c0NhbGliJFBhcmFtRmluYWxSCk91dHB1dHNNb2RlbCA8LSBSdW5Nb2RlbF9HUjRKKElucHV0c01vZGVsID0gSW5wdXRzTW9kZWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuT3B0aW9ucyA9IFJ1bk9wdGlvbnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGFyYW09IFBhcmFtKQpwbG90X291dHB1dF9kYXRhIDwtIGFzLmRhdGEuZnJhbWUobWF0cml4KE5BLCBuY29sID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbGVuZ3RoKE91dHB1dHNNb2RlbCREYXRlc1IpKSkKY29sbmFtZXMocGxvdF9vdXRwdXRfZGF0YSkgPC0gYygiRGF0ZSIsICJRc2ltIiwgIlFvYnMiKQpwbG90X291dHB1dF9kYXRhJERhdGUgPC0gT3V0cHV0c01vZGVsJERhdGVzUgpwbG90X291dHB1dF9kYXRhJFFzaW0gPC0gT3V0cHV0c01vZGVsJFFzaW0KcGxvdF9vdXRwdXRfZGF0YSRRb2JzIDwtIEJhc2luT2JzJFFvYnNbSW5kX1J1bl0KcGxvdF9vdXRwdXRfZGF0YV94dHMgPC0gYXMueHRzKHBsb3Rfb3V0cHV0X2RhdGEsIG9yZGVyLmJ5ID0gcGxvdF9vdXRwdXRfZGF0YSREYXRlKQpkeWdyYXBoKHBsb3Rfb3V0cHV0X2RhdGFfeHRzLCBtYWluPSJPYnNlcnZlZCBhbmQgU2ltdWxhdGVkIFJ1bm9mZiBmb3IgdGhlIFRoYW1lcyBhdCBLaW5nc3RvbiAoTmF0dXJhbGlzZWQpIiklPiUKICBkeU9wdGlvbnMoY29sb3JzID0gUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsIlNldDEiKVsyOjFdKSU+JQogIGR5QXhpcygieSIsIGxhYmVsPSJSdW5vZmYgKG1tL2RheSkiKSU+JQogIGR5UmFuZ2VTZWxlY3RvcigpCmBgYAoKVGhhbmtzIGZvciBsaXN0ZW5pbmchIEhvcGUgeW91IGdldCB0byB0cnkgaXQgb3V0LgoKQW55IGdlbmVyYWwgcXVlc3Rpb25zPyBQbGVhc2UgZmVlbCBmcmVlIHRvIHBvc3QgdGhlbSBvbiBmYWNlYm9vayBwYWdlOiBodHRwczovL3d3dy5mYWNlYm9vay5jb20vZ3JvdXBzLzExMzAyMTQ3NzcxMjM5MDkvP3JlZj1ib29rbWFya3MKCkdSIHNwZWNpZmljIHF1ZXN0aW9ucz8gRW1haWwgPGFpckdSQGlyc3RlYS5mcj4KClwKW3RvcF0oI3RvcCkKCiMgVHJlbmQKRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyBpbmNsdWRpbmcgZmxvdyBkdXJhdGlvbiBjdXJ2ZSBhbmQgdHJlbmQgYW5hbHlzaXMgb24gdGltZS1zZXJpZXMKKipTaGF1biBIYXJyaWdhbioqIENlbnRyZSBmb3IgRWNvbG9neSAmIEh5ZHJvbG9neSAoPHNoYXVoYXJAY2VoLmFjLnVrPikKCioqKgojIyAxLiBJbnRyb2R1Y3Rpb24KClRoZXJlIGlzIG5vICdvbmUgYmVzdCcgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UsIGVhY2ggaGFzIGluZGl2aWR1YWwgYmVuZWZpdHMuIFIncyBzdHJvbmcgcG9pbnQgaXMgaW1wb3J0aW5nIG1lc3N5ICdyZWFsLXdvcmxkJyBkYXRhLCBjbGVhbmluZyBpdCB1cCwgYW5kIGV4cGxvcmluZyBpdCB3aXRoIHBvd2VyZnVsIHN0YXRpc3RpY2FsIHRvb2xzIQoKSW4gdGhpcyBzaG9ydCAnVHJlbmQgQW5hbHlzaXMgdGFzdGVyJyB3ZSdyZSBnb2luZyB0bzoKCiogKipSZWFkKiogYW5kIGNsZWFuIHVwIHN0cmVhbWZsb3cgZGF0YSBmcm9tIHRoZSBCb3luZSBjYXRjaG1lbnQgaW4gSXJlbGFuZAoqIFJ1biBhbiAqKkV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkqKgogICAgKyBGbG93IER1cmF0aW9uIEN1cnZlIChGREMpCiAgICArIENoZWNrIHN0YXRpc3RpY2FsIGFzc3VtcHRpb25zCiogKipQbG90KiogdGltZS1zZXJpZXMKKiBQZXJmb3JtIGEgKipNYW5uLUtlbmRhbGwqKiB0cmVuZCB0ZXN0IGZvciBldmlkZW5jZSBvZiBncmFkdWFsIGNoYW5nZQoqIFBlcmZvcm0gYSAqKlBldHRpdHQqKiBwb2ludCB0ZXN0IGZvciBldmlkZW5jZSBvZiBhYnJ1cHQgY2hhbmdlCgojIyMjIFRoZSBCb3luZSBDYXNlIFN0dWR5CgpXZSB3aWxsIHVzZSB0aGUgQm95bmUgY2F0Y2htZW50IGluIElyZWxhbmQgYXMgYSBjYXNlIHN0dWR5LiBUaGlzIGNhdGNobWVudCBoYXMgYSB3ZWxsIGtub3duIGFicnVwdCBjaGFuZ2UgaW4gaXRzIHRpbWUtc2VyaWVzIFsoSGFycmlnYW4gZXQgYWwuLCAyMDE0IEhFU1MpXShodHRwOi8vd3d3Lmh5ZHJvbC1lYXJ0aC1zeXN0LXNjaS5uZXQvMTgvMTkzNS8yMDE0LykgYW5kIHRoaXMgY2FzZSBzdHVkeSBub3cgZmVhdHVyZXMgaW4gWyJ0aGUgRGlydHkgRG96ZW4gb2YgZnJlc2h3YXRlciBzY2llbmNlIl0oaHR0cDovL29ubGluZWxpYnJhcnkud2lsZXkuY29tL2RvaS8xMC4xMDAyL3dhdDIuMTIwOS9mdWxsKSBieSBXaWxieSBldCBhbC4sIDIwMTcgV0lSRXM6IFdhdGVyLgoKIVtdKFNoYXVuL1RoZV9Cb3luZV9DYXNlU3R1ZHkuUE5HKQoKCgpPSywgbGV0cyBnbyBnZXQgdGhlIGRhdGEuLi4KCioqKgojIyAyLiBSZWFkIGluIGRhdGEKCkRhdGEgdGFrZW4gZnJvbSBIRVNTIHBhcGVyOgoKKiBPYnMgPSBPYnNlcnZlZCBkYWlseSBtZWFuIGZsb3cgKG0zL3MpIGRhdGEgZnJvbSAwMS8wMS8xOTUyIHRvIDMxLzEyLzIwMDkKKiBTaW0gPSBTaW11bGF0ZWQgZGFpbHkgbWVhbiBmbG93IChtMy9zKSBkYXRhIGZyb20gMDEvMDEvMTk1MiB0byAzMS8xMi8yMDA5IHVzaW5nIGVuc2VtYmxlIG1lYW4gYWNyb3NzIDMgaHlkcm9sb2dpY2FsIG1vZGVscyBhbmQgMzI4IHBhcmFtZXRlciBzZXRzCgpPYnNlcnZlZCBzdHJlYW1mbG93IGRhdGEgcHJvdmlkZWQgYnkgdGhlIFtPZmZpY2Ugb2YgUHVibGljIFdvcmtzIChPUFcpXShodHRwOi8vd2F0ZXJsZXZlbC5pZS9oeWRyby1kYXRhL2hvbWUuaHRtbCkgaW4gSXJlbGFuZC4gUGxlYXNlIHJlZmVyIHRvIHRoZWlyIFRzJkNzIGJlZm9yZSByZS11c2UuCgoqKkEgLmNzdiBvZiB0aGUgZGF0YSB1c2VkIGluIHRoaXMgdHV0b3JpYWwgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vYnJyeS9yaHlkcm8vbWFzdGVyL3ByZXNlbnRhdGlvbnMvZGF0YS9Cb3luZV9IRVNTX2RhdGFfRUdVMjAxNy5jc3YpLioqCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KCiMgTG9hZCByZXF1aXJlZCBwYWNrYWdlcwpsaWJyYXJ5KHRyZW5kKSAjIE5vbi1QYXJhbWV0cmljIHRyZW5kIHRlc3RzIGFuZCBjaGFuZ2UgcG9pbnQgZGV0ZWN0aW9uIChieSBUaG9yc3RlbiBQb2hsZXJ0KQpsaWJyYXJ5KGh5ZHJvVFNNKSAjIFRpbWUgc2VyaWVzIGFuYWx5c2lzIGZvciBoeWRyb2xvZ2ljYWwgbW9kZWxsaW5nIChieSBNYXVyaWNpbyBaYW1icmFubyBCaWdpYXJpbmkgKQoKIyBSZWFkIGRhdGEKbXlEYXRhIDwtIHJlYWQuY3N2KCJkYXRhL0JveW5lX0hFU1NfZGF0YV9FR1UyMDE3LmNzdiIpCgojIFRha2UgYSBwZWFrCmhlYWQobXlEYXRhKSAjIEZpc3QgNiByb3dzCnRhaWwobXlEYXRhKSAjIExhc3QgNiByb3dzCgojIFF1aWNrIGxpbmUgcGxvdCBvZiBPYnMgYW5kIFNpbSAodHlwZSA9ICdsJykKcGxvdChteURhdGEkT2JzLCB0eXBlID0gJ2wnKQpsaW5lcyhteURhdGEkU2ltLCBjb2wgPSAicmVkIikgIyAnbGluZXMnIHdpbGwgYWRkIGEgbGluZSB0byBhY3RpdmUgcGxvdCBkZXZpY2UKCgpgYGAKCioqKgojIyAzLiBGbG93IER1cmF0aW9uIEN1cnZlcyAoRkRDKQoKV2UgY2FuIGNyZWF0ZSBhIEZEQyBlYXNpbHkgdXNpbmcgdGhlIGBmZGNgIGZ1bmN0aW9uIGZyb20gdGhlICoqaHlkcm9UU00qKiBwYWNrYWdlLiBIZXJlIHdlIHdpbGwgaGF2ZSBhIGxvb2sgYXQgRkRDcyBmcm9tIGJvdGggb2JzZXJ2ZWQgYW5kIHNpbXVsYXRlZCBCb3luZSBzdHJlYW1mbG93IHNlcmllcywgcHJlIGFuZCBwb3N0IHRoZSBrbm93biBwZXJpb2Qgb2YgY2hhbm5lbCBkcmFpbmFnZSAoMTk2OS0xOTg2KToKCmBgYHtyLCBtZXNzYWdlPUZBTFNFfQoKIyBTdWJzZXQgZGF0YSBwcmUtIGFuZCBwb3N0LSBkcmFpbmFnZSBwZXJpb2QKcHJlRHJhaW4gPC0gc3Vic2V0KG15RGF0YSwgbXlEYXRhJFllYXIgPj0gMTk1MiAmIG15RGF0YSRZZWFyIDw9IDE5NjgpCnBvc3REcmFpbiA8LSBzdWJzZXQobXlEYXRhLCBteURhdGEkWWVhciA+PSAxOTg3ICYgbXlEYXRhJFllYXIgPD0gMjAwOSkKCiMgU2VsZWN0ICdPYnMnIGFuZCAnU2ltJyBjb2x1bW5zIGFuZCBjb252ZXJ0IHRvICdtYXRyaXgnIGNsYXNzIChyZXF1aXJlZCBpbiAnZmRjJyBmdW5jdGlvbikKcHJlRHJhaW5GREMgPC0gYXMubWF0cml4KHByZURyYWluWyAsYyg0LCA1KV0pCnBvc3REcmFpbkZEQyA8LSBhcy5tYXRyaXgocG9zdERyYWluWyAsYyg0LCA1KV0pCgojIFBsb3QgcHJlLWRyYWluYWdlIEZEQyBhbmQgcHJpbnQgZmlyc3QgMTAgdmFsdWVzCmZkYyhwcmVEcmFpbkZEQywgbWFpbiA9ICJGREMgUHJlLWRyYWluYWdlICgxOTUyLTE5NjgpIiwgdmVyYm9zZSA9IEZBTFNFKVtjKDE6MTApXQoKIyBQbG90IHBvc3QtZHJhaW5hZ2UgRkRDIGFuZCBwcmludCBmaXJzdCAxMCB2YWx1ZXMKZmRjKHBvc3REcmFpbkZEQywgbWFpbiA9ICJGREMgUG9zdC1kcmFpbmFnZSAoMTk4Ny0yMDA5KSIsIHZlcmJvc2UgPSBGQUxTRSlbMToxMF0KCmBgYAoKKioqCiMjIDQuIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMgKEVEQSkKCkVEQSBpcyB0aGUgZmlyc3Qgc3RlcCBpbiBhbnkgdHJlbmQgYW5hbHlzaXMsIGFuZCBpdHMgaW1wb3J0YW5jZSBpcyBvZnRlbiB1bmRlciBhcHByZWNpYXRlZC4gSXQgc2VydmVzIDMga2V5IHB1cnBvc2VzOgoKMS4gVmlzdWFsbHkgaWRlbnRpZnlpbmcgaW50ZXJlc3RpbmcgcGF0dGVybnMgaW4gdGhlIGRhdGEKMi4gRGV0ZWN0aW5nIGRhdGEgcXVhbGl0eSBwcm9ibGVtcyAobWlzc2luZyBkYXRhLCBvdXRsaWVycyBldGMuKQozLiBUZXN0aW5nIGtleSBhc3N1bXB0aW9ucyBmb3Igc3RhdGlzdGljYWwgdGVzdHMgdXNlZCBsYXRlciAoZS5nLiBOb3JtYWxpdHkgYW5kIHNlcmlhbCBjb3JyZWxhdGlvbikKCldlIHdpbGwgcnVuIGEgbnVtYmVyIG9mIHN0YW5kYXJkIEVEQSB0ZXN0cyBhbmQgcGxvdHMgdXNpbmcgUidzIGJ1aWx0IGluIGZ1bmN0aW9uczoKCiogRXh0cmFjdCBkZXNpcmVkIGh5ZHJvbG9naWNhbCBpbmRpY2F0b3JzIChgYWdncmVnYXRlYCwgYG1lYW5gKQoqIFRpbWUtc2VyaWVzIHBsb3RzIChgcGxvdGAgd2l0aCBsaW5lYXIgcmVncmVzc2lvbiBsaW5lIFtgbG1gXSBhbmQgYGxvd2Vzc2Agc21vb3RoaW5nKQoqIEhpc3RvZ3JhbXMgKGBoaXN0YCkKKiBRLVEgcGxvdCB0byBjaGVjayBub3JtYWxpdHkgKGBxcW5vcm1gOyBgcXFsaW5lYCkKKiBTaGFwaXJvLVdpbGsgdGVzdCBmb3Igbm9ybWFsaXR5IChgc2hhcGlyby50ZXN0YCkKKiBBdXRvY29ycmVsYXRpb24gRnVuY3Rpb24gKGBhY2ZgKQoKCiMjIyMgNC4xIEV4dHJhY3QgaHlkcm9sb2dpY2FsIGluZGljYXRvcnMKCldlIHdpbGwga2VlcCBpdCBzaW1wbGUgYW5kIGV4dHJhY3QgKipBbm51YWwgTWVhbiBGbG93IChBTUYpKiogZnJvbSBib3RoIG9ic2VydmVkIGFuZCBzaW11bGF0ZWQgc2VyaWVzIHVzaW5nIGBhZ2dyZWdhdGVgIHdpdGggdGhlIGBtZWFuYCBmdW5jdGlvbi4KCmBgYHtyfQoKIyBBbm51YWwgbWVhbiBmbG93IHNlcmllcyAtIE9icyAoTm90ZTogbmEucm0gPSBUIGlzIHVzZWQgdG8gZGVhbCB3aXRoIE5BcykKb2JzQU1GIDwtIGFnZ3JlZ2F0ZShteURhdGEkT2JzLCBieSA9IGxpc3QobXlEYXRhJFllYXIpLCBGVU4gPSBmdW5jdGlvbih4KSB7IG1lYW4oeCwgbmEucm0gPSBUKX0pCmNvbG5hbWVzKG9ic0FNRikgPC0gYygiWWVhciIsICJGbG93IikKCiMgQW5udWFsIG1lYW4gZmxvdyBzZXJpZXMgLSBTaW0Kc2ltQU1GIDwtIGFnZ3JlZ2F0ZShteURhdGEkU2ltLCBieSA9IGxpc3QobXlEYXRhJFllYXIpLCBGVU4gPSBtZWFuKQpjb2xuYW1lcyhzaW1BTUYpIDwtIGMoIlllYXIiLCAiRmxvdyIpCgojIEhhdmUgYSBsb29rIQpoZWFkKG9ic0FNRikKc3RyKG9ic0FNRikKCmBgYAoKIyMjIyA0LjIgUGxvdCBib3RoIG9ic2VydmVkIGFuZCBzaW11bGF0ZWQgc2VyaWVzIHdpdGggbGluZWFyIGFuZCBsb3dlc3MgbGluZXMKCmBgYHtyfQoKCiMgVGltaWUtc2VyaWVzIHBsb3Qgb2Ygb2JzZXJ2ZWQgYW5kIHNpbXVsYXRlZCBBTUYKcGxvdChvYnNBTUYkWWVhciwgb2JzQU1GJEZsb3csIHR5cGUgPSAnbCcsIG1haW4gPSAiT2JzZXJ2ZWQgJiBzaW11bGF0ZWQgQm95bmUgQU1GIDE5NTItMjAwOSIsIHhsYWIgPSAiWWVhciIsIHlsYWIgPSAiY3VtZWNzIiwgcGFuZWwuZmlyc3QgPSBncmlkKCksIGx3ZCA9IDEuNSkKCmxpbmVzKHNpbUFNRiRZZWFyLCBzaW1BTUYkRmxvdywgY29sID0gInJlZCIsIGx3ZCA9IDEuNSkKCiMgTGluZWFyIHJlZ3Jlc3Npb24KbGluUmVnT3V0T2JzIDwtIGxtKG9ic0FNRiRGbG93fm9ic0FNRiRZZWFyKQpsaW5SZWdPdXRTaW0gPC0gbG0oc2ltQU1GJEZsb3d+c2ltQU1GJFllYXIpCgojIEFkZCBsaW5lYXIgcmVncmVzc2lvbiBsaW5lCmFibGluZShsaW5SZWdPdXRPYnMsIGNvbCA9ICJibGFjayIsIGx0eSA9IDIsIGx3ZCA9IDIpCmFibGluZShsaW5SZWdPdXRTaW0sIGNvbCA9ICJyZWQiLCBsdHkgPSAyLCBsd2QgPSAyKQoKIyBBZGQgTE9XRVNTIGxpbmUgKGYgPSBzbW9vdGhlciBzcGFuIHBhcmFtdGVyLCBpLmUuIHByb3BvcnRpb24gb2YgZGF0YSBpbmZsdWVuY2luZyBzbW9vdGgpCmxpbmVzKGxvd2VzcyhvYnNBTUYkRmxvd35vYnNBTUYkWWVhciwgZiA9IDAuMiksIGNvbCA9ICJibGFjayIsIGx0eSA9IDMsIGx3ZCA9IDIpCmxpbmVzKGxvd2VzcyhzaW1BTUYkRmxvd35zaW1BTUYkWWVhciwgZiA9IDAuMiksIGNvbCA9ICJyZWQiLCBsdHkgPSAzLCBsd2QgPSAyKQoKCiMgQWRkIGxlZ2VuZCB0byBwbG90CmxlZ2VuZCgidG9wbGVmdCIsCiAgICAgICBsZWdlbmQ9YygiT2JzIiwgIlNpbSIpLAogICAgICAgY29sPSBjKCJibGFjayIsICJyZWQiKSwgbHR5PTEsbHdkPTEsIGJ0eT0ibiIpCgojIEFkZCB0ZXh0CnRleHQoMjAwMCwgMjAsIHBhc3RlKCJUcmVuZCBzbG9wZSA9ICIsIHJvdW5kKGxpblJlZ091dE9icyRjb2VmZmljaWVudHNbMl0sMiksIHNlcCA9ICIiKSwgY29sID0gImJsYWNrIikKdGV4dCgyMDAwLCAxNywgcGFzdGUoIlRyZW5kIHNsb3BlID0gIiwgcm91bmQobGluUmVnT3V0U2ltJGNvZWZmaWNpZW50c1syXSwyKSwgc2VwID0gIiIpLCBjb2wgPSAicmVkIikKCmBgYAoKIyMjIyA0LjMgQ2hlY2sgZm9yIG5vcm1hbGl0eQoKTWFueSBzdGF0aXN0aWNhbCB0ZXN0cyByZXF1aXJlIHRoYXQgdGhlIGRhdGEgZm9sbG93IG5vcm1hbCAob3IgR2F1c3NpYW4pIGRpc3RyaWJ1dGlvbiwgZm9yIGV4YW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24uCgpIb3dldmVyLCBvZnRlbiBoeWRyb2NsaW1hdGljIGRhdGEgdmlvbGF0ZSB0aGlzIGFzc3VtcHRpb24sIHNvIG5vbi1wYXJhbWV0cmljIHRlc3RzIGFyZSBtb3JlIHdpZGVseSBhcHBsaWVkIChlLmcuIE1hbm4tS2VuZGFsbCwgUGV0dGl0dCksIGFzIHdpbGwgYmUgdXNlZCBiZWxvdy4gSG93ZXZlciwgaXQgaXMgc3RpbGwgZ29vZCBwcmFjdGljZSB0byBrbm93IHRoZSBkaXN0cmlidXRpb24gb2YgeW91ciBkYXRhLgoKV2UnbGwgdXNlICoqaGlzdG9ncmFtcyoqLCAqKlF1YW50aWxlLVF1YW50aWxlLXBsb3RzIChRUSBwbG90cykqKiwgYW5kIHRoZSAqKnNoYXBpcm8tV2lsayoqIHRlc3QgdG8gZXhhbWluZSB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIG5vcm1hbGl0eS4KCiMjIyMgSGlzdG9ncmFtcwpgYGB7cn0KCiMgRmlyc3QgaGF2ZSBhIGxvb2sgYXQgdGhlIGhpc3RvcmdyYW0gKHByb2IgPSBUUlVFIGlzIGZvciBkZW5zaXR5KQojIE9icyBoaXN0b2dyYW0KaGlzdChvYnNBTUYkRmxvdywgY29sID0gImRhcmtibHVlIiwgcHJvYiA9IFRSVUUsIG1haW4gPSAiT2JzIikKbGluZXMoZGVuc2l0eShvYnNBTUYkRmxvdyksIGx3ZCA9IDIpCgojIFNpbSBoaXN0b2dyYW0KaGlzdChzaW1BTUYkRmxvdywgY29sID0gImRhcmtibHVlIiwgcHJvYiA9IFRSVUUsIG1haW4gPSAiU2ltIikKbGluZXMoZGVuc2l0eShzaW1BTUYkRmxvdyksIGx3ZCA9IDIpCgpgYGAKCiMjIyMgUVEtcGxvdHMKCmBgYHtyfQoKIyBRUXBsb3QgZm9yIG5vcm1hbGl0eSAtIE9icyBBTUYKcXFub3JtKG9ic0FNRiRGbG93LCBjb2wgPSAiZGFya2JsdWUiLCBtYWluID0gIk9icyIpOyBxcWxpbmUob2JzQU1GJEZsb3csIGRpc3RyaWJ1dGlvbiA9IHFub3JtKQoKIyBRUXBsb3QgZm9yIG5vcm1hbGl0eSAtIFNpbSBBTUYKcXFub3JtKHNpbUFNRiRGbG93LCBjb2wgPSAiZGFya2JsdWUiLCBtYWluID0gIlNpbSIpOyBxcWxpbmUoc2ltQU1GJEZsb3csIGRpc3RyaWJ1dGlvbiA9IHFub3JtKQoKYGBgCgoKIyMjIyBTaGFwaXJvLVdpbGsgdGVzdCBmb3Igbm9ybWFsaXR5CgpXZSB3aWxsIHJlamVjdCB0aGUgbnVsbCBoeXBvdGhlc2lzIG9mIG5vcm1hbGl0eSBmb3IgcC12YWx1ZSA8IDAuMSAoYWNjb3JkaW5nIHRvIFJveXN0b24gKDE5OTUpIC0gUiBoZWxwIHBhZ2UgZm9yIGBzaGFwaXJvLXRlc3RgKS4KCmBgYHtyfQoKIyBTaGFwaXJvLVdpbGsgLSBPYnMKc2hhcGlyby50ZXN0KG9ic0FNRiRGbG93KQoKIyBTaGFwaXJvLVdpbGsgLSBTaW0Kc2hhcGlyby50ZXN0KHNpbUFNRiRGbG93KQoKYGBgCgoqKkNvbmNsdXNpb24gb24gTm9ybWFsaXR5OioqIFRoZSBzaW11bGF0ZWQgc2VyaWVzIGNhbiBiZSBjb25zaWRlcmVkIGJyb2FkbHkgbm9ybWFsbHkgZGlzdHJpYnV0ZWQsIGJ1dCB0aGUgb2JzZXJ2ZWQgc2VyaWVzIGZhaWxzIHRoZSBTaGFwaXJvLVdpbGsgdGVzdCBhbmQgd2UgY2FuIHNlZSBmcm9tIHRoZSBoaXN0b2dyYW0gaXMgYmltb2RhbC4KCiMjIyMgNC40IENoZWNrIGZvciBzZXJpYWwgY29ycmVsYXRpb24KClByZXNlbmNlIG9mIHBvc2l0aXZlIHNlcmlhbCBjb3JyZWxhdGlvbiAob3IgYXV0b2NvcnJlbGF0aW9uKSB3aXRoaW4gYSBzZXJpZXMgY2FuIGluZmxhdGUgVHlwZSAxIGVycm9ycy4gVGhhdCBpcywgdGhlIGZsb3cgZnJvbSBsYXN0IHllYXIgaXMgc29tZWhvdyByZWxhdGVkIHRvIHRoaXMgeWVhci4gVGhpcyBpcyBjb21tb24gaW4gc2xvdyByZXNwb25kaW5nIGdyb3VuZHdhdGVyIGNhdGNobWVudHMsIG9yIGluIHJlZ2lvbnMgdGhhdCBhcmUgc3Ryb25nbHkgYWZmZWN0ZWQgYnkgbGFyZ2Utc2NhbGUgY2xpbWF0ZSBwcm9jZXNzIG92ZXIgbXVsdGkteWVhciBwZXJpb2RzIChlLmcuIE5vcnRoIEF0bGFudGljIE9zY2lsbGF0aW9uIChOQU8pKSBpbiBFdXJvcGUuIFRoaXMgdGVuZHMgdG8gcmV0dXJuIHRvbyBtYW55ICoic3RhdGlzdGljYWxseSBzaWduaWZpY2FudCIqIHJlc3VsdHMsIGFuZCB0aHVzIG1pc2xlYWRpbmcgcmVzdWx0cy4KCldlJ2xsIHVzZSB0aGUgYnVpbHQgaW4gKiphdXRvY29ycmVsYXRpb24gZnVuY3Rpb24qKiBgYWNmYC4gVHlwaWNhbGx5IHdlJ3JlIG9ubHkgY29uY2VybmVkIGlmIHRoZXJlIGlzIGV2aWRlbmNlIG9mIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcG9zaXRpdmUgbGFnLTEgc2VyaWFsIGNvcnJlbGF0aW9uIGF0IHRoZSA1JSBsZXZlbCAoc2VlIGRhc2hlZCBob3Jpem9udGFsIGxpbmUgaW4gcGxvdHMpOgoKYGBge3J9CgojIEFDRiBwbG90IGZvciB0ZXN0aW5nIGZvciBhdXRvY29ycmVsYXRpb24gLSBPYnMgQU1GCmFjZihvYnNBTUYkRmxvdywgbWFpbiA9ICJPYnMiKQoKIyBBQ0YgcGxvdCBmb3IgdGVzdGluZyBmb3IgYXV0b2NvcnJlbGF0aW9uIC0gU2ltIEFNRgphY2Yoc2ltQU1GJEZsb3csIG1haW4gPSAiU2ltIikKCgpgYGAKCioqQ29uY2x1c2lvbiBvbiBTZXJpYWwgQ29ycmVsYXRpb24qKjogVGhlcmUgaXMgbm8gc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBsYWctMSBzZXJpYWwgY29ycmVsYXRpb24sIGFsdGhvdWdoIHNvbWUgZXZpZGVuY2UgYXQgbGFnLTIgaW4gb2JzZXJ2ZWQgQW5udWFsIE1lYW4gRmxvdy4KCioqKgoKIyMgNS4gU3RhdGlzdGljYWwgdHJlbmQgYW5kIGNoYW5nZSBwb2ludCBhbmFseXNpcwoKT3VyICoqRURBKiogaGFzIHJldmVhbGVkIGEgbGFyZ2UgYXBwYXJlbnQgZGlmZmVyZW5jZSBpbiBwYXR0ZXJuIG9mIGNoYW5nZSBpbiBvYnNlcnZhdGlvbnMgY29tcGFyZWQgdG8gc2ltdWxhdGVkLiBIb3dldmVyLCB3ZSBjYW4gdGVzdCBpZiB0aGVzZSBwYXR0ZXJucyBvZiBjaGFuZ2UgYXJlIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuCgpXZSdsbCB1c2UgdGhlIGZyZWVseSBhdmFpbGFibGUgKip0cmVuZCoqIHBhY2thZ2UgZm9yIDMgdGFza3M6CgoxLiBDYWxjdWxhdGluaWcgdGhlIHJlbGF0aXZlICUgY2hhbmdlIGluIGZsb3cgb3ZlciB0aGUgcGVyaW9kLW9mLXJlY29yZCB1c2luZyB0aGUgcm9idXN0IFRoZWktU2VuIGFwcHJvYWNoIChUU0EpIChgc2Vucy5zbG9wZWApCjIuIFRlc3RpbmcgZm9yIGdyYWR1YWwgY2hhbmdlIGluIGFubnVhbCBtZWFuIG9ic2VydmVkIGFuZCBzaW11bGF0ZWQgZmxvdyB3aXRoIHRoZSBNYW5uLUtlbmRhbGwgdGVzdCAoYG1rLnRlc3RgKQozLiBUZXN0aW5nIGZvciBhYnJ1cHQgY2hhbmdlIGluIGFubnVhbCBtZWFuIG9ic2VydmVkIGFuZCBzaW11bGF0ZWQgZmxvdyB3aXRoIHRoZSBQZXR0aXR0IHRlc3QgKGBwZXR0aXR0LnRlc3RgKQoKIyMjIyA1LjEgQ2FsY3VsYXRlIHRyZW5kIG1hZ25pdHVkZSB1c2luZyB0aGUgcmVsYXRpdmUgVGhlaWwtU2VuIEFwcHJvYWNoIChUU0FyZWwpCgpUaGUgbm9uLXBhcmFtZXRyaWMgVGhlaWwtU2VuIGVzdGltYXRvciBpcyBhIG1vcmUgcm9idXN0IG1lYXN1cmUgb2YgdHJlbmQgc2xvcGUgdGhhbiBsaW5lYXIgcmVncmVzc2lvbiBhbmQgd2lsbCBnaXZlIGEgbXVjaCBtb3JlIGFjY3VyYXRlIHZhbHVlIGluIHRoZSBwcmVzZW5jZSBvZiBvdXRsaWVycy4KClRoZSByZWxhdGl2ZSBUaGVpbC1TZW4gQXBwcm9hY2ggKFRTQXJlbCkgZm9yIGNhbGN1bGF0aW9uIG9mIHRyZW5kIG1hZ25pdHVkZSAoJSkgaXMgZ2l2ZW46CgpUU0FyZWwgPSAoJFxiZXRhICogbiQpIC8gJFxtdSQgKiAxMDAKCndoZXJlICRcYmV0YSQgaXMgdGhlIHNsb3BlIGZyb20gVGhlaWwtU2VuLCAkbiQgaXMgdGhlIHNlcmllcyBsZW5ndGgsIGFuZCAkXG11JCBpcyB0aGUgbWVhbiBvZiB0aGUgc2VyaWVzLgoKYGBge3J9CgojIEZpcnN0IHRoZSAndHJlbmQnIHBhY2thZ2UgcmVxdWlyZXMgZGF0YSBhcmUgaW4gJ3RzJyAodGltZS1zZXJpZXMpIGZvcm1hdApvYnNBTUZ0cyA8LSBhcy50cyhvYnNBTUYpCnNpbUFNRnRzIDwtIGFzLnRzKHNpbUFNRikKCiMgQ2FsY3VhbHRlIFRoZWlsLVNlYW4gc2xvcGUgLSBPYnMgJiBTaW0Kb2JzU2VuU2xvcGUgPC0gc2Vucy5zbG9wZShvYnNBTUZ0c1sgLDJdKVsxXSAjIG9icwpzaW1TZW5TbG9wZSA8LSBzZW5zLnNsb3BlKHNpbUFNRnRzWyAsMl0pWzFdICMgU2ltCgojIENhbGN1bGF0ZSBUU0FyZWw6Cm9ic1RTQXJlbCA8LSAoYXMubnVtZXJpYyhvYnNTZW5TbG9wZSkgKiBsZW5ndGgob2JzQU1GJFllYXIpKSAvIG1lYW4ob2JzQU1GJEZsb3cpICogMTAwCnNpbVRTQXJlbCA8LSAoYXMubnVtZXJpYyhzaW1TZW5TbG9wZSkgKiBsZW5ndGgoc2ltQU1GJFllYXIpKSAvIG1lYW4oc2ltQU1GJEZsb3cpICogMTAwCgojIFRyZW5kIG1hZ25pdHVkZSBpbiBPYnMgKCUpCm9ic1RTQXJlbAoKIyBUcmVuZCBtYWduaXR1ZGUgaW4gU2ltICglKQpzaW1UU0FyZWwKCmBgYAoKKipUcmVuZCBtYWduaXR1ZGUncyoqIGluIGFubnVhbCBtZWFuIGZsb3cgYXJlIFRTQXJlbCA9ICszOCUgYW5kIFRTQXJlbCA9ICsxMiUgZm9yIG9ic2VydmVkIGFuZCBzaW11bGF0ZWQsIHJlc3BlY3RpdmVseSEKCldPVy4uLiBhbiAqYWxtb3N0IDQwJSBpbmNyZWFzZSogaW4gb2JzZXJ2ZWQgbWVhbiBmbG93IGluIHRoZSBCb3luZS4gTGV0J3Mgc2VlIHdoYXQgdGhlIHN0YXRpc3RpY2FsIHRyZW5kIHRlc3RzIHNheS4uLgoKCiMjIyMgNS4yIE1hbm4tS2VuZGFsbCB0ZXN0IGZvciAoZ3JhZHVhbCkgY2hhbmdlCgpXZSdsbCB0ZXN0IGZvciBncmFkdWFsIChtb25vdG9uaWMpIHRyZW5kIHdpdGggdGhlIG5vbi1wYXJhbWV0cmljIE1hbm4tS2VuZGFsbCB0ZXN0LgoKVGhlICoqc3RhbmRhcmRpc2VkIE1LIHN0YXRpc3RpYyAoTUtacykqKiBmb2xsb3dzIHRoZSBzdGFuZGFyZCBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggYSBtZWFuIG9mIHplcm8gYW5kIHZhcmlhbmNlIG9mIG9uZS4gQSAqcG9zaXRpdmUgKG5lZ2F0aXZlKSogdmFsdWUgb2YgTUtacyBpbmRpY2F0ZXMgYW4gKmluY3JlYXNpbmcgKGRlY3JlYXNpbmcpKiB0cmVuZC4gU3RhdGlzdGljYWwgc2lnbmlmaWNhbmNlIHdhcyBldmFsdWF0ZWQgd2l0aCBwcm9iYWJpbGl0eSBvZiB0eXBlIDEgZXJyb3Igc2V0IGF0IHRoZSA1JSBzaWduaWZpY2FuY2UgbGV2ZWwuIFdlIHdpbGwgdXNlIGEgdHdvLXRhaWxlZCBNSyB0ZXN0IChhcyB3ZSB3b24ndCBhc3N1bWUgdGhlIGRpcmVjdGlvbiBvZiBjaGFuZ2UgYmVmb3JlaGFuZCkuCgoqKlRoZSBudWxsIGh5cG90aGVzaXMgb2Ygbm8gdHJlbmQgKGluY3JlYXNpbmcgb3IgZGVjcmVhc2luZykgaXMgcmVqZWN0ZWQgd2hlbiB8TUtac3w+MS45Ni4qKgoKYGBge3J9CgojIE9icyBNS1pzCm9ic01LT3V0IDwtIG1rLnRlc3Qob2JzQU1GdHMpCm9ic01LWnMgIDwtIG9ic01LT3V0JFpnWzJdCm9ic01LWnMKCiMgU2ltIE1LWnMKc2ltTUtPdXQgPC0gbWsudGVzdChzaW1BTUZ0cykKc2ltTUtacyAgPC0gc2ltTUtPdXQkWmdbMl0Kc2ltTUtacwoKCmBgYAoKKipNSyBSZXN1bHRzOioqIE1LWnMgZm9yIG9icyBpcyAzLjEgd2hpY2ggaXMgbXVjaCBoaWdoZXIgdGhhbiBvdXIgc2lnbmlmaWNhbmNlIHRocmVzaG9sZCBvZiB8MS45Nnwgc28gKip0aGVyZSBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGluY3JlYXNpbmcgdHJlbmQgaW4gb2JzZXJ2ZWQgbWVhbiBmbG93LioqCgpIb3dldmVyLCBNS1pzIGZvciBzaW11bGF0ZWQgZmxvdyBpcyAxLjEsIHNvIHdoaWxlIHRoZSB0cmVuZCBpcyBpbmNyZWFzaW5nLCB0aGUgY2hhbmdlICoqaXMgbm90Kiogc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4KCiMjIyMgNS4zIFBldHRpdHQgdGVzdCBmb3IgKGFicnVwdCkgY2hhbmdlCgpXZSdsbCB1c2UgdGhlIFBldHRpdHQgc3RhdGlzdGljIHRvIGlkZW50aWZ5IGEgc2luZ2xlIChhYnJ1cHQpIGNoYW5nZSBwb2ludCBpbiBvYnNlcnZlZCBhbmQgc2ltdWxhdGVkIHNlcmllcy4KClRoZSBQZXR0aXR0IHRlc3QgaXMgbm9ucGFyYW1ldHJpYyBhbmQgcmVsYXRpdmUgdG8gb3RoZXIgdGVzdHMgaXMgbGVzcyBzZW5zaXRpdmUgdG8gb3V0bGllcnMgYW5kIHNrZXdlZCBkYXRhLiAqKlRoZSBudWxsIGh5cG90aGVzaXMgKG5vIHN0ZXAgY2hhbmdlIGluIHRpbWUgc2VyaWVzKSBhZ2FpbnN0IHRoZSBhbHRlcm5hdGl2ZSAoYW4gdXB3YXJkIG9yIGRvd253YXJkIGNoYW5nZSBwb2ludCBpbiBhIGdpdmVuIHllYXIpIGlzIHRlc3RlZCBhdCB0aGUgNSUgc2lnbmlmaWNhbmNlIGxldmVsLioqCgpgYGB7cn0KCiMgUGV0dGl0dCB0ZXN0Cm9ic1BldHRpdHRPdXQgPC0gcGV0dGl0dC50ZXN0KG9ic0FNRnRzWyAsMl0pCnNpbVBldHRpdHRPdXQgPC0gcGV0dGl0dC50ZXN0KHNpbUFNRnRzWyAsMl0pCgojIEZpbmQgcC12YWx1ZSBvZiBjaGFuZ2UgcG9pbnRzCm9ic19QZXR0UF92YWwgPC0gb2JzUGV0dGl0dE91dCRwLnZhbHVlCnNpbV9QZXR0UF92YWwgPC0gc2ltUGV0dGl0dE91dCRwLnZhbHVlCgojIEZpbmQgeWVhciBvZiBjaGFuZ2UgcG9pbnRzCiMgT2JzCm9ic19QZXR0WWVhciAgPC0gb2JzUGV0dGl0dE91dCRlc3RpbWF0ZQpvYnNDaGFuZ2VZZWFyIDwtIG9ic0FNRiRZZWFyW29ic19QZXR0WWVhcl0Kb2JzQ2hhbmdlWWVhciAjIFllYXIKb2JzX1BldHRQX3ZhbCAjIHAtdmFsdWUKCiMgU2ltCnNpbV9QZXR0WWVhciAgPC0gc2ltUGV0dGl0dE91dCRlc3RpbWF0ZQpzaW1DaGFuZ2VZZWFyIDwtIHNpbUFNRiRZZWFyW3NpbV9QZXR0WWVhcl0Kc2ltQ2hhbmdlWWVhciAjIFllYXIKc2ltX1BldHRQX3ZhbCAjIHAtdmFsdWUKCgpgYGAKCioqUGV0dGl0dCBSZXN1bHRzOioqIFRoZXJlIGlzIGEgKipzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHVwd2FyZCBjaGFuZ2UgcG9pbnQgaW4gQm95bmUgbWVhbiBmbG93KiogYW5kIHRoaXMgb2NjdXJyZWQgaW4gMTk3OCAocCA9IDAuMDAxKS4gV2hpbGUgdGhlIFBldHRpdHQgdGVzdCByZXR1cm5zIDE5NzggYXMgdGhlIHllYXIgb2YgY2hhbmdlLCAqKml0J3MgbXVjaCB0b28gd2VhayB0byBiZSBjb25zaWRlcmVkIGFuIGFicnVwdCBjaGFuZ2UgcG9pbnQqKiBhcyB0aGUgcC12YWx1ZSBpcyAwLjQuCgoqKioKIyMgNi4gU3VtbWFyeSBvZiByZXN1bHRzCgoqKlRlc3QqKiAgICB8ICoqT2JzZXJ2ZWQqKiAgICAgICAgIHwgKipNb2RlbGxlZCoqCi0tLS0tLS0tICAgIHwtLS0tLS0tLS0tLS0tICAgICAgICAgfC0tLS0tLS0tLS0tLS0KKipUU0FyZWwqKiAgfCAzOCUgICAgICAgICAgICAgICAgICB8IDEyJQoqKk1LWnMqKiAgICB8IDMuMSAgICAgICAgICAgICAgICAgIHwgMS4xCioqUGV0dGl0dCoqIHwgMTk3OCAocCA9IDAuMDAxKSAgICAgfCAxOTc4IChwID0gMC40KQoKCmBgYHtyfQoKCiMgVGltaWUtc2VyaWVzIHBsb3Qgb2Ygb2JzZXJ2ZWQgYW5kIHNpbXVsYXRlZCBBTUYKcGxvdChvYnNBTUYkWWVhciwgb2JzQU1GJEZsb3csIHR5cGUgPSAnbCcsIG1haW4gPSAiT2JzZXJ2ZWQgJiBzaW11bGF0ZWQgQm95bmUgQU1GIDE5NTItMjAwOSIsIHhsYWIgPSAiWWVhciIsIHlsYWIgPSAiY3VtZWNzIiwgcGFuZWwuZmlyc3QgPSBncmlkKCksIGx3ZCA9IDEuNSkKCmxpbmVzKHNpbUFNRiRZZWFyLCBzaW1BTUYkRmxvdywgY29sID0gInJlZCIsIGx3ZCA9IDEuNSkKCiMgTGluZWFyIHJlZ3Jlc3Npb24KbGluUmVnT3V0T2JzIDwtIGxtKG9ic0FNRiRGbG93fm9ic0FNRiRZZWFyKQpsaW5SZWdPdXRTaW0gPC0gbG0oc2ltQU1GJEZsb3d+c2ltQU1GJFllYXIpCgojIEFkZCBsaW5lYXIgcmVncmVzc2lvbiBsaW5lCmFibGluZShsaW5SZWdPdXRPYnMsIGNvbCA9ICJibGFjayIsIGx0eSA9IDIsIGx3ZCA9IDIpCmFibGluZShsaW5SZWdPdXRTaW0sIGNvbCA9ICJyZWQiLCBsdHkgPSAyLCBsd2QgPSAyKQoKIyBBZGQgTE9XRVNTIGxpbmUgKGYgPSBzbW9vdGhlciBzcGFuIHBhcmFtdGVyLCBpLmUuIHByb3BvcnRpb24gb2YgZGF0YSBpbmZsdWVuY2luZyBzbW9vdGgpCmxpbmVzKGxvd2VzcyhvYnNBTUYkRmxvd35vYnNBTUYkWWVhciwgZiA9IDAuMiksIGNvbCA9ICJibGFjayIsIGx0eSA9IDMsIGx3ZCA9IDIpCmxpbmVzKGxvd2VzcyhzaW1BTUYkRmxvd35zaW1BTUYkWWVhciwgZiA9IDAuMiksIGNvbCA9ICJyZWQiLCBsdHkgPSAzLCBsd2QgPSAyKQoKIyBBZGQgdmVydGljYWwgY2hhbmdlIHBvaW50IGxpbmUKYWJsaW5lKHYgPSAxOTc4LCBjb2wgPSAiZGFya2JsdWUiLCBsd2QgPSAyKQoKdGV4dCgxOTc2LCA0NSwgIkNoYW5nZSBQb2ludCAxOTc4IiwgY29sID0gImRhcmtibHVlIiwgc3J0ID0gOTApICMgJ3NydCcgcm90YXRlcyB0ZXh0IDkwIGRlZ3JlZXMKCgojIEFkZCBsZWdlbmQgdG8gcGxvdApsZWdlbmQoInRvcGxlZnQiLAogICAgICAgbGVnZW5kPWMoIk9icyIsICJTaW0iKSwKICAgICAgIGNvbD0gYygiYmxhY2siLCAicmVkIiksIGx0eT0xLGx3ZD0xLCBidHk9Im4iKQoKIyBBZGQgdGV4dAp0ZXh0KDIwMDAsIDIwLCBwYXN0ZSgiVHJlbmQgbWFnLiAoJSkgPSAiLCByb3VuZChvYnNUU0FyZWwsMCksIHNlcCA9ICIiKSwgY29sID0gImJsYWNrIikKdGV4dCgyMDAwLCAxNywgcGFzdGUoIlRyZW5kIG1hZy4gKCUpID0gIiwgcm91bmQoc2ltVFNBcmVsLDApLCBzZXAgPSAiIiksIGNvbCA9ICJyZWQiKQoKCmBgYAoKIyMgNy4gRnVydGhlciB0b3BpY3MKCiMjIyMgNy4xIEJsb2NrLWJvb3RzdHJhcHBpbmcgKEJCUykgc3RhdGlzdGljcwoKSWYgdGhlIHNlcmlhbCBjb3JyZWxhdGlvbiBhc3N1bXB0aW9uIGlzIHNldmVyZWx5IHZpb2xhdGVkLCBibG9jay1ib290c3RyYXBwaW5nIChCQlMpIG9mZmVycyBhIHZpYWJsZSBtZXRob2QgLSBleGFtcGxlIGJlbG93OgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CgojIE5lZWQgJ2Jvb3QnIHBhY2thZ2UKbGlicmFyeShib290KSAjIGJvb3RzdHJhcHBpbmcKbGlicmFyeShLZW5kYWxsKSAjIEFub3RoZXIgcGFja2FnZSB3aXRoIHRoZSAnTWFubi1LZW5kYWxsJyB0ZXN0IC0gYnV0IHRoaXMgd29ya3MgYmV0dGVyIHdpdGggJ3RzYm9vdCcKCiMgTXkgTUtacyBGdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KbXlNS1pzRnVuIDwtIGZ1bmN0aW9uKHgpIHsKCiAgUyA8LSBNYW5uS2VuZGFsbCh4KSRTCiAgdmFyUyA8LSBNYW5uS2VuZGFsbCh4KSR2YXJTCgogICNDYWxjdWxhdGUgdGhlIFpzIHN0YXRpc3RpYwogIGlmIChTID4gMCkgewogICAgWnMgPC0gKFMtMSkvc3FydCh2YXJTKQogIH0gZWxzZSBpZiAoUyA8IDApIHsKICAgIFpzIDwtIChTKzEpL3NxcnQodmFyUykKICB9IGVsc2UgewogICAgWnMgPC0gMAogIH0KCiAgcmV0dXJuKFpzKSAgIyBNa1pzCgp9CgojIGVuZCBmdW5jdGlvbiAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMgU2V0LnNlZWQoKSBmb3IgcmVwcm9kdWNhYmlsaXR5CnNldC5zZWVkKDUwKQoKIyBCQlMgd2l0aCBibG9jayBsZW5ndGggPSA0IGFuZCAxMDAwIHJlc2FtcGxlcwpiNCA8LSB0c2Jvb3QoKG9ic0FNRlsgLDJdKSwgbXlNS1pzRnVuLCBSPTEwMDAsICBzaW09ImZpeGVkIiwgbD0zKQoKIyBzb3J0IGJvb3Qgb3V0cHV0CmJzNCA8LSBzb3J0KGI0JHQpCgojIEJCUyBjcml0aWNhbCB2YWx1ZXMKYnM0WzI1XSAjIGxvd2VyCmJzNFs5NzVdICMgdXBwZXIKCiMgUGxvdCBib290c3RyYXAgb3V0cHV0CnBsb3QoYjQpCgoKYGBgCgojIyMjIDcuMiBNdWx0aXBsZSBNYW5uLUtlbmRhbGwgdGVzdHMKClIgaXMgbXVjaCBtb3JlIHBvd2VyZnVsIHRoYW4ganVzdCBhIHN0YXRpc3RpY2FsIHRvb2wgLSBpdCdzIGFsc28gYSBjYXBhYmxlIHByb2dyYW1taW5nIGVudmlyb25tZW50IHNvIHdlJ3JlIGFibGUgdG8gcGVyZm9ybSBpbmRpdmlkdWFsIHRhc2tzLCBzdWNoIGFzIGNhbGN1bGF0aW5nIGEgdHJlbmQgc3RhdGlzdGljLCBmb3IgbWFueSBpdGVyYXRpb25zIHVzaW5nIGxvb3BzIGV0Yy4KCkhlcmUgaXMgYSBxdWljayBleGFtcGxlIG9mIGhvdyB0aGUgZGVwZW5kZW5jZSBvZiB0aGUgTWFubi1LZW5kYWxsIFpzIHN0YXRpc3RpYyBvbiB0aGUgcGVyaW9kLW9mLXJlY29yZCB1c2VkLiBXZSB0ZXN0IGZvciBjaGFuZ2VzIGluIG9ic2VydmVkIGFuZCBtb2RlbGxlZCBmbG93IGZyb20gYWxsIHBvc3NpYmxlIHN0YXJ0IHllYXJzIHVwIHRvIDE5ODU6IDE5NTItMjAwOSwgMTk1My0yMDA5LC4uLiwxOTg1LTIwMDkuCgpgYGB7cn0KCiMgQ3JlYXRlIG91dHB1dCBtYXRyaXggdG8gc3RvcmUgdHJlbmQgcmVzdWx0cwptYXQgPC0gbWF0cml4KE5BLCBuY29sID0gMywgbnJvdyA9IDM0KQpjb2xuYW1lcyhtYXQpIDwtIGMoIlN0YXJ0WWVhciIsICJvYnNNS1pzIiwgInNpbU1LWnMiKQptYXRbICwxXSA8LSAxOTUyOjE5ODUKCiMgTG9vcCBNYW5uLUtlbmRhbGwgdGVzdCBmb3IgYWxsIHBvc3NpYmxlIHN0YXJ0IHllYXJzIGluIGJvdGggb2JzIGFuZCBzaW0KZm9yKGkgaW4gMTozNCkgewoKICBtYXRbaSwyXSA8LSBteU1LWnNGdW4ob2JzQU1GWyAsMl1baTo1OF0pICMgT2JzCiAgbWF0W2ksM10gPC0gbXlNS1pzRnVuKHNpbUFNRlsgLDJdW2k6NThdKSAjIFNpbQoKfSAjIGVuZCBpCgojIFBsb3QKcGxvdCggbWF0WyAsMV0sIG1hdFsgLDJdLCB0eXBlID0gJ2wnLCBjb2wgPSAiZGFya2dyYXkiLCBsdHkgPSAxLCBsd2QgPSAyLCB5bGltID0gYygtMywgMyksIG1haW4gPSAiUGVyc2lzdGVuY2Ugb2YgdHJlbmQgLSBPYnMgJiBTaW0iLCB4bGFiID0gIlN0YXJ0IFllYXIiLCB5bGFiID0gIk1LWnMiKQpsaW5lcyhtYXRbICwxXSwgbWF0WyAsM10sIGNvbCA9ICJkYXJrZ3JheSIsIGx0eSA9IDMsIGx3ZCA9IDIpCgojIEFkZCBsaW5lcyB0byBzaG93IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgKy8tIHRyZW5kcyBhbmQgemVybyB0cmVuZAphYmxpbmUoaCA9ICAxLjk2LCBjb2wgPSAicmVkIiwgbHR5ID0gMiwgbHdkID0gMikKYWJsaW5lKGggPSAtMS45NiwgY29sID0gInJlZCIsIGx0eSA9IDIsIGx3ZCA9IDIpCmFibGluZShoID0gMCwgY29sID0gImJsYWNrIiwgbHR5ID0gMiwgbHdkID0gMikKCiMgRmlyc3QgbGVnZW5kCmxlZ2VuZC50ZXh0IDwtIGMoIk5vIHRyZW5kIChNS1pzID0gMCkiLCAiU2lnLiB0cmVuZCAoTUtacyA+IHwxLjk2fCkiKQpsZWdlbmQoImJvdHRvbWxlZnQiLCAgICAgICAgICAgICAgICAgICAjIFNldCBsb2NhdGlvbiBvZiB0aGUgbGVnZW5kCiAgICAgICBsZWdlbmQgPSBsZWdlbmQudGV4dCwgICAgICAgICAgICMgU3BlY2lmeSB0ZXh0CiAgICAgICBjb2wgPSBjKCJibGFjayIsInJlZCIpLCAgICAgICAgICMgU2V0IGNvbG9ycyBmb3IgbGVnZW5kCiAgICAgICBsdHkgPSBjKDIsMiksICAgICAgICAgICAgICAgICAgICMgU2V0IHR5cGUgb2YgbGluZXMgaW4gbGVnZW5kCiAgICAgICBtZXJnZSA9IFRSVUUsIGJ0eSA9ICduJywgbHdkID0gYygyLCAyKSkKCiMgU2Vjb25kIGxlZ2VuZApsZWdlbmQoInRvcHJpZ2h0IiwKICAgICAgIGxlZ2VuZD1jKCJPYnMiLCAiU2ltIiksCiAgICAgICBjb2w9IGMoImRhcmtncmF5IiwgImRhcmtncmF5IiksIGx0eT1jKDEsIDMpLGx3ZD1jKDIsMiksIGJ0eT0ibiIpCgpgYGAKCioqKgojIyMjIEVORCBvZiB0dXRvcmlhbCAtIEkgaG9wZSB5b3UnbGwgYWdyZWUgUiBpcyBhIG5pY2UgKGZyZWUhKSBhbHRlcm5hdGl2ZSB0byBlLmcuIE1hdGxhYiA6KQoqKioKClwKW3RvcF0oI3RvcCkKCgojIERpc2N1c3Npb24KClBsZWFzZSBnaXZlIHVzIGZlZWRiYWNrIGF0Cjxmb250IHNpemU9IjYiPltiaXQubHkvZmVlZGJhY2tSXShodHRwczovL2JpdC5seS9mZWVkYmFja1IpPC9mb250PgoKRm9yIGRpc2N1c3Npb25zLCBwbGVhc2UgdXNlIHRoZQpbSHlkcm9sb2d5IGluIFIgRmFjZWJvb2sgZ3JvdXBdKGh0dHBzOi8vd3d3LmZhY2Vib29rLmNvbS9ncm91cHMvMTEzMDIxNDc3NzEyMzkwOS8pLgoKRnVydGhlciByZXNvdXJjZXMgY2FuIGJlIGZvdW5kIG9uIHRoZSBnaXRodWIgcGFnZToKPGh0dHBzOi8vZ2l0aHViLmNvbS9icnJ5L3JoeWRybyNyZXNvdXJjZXM+Cgo=