1 DESCRIPTION

This R Markdown document presents the functions and the steps used to estimate species’ Area of Habitat (AOH) as described by Dória & Dobrovolski (2020) in the paper “Improving post-2020 conservation of terrestrial vertebrates in Caatinga”.

2 REQUIREMENTS

2.1 Install and load the required packages

install.packages ('sp')
install.packages ('raster')
install.packages ('maptools')
install.packages ('maps')
install.packages ('rworldmap')
install.packages ('colorRamps')
install.packages ('rgdal')
install.packages ('here')
library (sp)
library (raster)
library (maptools)
## Warning: package 'maptools' was built under R version 3.6.1
## Checking rgeos availability: TRUE
library (maps)
library (rworldmap)
## ### Welcome to rworldmap ###
## For a short introduction type :   vignette('rworldmap')
library (colorRamps)
library (rgdal)
## rgdal: version: 1.3-4, (SVN revision 766)
##  Geospatial Data Abstraction Library extensions to R successfully loaded
##  Loaded GDAL runtime: GDAL 2.2.3, released 2017/11/20
##  Path to GDAL shared files: C:/Users/Thais/OneDrive/Documentos/R/win-library/3.6/rgdal/gdal
##  GDAL binary built with GEOS: TRUE 
##  Loaded PROJ.4 runtime: Rel. 4.9.3, 15 August 2016, [PJ_VERSION: 493]
##  Path to PROJ.4 shared files: C:/Users/Thais/OneDrive/Documentos/R/win-library/3.6/rgdal/proj
##  Linking to sp version: 1.3-1
library (here)
## Warning: package 'here' was built under R version 3.6.1
## here() starts at F:/DOC/PAPERS/Paper1/MATERIAL SUPLEMENTAR/RPubs

2.2 Load the required functions

    1. To download habitat preferences from IUCN database
f.hab.redlist <- function(z){
  hab.spp <- rl_habitats(z, key="38e913256ac81b0159bc5421b633ad467b94be67e9c886a3ca86ccc78710ef50")
  return(hab.spp$result$code)
}
#Notes:
#'z' in this function is matrix with specie's names 
#in rl_habitats function, the argument 'key' must be requested from IUCN.
    1. To create a matrix with habitat preferences of species
f.pref.hab <- function(y, x){ 
  for (i in 1:nrow(y))
    y[i,"artif"] <- ifelse(sum(x[i,]>14 & x[i,]<15),1,0)
  yart <- y
  { 
    for (i in 1:nrow(yart)) 
      yart[i,"opena"] <- ifelse(sum(x[i,]>2 & x[i,]<5 | x[i,]>=12.2 & x[i,]<=12.3 | x[i,]==13.3),1,0)
    yopen <- yart  
    { 
      for (i in 1:nrow(yopen)) 
        yopen[i,"fores"] <- ifelse(sum(x[i,]<=1.9),1,0)
     return(yopen)}}
  }
# Notes:
# 'y' in this function is a matrix with categories of habitat that will be considered as preference to each species
# 'x' in this function is a matrix with species code's indicating habitat preference according to IUCN database
    1. To refine ranges of distribution by the habitat and elevation preferences of species
f.habelev.ref <- function(a, b, c) {
  nsp <- nlayers(a)
  for  (i in 1:nsp)  {
    tab2 <- cbind(categs.pref, (as.numeric(b[i,3:7]))) 
    landcover.reclass2 <- reclassify(landcover.reclass, tab2, progress="text") 
    if(c[i,2]!=0){
      elev.reclass <- elev.caat>=c[i,1]&elev.caat<=c[i,2]
      landcover.reclass2 <- overlay(landcover.reclass2,elev.reclass,fun=function (x,y){x*y}, progress="text")
    }
    landelev.reclass.res <- aggregate(landcover.reclass2, fun=sum, fact=172, progress="text") #0.05 resolution
    landelev.abfrac <- landelev.reclass.res/29584
    spp <- a[[i]]
    proj <- CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0")       
    proj4string(spp) <- proj 
    resamplesp <- resample(landelev.abfrac,spp,method="ngb", progress="text")
    mult <- overlay(resamplesp,spp,fun=function (x,y){x*y})
    nome <- unique[i] #species list created in STEP 1
    writeRaster(mult,paste(nome,"0.05_HSMs.asc",sep = ""),format="ascii")
  }} 
#Notes:
# 'a' in this function is the original raster of species 
# 'b' in this function is matrix of habitat's preference of species  
# 'c' in this function is the matrix of elevation's preference of species

3 PERFORMING THE ANALYSIS

3.1 AMPHIBIA

3.1.1 Loading data

adata <- "https://github.com/thaisdoria/TD_Thesis_Chapter1_RData/blob/master/DoriaTAF_Amphibians.RData?raw=true"
load(url(adata)) # RData file created on 2017/2018 containing the files listed bellow
unifiedshp.amphibians <- AMPHCAAT #amphibians species distribution (IUCN data)
shape.caat <- caatinga #shapefile of the region 
landcover.caat <- mpbio.ori #raster of the region landcover 
tab.reclass <- tab.reclass #table for reclassify landcover 
pref.hab.spp <- habnull.a #table containing only preferences data on water bodies (manually assigned following specific criteria) that will be filled with habitat preferences from IUCN
elev.caat <- elev.resample #raster of the region elevation
pref.elev.spp <- elevspp.a #table with the interval of elevation in which each species occurs (data compiled manually from IUCN database)

ATTENTION: polygons from IUCN were downloaded at 2017/2018 and after that the genus of species “Hypsiboas albomarginatus”, “Hypsiboas albopunctatus”, “Hypsiboas atlanticus”, was modified for “Boana”. Thus, to run this script adequately and obtain correct correspondences between the species names from shapefiles and the most recent habitat data from IUCN database it is recommended to download the up-dated polygons and not use those available in this RData.

3.1.2 Running steps

##### STEP 1 - Creating an individual shapefile for each species distribution ####
data <- unifiedshp.amphibians 
names(data) #used to verify the column that contained the species names
unique <- sort(unique(data@data$binomial)) #accessing 'binomial', column that contain the species names, and saving these names removing the duplicate ones
for (i in 1:length(unique)) {
  tmp <- data[data$binomial== unique[i], ] 
  writeOGR(tmp, dsn=getwd(), unique[i], driver="ESRI Shapefile",
           overwrite_layer=TRUE)
}

#### STEP 2 - Creating an individual raster for each species distribution #### 
extent(landcover.caat) 
r0.05 <- raster(res=0.05, xmn=-44.50838, xmx=-35.17174, ymn=-16.08856, ymx=-2.783879)
r0.05[] <- 1:ncell(r0.05)
r0.05 # 266, 187, 49742 (nrow, ncol, ncell) 

lista.spp.a <- list.files(pattern = ".shp$", full.names = F) #list of amphibians shapefiles 

# Presence/Absence
for (i in 1:length(lista.spp.a)) {
  spp <- shapefile(lista.spp.a[i])
  spp.r <- rasterize(spp, r0.05, background = 0, getCover=T)
  spp.r[spp.r > 1] <- 1
  spp.c <- crop(spp.r, shape.caat) 
  spp.m <- mask(spp.c, shape.caat)
  e <- extent(-44.8, -34.8, -16.6, -2.2) 
  spp.e <- extend(spp.m,e)
  nome <- unique[i]
  writeRaster(spp.e, paste(nome, "0.05.asc", sep = ""), format = "ascii", overwrite = T)
} 

#### STEP 3 - Obtaining information of habitat species preferences data from IUCN #### 
spp.nomes <- as.matrix(unique) #remember: the 'unique' object was created in the STEP 1
f.hab.redlist <- function(spp.nomes){
  hab.spp <- rl_habitats(spp.nomes, key="99ad94015eeccd35abe9f869c633e1ebad67ee390ab444902eead0c309c800d5")
  return(hab.spp$result$code)
}

lista.hab <- lapply(1:length(spp.nomes),function(f0) f.hab.redlist(spp.nomes[f0])) #creating an object with all information about species habitat preference running 'hab_redlist' function which returns only the information about preference, while 'rl_habitats' function returns in addition the name of the species  
spp.codes <- do.call(rbind, lista.hab) # transforming the list with habitat codes of species in a matrix of habitat codes 

#### STEP 4 - Editing the habitat preference to obatin correspondence with the landcover categories #### 
## Preparing the matrix
pref.hab.spp # table already loaded with file .RData
hab.pref <- as.matrix(pref.hab.spp)
# Remove lines for which there is no available IUCN data (verify this on objetc 'lista.hab') 
hab.pref1 <- hab.pref[-109,] # the number here corresponds to lines corresponding to a species for which the habitat preference information was empty, that is, there wasn't habitat preference information for this species in IUCN database  
hab.pref1 <- replace(hab.pref1, list = is.na(hab.pref1), values = 0) #replace NA for 0
## Using the function (to fill the matrix with IUCN data for 128 spp)
hab.pref2 <- f.pref.hab (hab.pref1, spp.codes) #creating an matrix with habitat preferences of each species with function f.pref.hab
## Editing the matrix of spp. without IUCN data (1 spp)
hab.pref3 <- hab.pref[109,] 
hab.pref3 <- replace(hab.pref3, list = is.na(hab.pref3), values = 1) # replace NA data for 1 (to maintain original distribution on artificial, open areas and forest areas for species without IUCN data)
# Creating a final (total) matrix 
pref.hab.spp.a <- rbind(hab.pref2, hab.pref3) # Join the two matrix (spp with and without IUCN data)
pref.hab.spp.a <- pref.hab.spp.a[order(as.numeric(as.factor(pref.hab.spp.a[,2]))),] # reorder in alphabetical order
## Saving the habitat matrix of species
write.table(pref.hab.spp.a, file="HabMat_Amphs_FILLED.csv", sep=";",  header=T) #saving the matrix with habitat preferences of each species

#### STEP 5 - Reclassifying the land cover to obtain equivalence with the IUCN habitat categories #### 
landcover.reclass <- reclassify(landcover.caat, tab.reclass)
writeRaster(landcover.reclass, filename="landcoverReclass.tif")

#### STEP 6 - Refining the individual raster according to the land cover and habitat preferences ##### 
## Preparing data
categs.pref <- c(0,1,2,3,4)
landcover.reclass <- raster("landcoverReclass.tif") # uploading the reclassified land cover 
elev.caat # raster already loaded with file .RData
pref.hab.spp.a # table created in the STEP 4
pref.elev.spp # table already loaded with file .RData
pref.elev.spp.a <- as.matrix(pref.elev.spp)
pref.elev.spp.a <- matrix(as.numeric(pref.elev.spp.a[, 2:3]), ncol = 2 )
range.orig.a <- raster::stack(list.files(path="   ", pattern = "asc$", full.names = F)) #raster files created in the STEP 2

## Using the function
f.habelev.ref(range.orig.a, pref.hab.spp.a, pref.elev.spp.a)

3.2 REPTILIA

3.2.1 Loading data

rdata <- "https://github.com/thaisdoria/TD_Thesis_Chapter1_RData/blob/master/DoriaTAF_Reptilesg.RData?raw=true"
load(url(rdata)) #RData file created on 2019 containing the files listed bellow:
unifiedshp.reptiles.gard <- REPCAATgard #reptiles species distribution (GARD data)
shape.caat <- caatinga #shapefile of the region 
landcover.caat <- mpbio.ori #raster of the region landcover 
tab.reclass <- tab.reclass #table for reclassify landcover function
pref.hab.spp <- habnull.rg #table containing only preferences data on water bodies (manually assigned following specific criteria) that will be filled with habitat preferences from IUCN
elev.caat <- elev.resample #raster of the region elevation 
pref.elev.spp <- elevspp.rg #table with the interval of elevation in which each species occurs (data compiled manually from IUCN database)

3.2.2 Running steps

##### STEP 1 - Creating an individual shapefile for each species distribution ####
data <- unifiedshp.reptiles.gard 
names(data) #used to verify the column that contained the species names
unique.gard <- sort(unique(data@data$Binomial)) #accessing 'binomial', column that contain the species names, and saving these names removing the duplicate ones
for (i in 1:length(unique.gard)) {
  tmp <- data[data$Binomial== unique.gard[i], ] 
  writeOGR(tmp, dsn=getwd(), unique.gard[i], driver="ESRI Shapefile",
           overwrite_layer=TRUE)
}

#### STEP 2 - Creating an individual raster for each species distribution #### 
extent(landcover.caat) # this information was used to create a reference raster, the chosen resolution was 300m (0.002777778 x 0.002777778 degree)
r0.05 <- raster(res=0.05, xmn=-44.50838, xmx=-35.17174, ymn=-16.08856, ymx=-2.783879)
r0.05[] <- 1:ncell(r0.05)
r0.05  # 266, 187, 49742 (nrow, ncol, ncell) 

lista.spp.r.gard <- list.files(pattern = ".shp$", full.names = F) #list of reptiles shapefiles 

# Presence/Absence
for (i in 1:length(lista.spp.r.gard)) {
  spp <- shapefile(lista.spp.r.gard[i])
  spp.r <- rasterize(spp, r0.05, background = 0, getCover=T)
  spp.r[spp.r > 1] <- 1
  spp.c <- crop(spp.r, shape.caat) 
  spp.m <- mask(spp.c, shape.caat)
  e <- extent(-44.8, -34.8, -16.6, -2.2) 
  spp.e <- extend(spp.m,e)
  nome <- unique.gard[i]
  writeRaster(spp.e, paste(nome, "0.05g.asc", sep = ""), format = "ascii", overwrite = T)
} 

#### STEP 3 - Obtaining information of habitat species preferences data from IUCN #### 
spp.nomes.gard <- as.matrix(unique.gard) #remember: the 'unique.gard' object was created in the STEP 1
f.hab.redlist <- function(spp.nomes.gard){
  hab.spp <- rl_habitats(spp.nomes.gard, key="99ad94015eeccd35abe9f869c633e1ebad67ee390ab444902eead0c309c800d5")
  return(hab.spp$result$code)
}
lista.hab.gard <- lapply(1:length(spp.nomes.gard),function(f0) f.hab.redlist(spp.nomes.gard[f0])) #creating an object with all information about species habitat preference running 'hab_redlist' function which returns only the information about preference, while 'rl_habitats' function returns in addition the name of the species  
spp.codes.gard <- do.call("rbind", lista.hab.gard) # transforming the list with habitat codes of species in a matrix of habitat codes 
spp.codes.gard

#### STEP 4 - Editing the habitat preference to obatin correspondence with the landcover categories #### 
## Preparing the matrix
pref.hab.spp #   c table already loaded with file .RData
hab.pref.gard <- as.matrix(pref.hab.spp)
# There are IUCN data only for 82 spp. 
# Remove lines for which there is no available IUCN data (verify this on objetc 'lista.hab.gard') 
hab.pref.gard1 <- hab.pref.gard[-c(1,3:6,8:10,14:16, 18, 20:30, 32:33, 36:49, 51:61, 63:65, 68, 70, 72:74, 78, 80, 82:85, 87:100, 103:104, 107, 109, 113:115, 117:122, 127:140, 142, 145, 148, 150:152, 155:160, 162:174, 177:188, 190:192, 194:196, 198:208, 211, 213:215, 217:219, 222, 226:229, 232:236, 240:241, 247:251, 253, 255:258, 260:261, 264, 266:268, 270, 276:281, 283, 285),] # the number here corresponds to lines corresponding to a species for which the habitat preference information was empty, that is, there wasn't habitat preference information for this species in IUCN database  
hab.pref.gard1 <- replace(hab.pref.gard1, list = is.na(hab.pref.gard1), values = 0) #replace NA for 0
## Using the function (to fill the matrix with IUCN data for 82 spp)
hab.pref.gard2 <- f.pref.hab (hab.pref.gard1, spp.codes.gard) #creating an matrix with habitat preferences of each species with function f.pref.hab
## Identifing the spp. which IUCN data have no equivalence with LULC class (i.e. with data that not match with artificial, open and forest areas from MAPBIOMAS)
hab.pref.gard3 <- hab.pref.gard2[hab.pref.gard2[,4] == 0 & hab.pref.gard2[,5] == 0 & hab.pref.gard2[,6] == 0, ]
hab.pref.gard3[,4:6] <- 1 # replace NA data for 1 (to maintain original distribution on artificial, open areas and forest areas for species without IUCN data)
filtro1=match(as.character(hab.pref.gard3[,2]),as.character(hab.pref.gard2[,2])) 
hab.pref.gard2=hab.pref.gard2[-c(filtro1),]
hab.pref.gard2
## Editing the matrix of spp. without IUCN data (203 spp)
hab.pref.gard4 <- hab.pref.gard[c(1,3:6,8:10,14:16, 18, 20:30, 32:33, 36:49, 51:61, 63:65, 68, 70, 72:74, 78, 80, 82:85, 87:100, 103:104, 107, 109, 113:115, 117:122, 127:140, 142, 145, 148, 150:152, 155:160, 162:174, 177:188, 190:192, 194:196, 198:208, 211, 213:215, 217:219, 222, 226:229, 232:236, 240:241, 247:251, 253, 255:258, 260:261, 264, 266:268, 270, 276:281, 283, 285),] 
hab.pref.gard4 <- replace(hab.pref.gard4, list = is.na(hab.pref.gard4), values = 1) # replace NA data for 1 (to maintain original distribution on artificial, open areas and forest areas for species without IUCN data)
# Creating a final (total) matrix 
pref.hab.spp.rgard <- rbind(hab.pref.gard2, hab.pref.gard3, hab.pref.gard4) # Join the two matrix (spp with and without IUCN data)
pref.hab.spp.rgard <- pref.hab.spp.rgard[order(as.numeric(as.factor(pref.hab.spp.rgard[,2]))),] # reorder in alphabetical order
## Saving the habitat matrix of species
write.table(pref.hab.spp.rgard, file="HabMat_RepsGard_FILLED.csv", sep=";") #saving the matrix with habitat preferences of each species

#### STEP 5 - Reclassifying the land cover to obtain equivalence with the IUCN habitat categories #### 
landcover.reclass <- reclassify(landcover.caat, tab.reclass)
writeRaster(landcover.reclass, filename="landcoverReclass.tif")
plot(landcover.reclass)

#### STEP 6 - Refining the individual raster according to the land cover and habitat preferences ##### 
## Preparing data
categs.pref <- c(0,1,2,3,4)
landcover.reclass <- raster("landcoverReclass.tif") # uploading the reclassified land cover
elev.caat # raster already loaded with file .RData
elev.caat <- raster("TopoCaat_resample.asc")
pref.hab.spp.rgard  # table created in the STEP 4
pref.elev.spp # renamed object from table already loaded with file .RData 
pref.elev.spp.rgard <- as.matrix(pref.elev.spp)
pref.elev.spp.rgard <- matrix(as.numeric(pref.elev.spp.rgard[,3:4]), ncol = 2 )
range.orig.rgard <- raster::stack(list.files(path="   ", pattern = "asc$", full.names = F)) #raster files created in the STEP 2

## Using the function
f.habelev.refgard(range.orig.rgard, pref.hab.spp.rgard, pref.elev.spp.rgard)

3.3 AVES

3.3.1 Loading data

avdata <- "https://github.com/thaisdoria/TD_Thesis_Chapter1_RData/blob/master/DoriaTAF_Aves.RData?raw=true"
load(url(avdata)) # RData file created on 2017/2018 containing the files listed bellow:

#### Running steps ####
unifiedshp.aves <- AVESCAAT #aves species distribution (IUCN data)
shape.caat <- caatinga #shapefile of the region 
landcover.caat <- mpbio.ori #raster of the region landcover 
tab.reclass <- tab.reclass #table for reclassify landcover function
pref.hab.spp <- habnull.av #table containing only preferences data on water bodies (manually assigned following specific criteria) that will be filled with habitat preferences from IUCN
elev.caat <- elev.resample #raster of the region elevation
pref.elev.spp <- elevspp.av #table with the interval of elevation in which each species occurs (data compiled manually from IUCN database)

ATTENTION: polygons from IUCN were downloaded at 2017/2018 and after that the genus of specie “Phalacrocorax brasilianus” was modified for “Nannopterum”. Thus, to run this script adequately and obtain correct correspondences between the species names from shapefiles and the most recent habitat data from IUCN database it is recommended to download the up-dated polygons and not use those available in this RData.

3.3.2 Running steps

#### STEP 1 - Creating an individual shapefile for each species distribution ####
data <- unifiedshp.aves # repeat this step to all groups (amphibians, reptiles, mammals, aves)
names(data) #used to verify the column that contained the species names
unique <- sort(unique(data@data$SCINAME)) #accessing 'SCINAME', column that contain the species names, and saving these names removing the duplicate ones
for (i in 1:length(unique)) {
  tmp <- data[data$SCINAME== unique[i], ] 
  writeOGR(tmp, dsn=getwd(), unique[i], driver="ESRI Shapefile",
           overwrite_layer=TRUE)
}

#### STEP 2 - Creating an individual raster for each species distribution #### 
extent(landcover.caat) # this information was used to create a reference raster, the chosen resolution was 300m (0.002777778 x 0.002777778 degree)
r0.05 <- raster(res=0.05, xmn=-44.50838, xmx=-35.17174, ymn=-16.08856, ymx=-2.783879)
r0.05[] <- 1:ncell(r0.05)
r0.05 # 266, 187, 49742 (nrow, ncol, ncell) 

lista.spp.av <- list.files(pattern = ".shp$", full.names = F) # list of aves shapefiles 

# Presence/Absence
for (i in 1:length(lista.spp.av)) {
  spp <- shapefile(lista.spp.av[i])
  spp.r <- rasterize(spp, r0.05, background = 0, getCover=T)
  spp.r[spp.r > 1] <- 1
  spp.c <- crop(spp.r, shape.caat) 
  spp.m <- mask(spp.c, shape.caat)
  e <- extent(-44.8, -34.8, -16.6, -2.2) 
  spp.e <- extend(spp.m,e)
  nome <- unique[i]
  writeRaster(spp.e, paste(nome, "0.05.asc", sep = ""), format = "ascii", overwrite = T)
} 

#### STEP 3 - Obtaining information of habitat species preferences data from IUCN #### 
spp.nomes <- as.matrix(unique) #remember: the 'unique' object was created in the STEP 1
f.hab.redlist <- function(spp.nomes){
  hab.spp <- rl_habitats(spp.nomes, key="99ad94015eeccd35abe9f869c633e1ebad67ee390ab444902eead0c309c800d5")
  return(hab.spp$result$code)
}

lista.hab <- lapply(1:length(spp.nomes),function(f0) f.hab.redlist(spp.nomes[f0])) #creating an object with all information about species habitat preference running 'hab_redlist' function which returns only the information about preference, while 'rl_habitats' function returns in addition the name of the species  
spp.codes <- do.call(rbind, lista.hab) # transforming the list with habitat codes of species in a matrix of habitat codes 

#### STEP 4 - Editing the habitat preference to obatin correspondence with the landcover categories #### 
## Preparing the matrix
pref.hab.spp # table already loaded with file .RData
hab.pref <- as.matrix(pref.hab.spp)
# There are IUCN data for all aves species
hab.pref1 <- replace(hab.pref, list = is.na(hab.pref), values = 0) #replace NA for 0
## Using the function
hab.pref2 <- f.pref.hab (hab.pref1, spp.codes) #creating an matrix with habitat preferences of each species with function f.pref.hab
## Identifing the spp. which IUCN data have no equivalence with LULC class (i.e. with data that not match with artificial, open and forest areas from MAPBIOMAS)
hab.pref3 <- hab.pref2[hab.pref2[,4] == 0 & hab.pref2[,5] == 0 & hab.pref2[,6] == 0, ]
hab.pref3[,4:6] <- 1 # replace NA data for 1 (to maintain original distribution on artificial, open areas and forest areas for species without IUCN data)
filtro1=match(as.character(hab.pref3[,2]),as.character(hab.pref2[,2])) 
hab.pref2=hab.pref2[-c(filtro1),]
# Creating a final (total) matrix 
pref.hab.spp.av <- rbind(hab.pref2, hab.pref3) # Join the two matrix (spp with and without IUCN data)
pref.hab.spp.av <- pref.hab.spp.av[order(as.numeric(as.factor(pref.hab.spp.av[,2]))),] # reorder in alphabetical order
## Saving the habitat matrix of species
write.table(pref.hab.spp.m, file="HabMat_Aves_FILLED.csv", sep=";") #saving the matrix with habitat preferences of each species

#### STEP 5 - Reclassifying the land cover to obtain equivalence with the IUCN habitat categories #### 
landcover.reclass <- reclassify(landcover.caat, tab.reclass)
writeRaster(landcover.reclass, filename="landcoverReclass.tif")

#### STEP 6 - Refining the individual raster according to the land cover and habitat preferences ##### 
## Preparing data
categs.pref <- c(0,1,2,3,4)
landcover.reclass <- raster("landcoverReclass.tif") # uploading the reclassified land cover 
elev.caat #raster already loaded with file .RData
pref.hab.spp.av  # table created in the STEP 4
pref.elev.spp # renamed object from table already loaded with file .RData 
pref.elev.spp.av <- as.matrix(pref.elev.spp)
pref.elev.spp.av <- matrix(as.numeric(pref.elev.spp.av[, 2:3]), ncol = 2 )
range.orig.av <- raster::stack(list.files(path="  ", pattern = "asc$", full.names = F))

## Using the function
f.habelev.ref(range.orig.av, pref.hab.spp.av, pref.elev.spp.av)

3.4 MAMMALIA

3.4.1 Loading data

mdata <- "https://github.com/thaisdoria/TD_Thesis_Chapter1_RData/blob/master/DoriaTAF_Mammals.RData?raw=true"
load(url(mdata)) # RData file created on 2017/2018 containing the files listed bellow:

unifiedshp.mammals <- MAMICAAT #mammals species distribution (IUCN data)
shape.caat <- caatinga #shapefile of the region 
landcover.caat <- mpbio.ori #raster of the region landcover 
tab.reclass <- tab.reclass #table for reclassify landcover function
pref.hab.spp <- habnull.m #table containing only preferences data on water bodies (manually assigned following specific criteria) that will be filled with habitat preferences from IUCN
elev.caat <- elev.resample #raster of the region elevation
pref.elev.spp <- elevspp.m #table with the interval of elevation in which each species occurs (data compiled manually from IUCN database)

ATTENTION: after the polygons from IUCN were downloaded at 2017/2018 and after that the genus of specie “Mimon crenulatum” was modified for “Gardnerycteris”. Thus, to run this script adequately and obtain correct correspondences between the species names from shapefiles and the most recent habitat data from IUCN database it is recommended to download the up-dated polygons and not use those available in this RData.

3.4.2 Running steps

#### STEP 1 - Creating an individual shapefile for each species distribution ####
data <- unifiedshp.mammals 
names(data) #used to verify the column that contained the species names
unique <- sort(unique(data@data$binomial)) #accessing 'binomial', column that contain the species names, and saving these names removing the duplicate ones
for (i in 1:length(unique)) {
  tmp <- data[data$binomial== unique[i], ] 
  writeOGR(tmp, dsn=getwd(), unique[i], driver="ESRI Shapefile",
           overwrite_layer=TRUE)
}

#### STEP 2 - Creating an individual raster for each species distribution #### 
extent(landcover.caat) # this information was used to create a reference raster, the chosen resolution was 300m (0.002777778 x 0.002777778 degree)
r0.05 <- raster(res=0.05, xmn=-44.50838, xmx=-35.17174, ymn=-16.08856, ymx=-2.783879)
r0.05[] <- 1:ncell(r0.05)
r0.05  # 266, 187, 49742 (nrow, ncol, ncell) 

lista.spp.m <- list.files(pattern = ".shp$", full.names = F) #list of mammals shapefiles 

# Presence/Absence
for (i in 1:length(lista.spp.m)) {
  spp <- shapefile(lista.spp.m[i])
  spp.r <- rasterize(spp, r0.05, background = 0, getCover=T)
  spp.r[spp.r > 1] <- 1
  spp.c <- crop(spp.r, shape.caat) 
  spp.m <- mask(spp.c, shape.caat)
  e <- extent(-44.8, -34.8, -16.6, -2.2) 
  spp.e <- extend(spp.m,e)
  nome <- unique[i]
  writeRaster(spp.e, paste(nome, "0.05.asc", sep = ""), format = "ascii", overwrite = T)
} 

#### STEP 3 - Obtaining information of habitat species preferences data from IUCN #### 
spp.nomes <- as.matrix(unique) #remember: the 'unique' object was created in the STEP 1
f.hab.redlist <- function(spp.nomes){
  hab.spp <- rl_habitats(spp.nomes, key="99ad94015eeccd35abe9f869c633e1ebad67ee390ab444902eead0c309c800d5")
  return(hab.spp$result$code)
}

lista.hab <- lapply(1:length(spp.nomes),function(f0) f.hab.redlist(spp.nomes[f0])) #creating an object with all information about species habitat preference running 'hab_redlist' function which returns only the information about preference, while 'rl_habitats' function returns in addition the name of the species  
spp.codes <- do.call(rbind, lista.hab) # transforming the list with habitat codes of species in a matrix of habitat codes

#### STEP 4 - Editing the habitat preference to obatin correspondence with the landcover categories #### 
## Preparing the matrix
pref.hab.spp # table already loaded with file .RData
hab.pref <- as.matrix(pref.hab.spp)
# There are IUCN data for all 208 mammals
hab.pref1 <- replace(hab.pref, list = is.na(hab.pref), values = 0) #replace NA for 0
## Using the function
hab.pref2 <- f.pref.hab (hab.pref1, spp.codes) #creating an matrix with habitat preferences of each species with function f.pref.hab
## Identifing the spp. which IUCN data have no equivalence with LULC class (i.e. with data that not match with artificial, open and forest areas from MAPBIOMAS)
hab.pref3 <- hab.pref2[hab.pref2[,4] == 0 & hab.pref2[,5] == 0 & hab.pref2[,6] == 0, ]
hab.pref3[,4:6] <- 1 # replace NA data for 1 (to maintain original distribution on artificial, open areas and forest areas for species without IUCN data)
filtro1=match(as.character(hab.pref3[,2]),as.character(hab.pref2[,2])) 
hab.pref2=hab.pref2[-c(filtro1),]
# Creating a final (total) matrix 
pref.hab.spp.m <- rbind(hab.pref2, hab.pref3) # Join the two matrix (spp with and without IUCN data)
pref.hab.spp.m <- pref.hab.spp.m[order(as.numeric(as.factor(pref.hab.spp.m[,2]))),] # reorder in alphabetical order
## Saving the habitat matrix of species
write.table(pref.hab.spp.m, file="HabMat_Mammals_FILLED.csv", sep=";") #saving the matrix with habitat preferences of each species

#### STEP 5 - Reclassifying the land cover to obtain equivalence with the IUCN habitat categories #### 
landcover.reclass <- reclassify(landcover.caat, tab.reclass)
writeRaster(landcover.reclass, filename="landcoverReclass.tif")

#### STEP 6 - Refining the individual raster according to the land cover and habitat preferences ##### 
## Preparing data
categs.pref <- c(0,1,2,3,4)
landcover.reclass <- raster("landcoverReclass.tif") # uploading the reclassified land cover
elev.caat # raster already loaded with file .RData
pref.hab.spp.m  # table created in the STEP 4
pref.elev.spp # renamed object from table already loaded with file .RData 
pref.elev.spp.m <- as.matrix(pref.elev.spp)
pref.elev.spp.m <- matrix(as.numeric(pref.elev.spp.m[, 2:3]), ncol = 2 )
range.orig.m <- raster::stack(list.files(path="   ", pattern = "asc$", full.names = F)) #raster files created in the STEP 2

# Using the function
f.habelev.ref(range.orig.m, pref.hab.spp.m, pref.elev.spp.m)
LS0tDQp0aXRsZTogIlIgY29kZXMgdG8gbWFwIHNwZWNpZXMnIEFyZWEgb2YgSGFiaXRhdCAoQU9IKSINCmF1dGhvcjogIlRoYcOtcyBBbmRyYWRlIEZlcnJlaXJhIETDs3JpYSINCmRhdGU6ICdgciBTeXMuRGF0ZSgpYCcNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvYzogeWVzDQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgcGRmX2RvY3VtZW50Og0KICAgIHRvYzogeWVzDQotLS0NCg0KDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCiMgREVTQ1JJUFRJT04NCg0KVGhpcyBSIE1hcmtkb3duIGRvY3VtZW50IHByZXNlbnRzIHRoZSBmdW5jdGlvbnMgYW5kIHRoZSBzdGVwcyB1c2VkIHRvIGVzdGltYXRlIHNwZWNpZXMnIEFyZWEgb2YgSGFiaXRhdCAoQU9IKSBhcyBkZXNjcmliZWQgYnkgRMOzcmlhICYgRG9icm92b2xza2kgKDIwMjApIGluIHRoZSBwYXBlciAqKioiSW1wcm92aW5nIHBvc3QtMjAyMCBjb25zZXJ2YXRpb24gb2YgdGVycmVzdHJpYWwgdmVydGVicmF0ZXMgaW4gQ2FhdGluZ2EiKioqLg0KDQoNCiMgUkVRVUlSRU1FTlRTDQoNCiMjIEluc3RhbGwgYW5kIGxvYWQgdGhlIHJlcXVpcmVkIHBhY2thZ2VzDQpgYGB7ciBldmFsPUZ9DQppbnN0YWxsLnBhY2thZ2VzICgnc3AnKQ0KaW5zdGFsbC5wYWNrYWdlcyAoJ3Jhc3RlcicpDQppbnN0YWxsLnBhY2thZ2VzICgnbWFwdG9vbHMnKQ0KaW5zdGFsbC5wYWNrYWdlcyAoJ21hcHMnKQ0KaW5zdGFsbC5wYWNrYWdlcyAoJ3J3b3JsZG1hcCcpDQppbnN0YWxsLnBhY2thZ2VzICgnY29sb3JSYW1wcycpDQppbnN0YWxsLnBhY2thZ2VzICgncmdkYWwnKQ0KaW5zdGFsbC5wYWNrYWdlcyAoJ2hlcmUnKQ0KDQpgYGANCg0KYGBge3J9DQpsaWJyYXJ5IChzcCkNCmxpYnJhcnkgKHJhc3RlcikNCmxpYnJhcnkgKG1hcHRvb2xzKQ0KbGlicmFyeSAobWFwcykNCmxpYnJhcnkgKHJ3b3JsZG1hcCkNCmxpYnJhcnkgKGNvbG9yUmFtcHMpDQpsaWJyYXJ5IChyZ2RhbCkNCmxpYnJhcnkgKGhlcmUpDQpgYGANCg0KIyMgTG9hZCB0aGUgcmVxdWlyZWQgZnVuY3Rpb25zDQoNCiogMS4gVG8gZG93bmxvYWQgaGFiaXRhdCBwcmVmZXJlbmNlcyBmcm9tIElVQ04gZGF0YWJhc2UNCmBgYHtyfQ0KZi5oYWIucmVkbGlzdCA8LSBmdW5jdGlvbih6KXsNCiAgaGFiLnNwcCA8LSBybF9oYWJpdGF0cyh6LCBrZXk9IjM4ZTkxMzI1NmFjODFiMDE1OWJjNTQyMWI2MzNhZDQ2N2I5NGJlNjdlOWM4ODZhM2NhODZjY2M3ODcxMGVmNTAiKQ0KICByZXR1cm4oaGFiLnNwcCRyZXN1bHQkY29kZSkNCn0NCiNOb3RlczoNCiMneicgaW4gdGhpcyBmdW5jdGlvbiBpcyBtYXRyaXggd2l0aCBzcGVjaWUncyBuYW1lcyANCiNpbiBybF9oYWJpdGF0cyBmdW5jdGlvbiwgdGhlIGFyZ3VtZW50ICdrZXknIG11c3QgYmUgcmVxdWVzdGVkIGZyb20gSVVDTi4NCmBgYA0KDQoqIDIuIFRvIGNyZWF0ZSBhIG1hdHJpeCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgb2Ygc3BlY2llcw0KYGBge3J9DQpmLnByZWYuaGFiIDwtIGZ1bmN0aW9uKHksIHgpeyANCiAgZm9yIChpIGluIDE6bnJvdyh5KSkNCiAgICB5W2ksImFydGlmIl0gPC0gaWZlbHNlKHN1bSh4W2ksXT4xNCAmIHhbaSxdPDE1KSwxLDApDQogIHlhcnQgPC0geQ0KICB7IA0KICAgIGZvciAoaSBpbiAxOm5yb3coeWFydCkpIA0KICAgICAgeWFydFtpLCJvcGVuYSJdIDwtIGlmZWxzZShzdW0oeFtpLF0+MiAmIHhbaSxdPDUgfCB4W2ksXT49MTIuMiAmIHhbaSxdPD0xMi4zIHwgeFtpLF09PTEzLjMpLDEsMCkNCiAgICB5b3BlbiA8LSB5YXJ0ICANCiAgICB7IA0KICAgICAgZm9yIChpIGluIDE6bnJvdyh5b3BlbikpIA0KICAgICAgICB5b3BlbltpLCJmb3JlcyJdIDwtIGlmZWxzZShzdW0oeFtpLF08PTEuOSksMSwwKQ0KICAgICByZXR1cm4oeW9wZW4pfX0NCiAgfQ0KIyBOb3RlczoNCiMgJ3knIGluIHRoaXMgZnVuY3Rpb24gaXMgYSBtYXRyaXggd2l0aCBjYXRlZ29yaWVzIG9mIGhhYml0YXQgdGhhdCB3aWxsIGJlIGNvbnNpZGVyZWQgYXMgcHJlZmVyZW5jZSB0byBlYWNoIHNwZWNpZXMNCiMgJ3gnIGluIHRoaXMgZnVuY3Rpb24gaXMgYSBtYXRyaXggd2l0aCBzcGVjaWVzIGNvZGUncyBpbmRpY2F0aW5nIGhhYml0YXQgcHJlZmVyZW5jZSBhY2NvcmRpbmcgdG8gSVVDTiBkYXRhYmFzZQ0KYGBgDQoNCiogMy4gVG8gcmVmaW5lIHJhbmdlcyBvZiBkaXN0cmlidXRpb24gYnkgdGhlIGhhYml0YXQgYW5kIGVsZXZhdGlvbiBwcmVmZXJlbmNlcyBvZiBzcGVjaWVzDQpgYGB7cn0NCmYuaGFiZWxldi5yZWYgPC0gZnVuY3Rpb24oYSwgYiwgYykgew0KICBuc3AgPC0gbmxheWVycyhhKQ0KICBmb3IgIChpIGluIDE6bnNwKSAgew0KICAgIHRhYjIgPC0gY2JpbmQoY2F0ZWdzLnByZWYsIChhcy5udW1lcmljKGJbaSwzOjddKSkpIA0KICAgIGxhbmRjb3Zlci5yZWNsYXNzMiA8LSByZWNsYXNzaWZ5KGxhbmRjb3Zlci5yZWNsYXNzLCB0YWIyLCBwcm9ncmVzcz0idGV4dCIpIA0KICAgIGlmKGNbaSwyXSE9MCl7DQogICAgICBlbGV2LnJlY2xhc3MgPC0gZWxldi5jYWF0Pj1jW2ksMV0mZWxldi5jYWF0PD1jW2ksMl0NCiAgICAgIGxhbmRjb3Zlci5yZWNsYXNzMiA8LSBvdmVybGF5KGxhbmRjb3Zlci5yZWNsYXNzMixlbGV2LnJlY2xhc3MsZnVuPWZ1bmN0aW9uICh4LHkpe3gqeX0sIHByb2dyZXNzPSJ0ZXh0IikNCiAgICB9DQogICAgbGFuZGVsZXYucmVjbGFzcy5yZXMgPC0gYWdncmVnYXRlKGxhbmRjb3Zlci5yZWNsYXNzMiwgZnVuPXN1bSwgZmFjdD0xNzIsIHByb2dyZXNzPSJ0ZXh0IikgIzAuMDUgcmVzb2x1dGlvbg0KICAgIGxhbmRlbGV2LmFiZnJhYyA8LSBsYW5kZWxldi5yZWNsYXNzLnJlcy8yOTU4NA0KICAgIHNwcCA8LSBhW1tpXV0NCiAgICBwcm9qIDwtIENSUygiK3Byb2o9bG9uZ2xhdCArZGF0dW09V0dTODQgK2VsbHBzPVdHUzg0ICt0b3dnczg0PTAsMCwwIikgICAgICAgDQogICAgcHJvajRzdHJpbmcoc3BwKSA8LSBwcm9qIA0KICAgIHJlc2FtcGxlc3AgPC0gcmVzYW1wbGUobGFuZGVsZXYuYWJmcmFjLHNwcCxtZXRob2Q9Im5nYiIsIHByb2dyZXNzPSJ0ZXh0IikNCiAgICBtdWx0IDwtIG92ZXJsYXkocmVzYW1wbGVzcCxzcHAsZnVuPWZ1bmN0aW9uICh4LHkpe3gqeX0pDQogICAgbm9tZSA8LSB1bmlxdWVbaV0gI3NwZWNpZXMgbGlzdCBjcmVhdGVkIGluIFNURVAgMQ0KICAgIHdyaXRlUmFzdGVyKG11bHQscGFzdGUobm9tZSwiMC4wNV9IU01zLmFzYyIsc2VwID0gIiIpLGZvcm1hdD0iYXNjaWkiKQ0KICB9fSANCiNOb3RlczoNCiMgJ2EnIGluIHRoaXMgZnVuY3Rpb24gaXMgdGhlIG9yaWdpbmFsIHJhc3RlciBvZiBzcGVjaWVzIA0KIyAnYicgaW4gdGhpcyBmdW5jdGlvbiBpcyBtYXRyaXggb2YgaGFiaXRhdCdzIHByZWZlcmVuY2Ugb2Ygc3BlY2llcyAgDQojICdjJyBpbiB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBtYXRyaXggb2YgZWxldmF0aW9uJ3MgcHJlZmVyZW5jZSBvZiBzcGVjaWVzDQoNCmBgYA0KDQojIFBFUkZPUk1JTkcgVEhFIEFOQUxZU0lTICMNCg0KIyMgQU1QSElCSUENCiMjIyBMb2FkaW5nIGRhdGENCmBgYHtyfQ0KYWRhdGEgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS90aGFpc2RvcmlhL1REX1RoZXNpc19DaGFwdGVyMV9SRGF0YS9ibG9iL21hc3Rlci9Eb3JpYVRBRl9BbXBoaWJpYW5zLlJEYXRhP3Jhdz10cnVlIg0KbG9hZCh1cmwoYWRhdGEpKSAjIFJEYXRhIGZpbGUgY3JlYXRlZCBvbiAyMDE3LzIwMTggY29udGFpbmluZyB0aGUgZmlsZXMgbGlzdGVkIGJlbGxvdw0KdW5pZmllZHNocC5hbXBoaWJpYW5zIDwtIEFNUEhDQUFUICNhbXBoaWJpYW5zIHNwZWNpZXMgZGlzdHJpYnV0aW9uIChJVUNOIGRhdGEpDQpzaGFwZS5jYWF0IDwtIGNhYXRpbmdhICNzaGFwZWZpbGUgb2YgdGhlIHJlZ2lvbiANCmxhbmRjb3Zlci5jYWF0IDwtIG1wYmlvLm9yaSAjcmFzdGVyIG9mIHRoZSByZWdpb24gbGFuZGNvdmVyIA0KdGFiLnJlY2xhc3MgPC0gdGFiLnJlY2xhc3MgI3RhYmxlIGZvciByZWNsYXNzaWZ5IGxhbmRjb3ZlciANCnByZWYuaGFiLnNwcCA8LSBoYWJudWxsLmEgI3RhYmxlIGNvbnRhaW5pbmcgb25seSBwcmVmZXJlbmNlcyBkYXRhIG9uIHdhdGVyIGJvZGllcyAobWFudWFsbHkgYXNzaWduZWQgZm9sbG93aW5nIHNwZWNpZmljIGNyaXRlcmlhKSB0aGF0IHdpbGwgYmUgZmlsbGVkIHdpdGggaGFiaXRhdCBwcmVmZXJlbmNlcyBmcm9tIElVQ04NCmVsZXYuY2FhdCA8LSBlbGV2LnJlc2FtcGxlICNyYXN0ZXIgb2YgdGhlIHJlZ2lvbiBlbGV2YXRpb24NCnByZWYuZWxldi5zcHAgPC0gZWxldnNwcC5hICN0YWJsZSB3aXRoIHRoZSBpbnRlcnZhbCBvZiBlbGV2YXRpb24gaW4gd2hpY2ggZWFjaCBzcGVjaWVzIG9jY3VycyAoZGF0YSBjb21waWxlZCBtYW51YWxseSBmcm9tIElVQ04gZGF0YWJhc2UpDQpgYGANCkFUVEVOVElPTjogcG9seWdvbnMgZnJvbSBJVUNOIHdlcmUgZG93bmxvYWRlZCBhdCAyMDE3LzIwMTggYW5kIGFmdGVyIHRoYXQgdGhlIGdlbnVzIG9mIHNwZWNpZXMgKiJIeXBzaWJvYXMgYWxib21hcmdpbmF0dXMiKiwgKiJIeXBzaWJvYXMgYWxib3B1bmN0YXR1cyIqLCAqIkh5cHNpYm9hcyBhdGxhbnRpY3VzIiosIHdhcyBtb2RpZmllZCBmb3IgKiJCb2FuYSIqLiBUaHVzLCB0byBydW4gdGhpcyBzY3JpcHQgYWRlcXVhdGVseSBhbmQgb2J0YWluIGNvcnJlY3QgY29ycmVzcG9uZGVuY2VzIGJldHdlZW4gdGhlIHNwZWNpZXMgbmFtZXMgZnJvbSBzaGFwZWZpbGVzIGFuZCB0aGUgbW9zdCByZWNlbnQgaGFiaXRhdCBkYXRhIGZyb20gSVVDTiBkYXRhYmFzZSBpdCBpcyByZWNvbW1lbmRlZCB0byBkb3dubG9hZCB0aGUgdXAtZGF0ZWQgcG9seWdvbnMgYW5kIG5vdCB1c2UgdGhvc2UgYXZhaWxhYmxlIGluIHRoaXMgUkRhdGEuIA0KDQojIyMgUnVubmluZyBzdGVwcw0KYGBge3IsIGV2YWw9Rn0NCiMjIyMjIFNURVAgMSAtIENyZWF0aW5nIGFuIGluZGl2aWR1YWwgc2hhcGVmaWxlIGZvciBlYWNoIHNwZWNpZXMgZGlzdHJpYnV0aW9uICMjIyMNCmRhdGEgPC0gdW5pZmllZHNocC5hbXBoaWJpYW5zIA0KbmFtZXMoZGF0YSkgI3VzZWQgdG8gdmVyaWZ5IHRoZSBjb2x1bW4gdGhhdCBjb250YWluZWQgdGhlIHNwZWNpZXMgbmFtZXMNCnVuaXF1ZSA8LSBzb3J0KHVuaXF1ZShkYXRhQGRhdGEkYmlub21pYWwpKSAjYWNjZXNzaW5nICdiaW5vbWlhbCcsIGNvbHVtbiB0aGF0IGNvbnRhaW4gdGhlIHNwZWNpZXMgbmFtZXMsIGFuZCBzYXZpbmcgdGhlc2UgbmFtZXMgcmVtb3ZpbmcgdGhlIGR1cGxpY2F0ZSBvbmVzDQpmb3IgKGkgaW4gMTpsZW5ndGgodW5pcXVlKSkgew0KICB0bXAgPC0gZGF0YVtkYXRhJGJpbm9taWFsPT0gdW5pcXVlW2ldLCBdIA0KICB3cml0ZU9HUih0bXAsIGRzbj1nZXR3ZCgpLCB1bmlxdWVbaV0sIGRyaXZlcj0iRVNSSSBTaGFwZWZpbGUiLA0KICAgICAgICAgICBvdmVyd3JpdGVfbGF5ZXI9VFJVRSkNCn0NCg0KIyMjIyBTVEVQIDIgLSBDcmVhdGluZyBhbiBpbmRpdmlkdWFsIHJhc3RlciBmb3IgZWFjaCBzcGVjaWVzIGRpc3RyaWJ1dGlvbiAjIyMjIA0KZXh0ZW50KGxhbmRjb3Zlci5jYWF0KSANCnIwLjA1IDwtIHJhc3RlcihyZXM9MC4wNSwgeG1uPS00NC41MDgzOCwgeG14PS0zNS4xNzE3NCwgeW1uPS0xNi4wODg1NiwgeW14PS0yLjc4Mzg3OSkNCnIwLjA1W10gPC0gMTpuY2VsbChyMC4wNSkNCnIwLjA1ICMgMjY2LCAxODcsIDQ5NzQyIChucm93LCBuY29sLCBuY2VsbCkgDQoNCmxpc3RhLnNwcC5hIDwtIGxpc3QuZmlsZXMocGF0dGVybiA9ICIuc2hwJCIsIGZ1bGwubmFtZXMgPSBGKSAjbGlzdCBvZiBhbXBoaWJpYW5zIHNoYXBlZmlsZXMgDQoNCiMgUHJlc2VuY2UvQWJzZW5jZQ0KZm9yIChpIGluIDE6bGVuZ3RoKGxpc3RhLnNwcC5hKSkgew0KICBzcHAgPC0gc2hhcGVmaWxlKGxpc3RhLnNwcC5hW2ldKQ0KICBzcHAuciA8LSByYXN0ZXJpemUoc3BwLCByMC4wNSwgYmFja2dyb3VuZCA9IDAsIGdldENvdmVyPVQpDQogIHNwcC5yW3NwcC5yID4gMV0gPC0gMQ0KICBzcHAuYyA8LSBjcm9wKHNwcC5yLCBzaGFwZS5jYWF0KSANCiAgc3BwLm0gPC0gbWFzayhzcHAuYywgc2hhcGUuY2FhdCkNCiAgZSA8LSBleHRlbnQoLTQ0LjgsIC0zNC44LCAtMTYuNiwgLTIuMikgDQogIHNwcC5lIDwtIGV4dGVuZChzcHAubSxlKQ0KICBub21lIDwtIHVuaXF1ZVtpXQ0KICB3cml0ZVJhc3RlcihzcHAuZSwgcGFzdGUobm9tZSwgIjAuMDUuYXNjIiwgc2VwID0gIiIpLCBmb3JtYXQgPSAiYXNjaWkiLCBvdmVyd3JpdGUgPSBUKQ0KfSANCg0KIyMjIyBTVEVQIDMgLSBPYnRhaW5pbmcgaW5mb3JtYXRpb24gb2YgaGFiaXRhdCBzcGVjaWVzIHByZWZlcmVuY2VzIGRhdGEgZnJvbSBJVUNOICMjIyMgDQpzcHAubm9tZXMgPC0gYXMubWF0cml4KHVuaXF1ZSkgI3JlbWVtYmVyOiB0aGUgJ3VuaXF1ZScgb2JqZWN0IHdhcyBjcmVhdGVkIGluIHRoZSBTVEVQIDENCmYuaGFiLnJlZGxpc3QgPC0gZnVuY3Rpb24oc3BwLm5vbWVzKXsNCiAgaGFiLnNwcCA8LSBybF9oYWJpdGF0cyhzcHAubm9tZXMsIGtleT0iOTlhZDk0MDE1ZWVjY2QzNWFiZTlmODY5YzYzM2UxZWJhZDY3ZWUzOTBhYjQ0NDkwMmVlYWQwYzMwOWM4MDBkNSIpDQogIHJldHVybihoYWIuc3BwJHJlc3VsdCRjb2RlKQ0KfQ0KDQpsaXN0YS5oYWIgPC0gbGFwcGx5KDE6bGVuZ3RoKHNwcC5ub21lcyksZnVuY3Rpb24oZjApIGYuaGFiLnJlZGxpc3Qoc3BwLm5vbWVzW2YwXSkpICNjcmVhdGluZyBhbiBvYmplY3Qgd2l0aCBhbGwgaW5mb3JtYXRpb24gYWJvdXQgc3BlY2llcyBoYWJpdGF0IHByZWZlcmVuY2UgcnVubmluZyAnaGFiX3JlZGxpc3QnIGZ1bmN0aW9uIHdoaWNoIHJldHVybnMgb25seSB0aGUgaW5mb3JtYXRpb24gYWJvdXQgcHJlZmVyZW5jZSwgd2hpbGUgJ3JsX2hhYml0YXRzJyBmdW5jdGlvbiByZXR1cm5zIGluIGFkZGl0aW9uIHRoZSBuYW1lIG9mIHRoZSBzcGVjaWVzICANCnNwcC5jb2RlcyA8LSBkby5jYWxsKHJiaW5kLCBsaXN0YS5oYWIpICMgdHJhbnNmb3JtaW5nIHRoZSBsaXN0IHdpdGggaGFiaXRhdCBjb2RlcyBvZiBzcGVjaWVzIGluIGEgbWF0cml4IG9mIGhhYml0YXQgY29kZXMgDQoNCiMjIyMgU1RFUCA0IC0gRWRpdGluZyB0aGUgaGFiaXRhdCBwcmVmZXJlbmNlIHRvIG9iYXRpbiBjb3JyZXNwb25kZW5jZSB3aXRoIHRoZSBsYW5kY292ZXIgY2F0ZWdvcmllcyAjIyMjIA0KIyMgUHJlcGFyaW5nIHRoZSBtYXRyaXgNCnByZWYuaGFiLnNwcCAjIHRhYmxlIGFscmVhZHkgbG9hZGVkIHdpdGggZmlsZSAuUkRhdGENCmhhYi5wcmVmIDwtIGFzLm1hdHJpeChwcmVmLmhhYi5zcHApDQojIFJlbW92ZSBsaW5lcyBmb3Igd2hpY2ggdGhlcmUgaXMgbm8gYXZhaWxhYmxlIElVQ04gZGF0YSAodmVyaWZ5IHRoaXMgb24gb2JqZXRjICdsaXN0YS5oYWInKSANCmhhYi5wcmVmMSA8LSBoYWIucHJlZlstMTA5LF0gIyB0aGUgbnVtYmVyIGhlcmUgY29ycmVzcG9uZHMgdG8gbGluZXMgY29ycmVzcG9uZGluZyB0byBhIHNwZWNpZXMgZm9yIHdoaWNoIHRoZSBoYWJpdGF0IHByZWZlcmVuY2UgaW5mb3JtYXRpb24gd2FzIGVtcHR5LCB0aGF0IGlzLCB0aGVyZSB3YXNuJ3QgaGFiaXRhdCBwcmVmZXJlbmNlIGluZm9ybWF0aW9uIGZvciB0aGlzIHNwZWNpZXMgaW4gSVVDTiBkYXRhYmFzZSAgDQpoYWIucHJlZjEgPC0gcmVwbGFjZShoYWIucHJlZjEsIGxpc3QgPSBpcy5uYShoYWIucHJlZjEpLCB2YWx1ZXMgPSAwKSAjcmVwbGFjZSBOQSBmb3IgMA0KIyMgVXNpbmcgdGhlIGZ1bmN0aW9uICh0byBmaWxsIHRoZSBtYXRyaXggd2l0aCBJVUNOIGRhdGEgZm9yIDEyOCBzcHApDQpoYWIucHJlZjIgPC0gZi5wcmVmLmhhYiAoaGFiLnByZWYxLCBzcHAuY29kZXMpICNjcmVhdGluZyBhbiBtYXRyaXggd2l0aCBoYWJpdGF0IHByZWZlcmVuY2VzIG9mIGVhY2ggc3BlY2llcyB3aXRoIGZ1bmN0aW9uIGYucHJlZi5oYWINCiMjIEVkaXRpbmcgdGhlIG1hdHJpeCBvZiBzcHAuIHdpdGhvdXQgSVVDTiBkYXRhICgxIHNwcCkNCmhhYi5wcmVmMyA8LSBoYWIucHJlZlsxMDksXSANCmhhYi5wcmVmMyA8LSByZXBsYWNlKGhhYi5wcmVmMywgbGlzdCA9IGlzLm5hKGhhYi5wcmVmMyksIHZhbHVlcyA9IDEpICMgcmVwbGFjZSBOQSBkYXRhIGZvciAxICh0byBtYWludGFpbiBvcmlnaW5hbCBkaXN0cmlidXRpb24gb24gYXJ0aWZpY2lhbCwgb3BlbiBhcmVhcyBhbmQgZm9yZXN0IGFyZWFzIGZvciBzcGVjaWVzIHdpdGhvdXQgSVVDTiBkYXRhKQ0KIyBDcmVhdGluZyBhIGZpbmFsICh0b3RhbCkgbWF0cml4IA0KcHJlZi5oYWIuc3BwLmEgPC0gcmJpbmQoaGFiLnByZWYyLCBoYWIucHJlZjMpICMgSm9pbiB0aGUgdHdvIG1hdHJpeCAoc3BwIHdpdGggYW5kIHdpdGhvdXQgSVVDTiBkYXRhKQ0KcHJlZi5oYWIuc3BwLmEgPC0gcHJlZi5oYWIuc3BwLmFbb3JkZXIoYXMubnVtZXJpYyhhcy5mYWN0b3IocHJlZi5oYWIuc3BwLmFbLDJdKSkpLF0gIyByZW9yZGVyIGluIGFscGhhYmV0aWNhbCBvcmRlcg0KIyMgU2F2aW5nIHRoZSBoYWJpdGF0IG1hdHJpeCBvZiBzcGVjaWVzDQp3cml0ZS50YWJsZShwcmVmLmhhYi5zcHAuYSwgZmlsZT0iSGFiTWF0X0FtcGhzX0ZJTExFRC5jc3YiLCBzZXA9IjsiLCAgaGVhZGVyPVQpICNzYXZpbmcgdGhlIG1hdHJpeCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgb2YgZWFjaCBzcGVjaWVzDQoNCiMjIyMgU1RFUCA1IC0gUmVjbGFzc2lmeWluZyB0aGUgbGFuZCBjb3ZlciB0byBvYnRhaW4gZXF1aXZhbGVuY2Ugd2l0aCB0aGUgSVVDTiBoYWJpdGF0IGNhdGVnb3JpZXMgIyMjIyANCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJlY2xhc3NpZnkobGFuZGNvdmVyLmNhYXQsIHRhYi5yZWNsYXNzKQ0Kd3JpdGVSYXN0ZXIobGFuZGNvdmVyLnJlY2xhc3MsIGZpbGVuYW1lPSJsYW5kY292ZXJSZWNsYXNzLnRpZiIpDQoNCiMjIyMgU1RFUCA2IC0gUmVmaW5pbmcgdGhlIGluZGl2aWR1YWwgcmFzdGVyIGFjY29yZGluZyB0byB0aGUgbGFuZCBjb3ZlciBhbmQgaGFiaXRhdCBwcmVmZXJlbmNlcyAjIyMjIyANCiMjIFByZXBhcmluZyBkYXRhDQpjYXRlZ3MucHJlZiA8LSBjKDAsMSwyLDMsNCkNCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJhc3RlcigibGFuZGNvdmVyUmVjbGFzcy50aWYiKSAjIHVwbG9hZGluZyB0aGUgcmVjbGFzc2lmaWVkIGxhbmQgY292ZXIgDQplbGV2LmNhYXQgIyByYXN0ZXIgYWxyZWFkeSBsb2FkZWQgd2l0aCBmaWxlIC5SRGF0YQ0KcHJlZi5oYWIuc3BwLmEgIyB0YWJsZSBjcmVhdGVkIGluIHRoZSBTVEVQIDQNCnByZWYuZWxldi5zcHAgIyB0YWJsZSBhbHJlYWR5IGxvYWRlZCB3aXRoIGZpbGUgLlJEYXRhDQpwcmVmLmVsZXYuc3BwLmEgPC0gYXMubWF0cml4KHByZWYuZWxldi5zcHApDQpwcmVmLmVsZXYuc3BwLmEgPC0gbWF0cml4KGFzLm51bWVyaWMocHJlZi5lbGV2LnNwcC5hWywgMjozXSksIG5jb2wgPSAyICkNCnJhbmdlLm9yaWcuYSA8LSByYXN0ZXI6OnN0YWNrKGxpc3QuZmlsZXMocGF0aD0iICAgIiwgcGF0dGVybiA9ICJhc2MkIiwgZnVsbC5uYW1lcyA9IEYpKSAjcmFzdGVyIGZpbGVzIGNyZWF0ZWQgaW4gdGhlIFNURVAgMg0KDQojIyBVc2luZyB0aGUgZnVuY3Rpb24NCmYuaGFiZWxldi5yZWYocmFuZ2Uub3JpZy5hLCBwcmVmLmhhYi5zcHAuYSwgcHJlZi5lbGV2LnNwcC5hKQ0KDQpgYGANCg0KIyMgUkVQVElMSUENCiMjIyBMb2FkaW5nIGRhdGENCmBgYHtyfQ0KcmRhdGEgPC0gImh0dHBzOi8vZ2l0aHViLmNvbS90aGFpc2RvcmlhL1REX1RoZXNpc19DaGFwdGVyMV9SRGF0YS9ibG9iL21hc3Rlci9Eb3JpYVRBRl9SZXB0aWxlc2cuUkRhdGE/cmF3PXRydWUiDQpsb2FkKHVybChyZGF0YSkpICNSRGF0YSBmaWxlIGNyZWF0ZWQgb24gMjAxOSBjb250YWluaW5nIHRoZSBmaWxlcyBsaXN0ZWQgYmVsbG93Og0KdW5pZmllZHNocC5yZXB0aWxlcy5nYXJkIDwtIFJFUENBQVRnYXJkICNyZXB0aWxlcyBzcGVjaWVzIGRpc3RyaWJ1dGlvbiAoR0FSRCBkYXRhKQ0Kc2hhcGUuY2FhdCA8LSBjYWF0aW5nYSAjc2hhcGVmaWxlIG9mIHRoZSByZWdpb24gDQpsYW5kY292ZXIuY2FhdCA8LSBtcGJpby5vcmkgI3Jhc3RlciBvZiB0aGUgcmVnaW9uIGxhbmRjb3ZlciANCnRhYi5yZWNsYXNzIDwtIHRhYi5yZWNsYXNzICN0YWJsZSBmb3IgcmVjbGFzc2lmeSBsYW5kY292ZXIgZnVuY3Rpb24NCnByZWYuaGFiLnNwcCA8LSBoYWJudWxsLnJnICN0YWJsZSBjb250YWluaW5nIG9ubHkgcHJlZmVyZW5jZXMgZGF0YSBvbiB3YXRlciBib2RpZXMgKG1hbnVhbGx5IGFzc2lnbmVkIGZvbGxvd2luZyBzcGVjaWZpYyBjcml0ZXJpYSkgdGhhdCB3aWxsIGJlIGZpbGxlZCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgZnJvbSBJVUNODQplbGV2LmNhYXQgPC0gZWxldi5yZXNhbXBsZSAjcmFzdGVyIG9mIHRoZSByZWdpb24gZWxldmF0aW9uIA0KcHJlZi5lbGV2LnNwcCA8LSBlbGV2c3BwLnJnICN0YWJsZSB3aXRoIHRoZSBpbnRlcnZhbCBvZiBlbGV2YXRpb24gaW4gd2hpY2ggZWFjaCBzcGVjaWVzIG9jY3VycyAoZGF0YSBjb21waWxlZCBtYW51YWxseSBmcm9tIElVQ04gZGF0YWJhc2UpDQpgYGANCg0KIyMjIFJ1bm5pbmcgc3RlcHMNCmBgYHtyLCBldmFsPUZ9DQojIyMjIyBTVEVQIDEgLSBDcmVhdGluZyBhbiBpbmRpdmlkdWFsIHNoYXBlZmlsZSBmb3IgZWFjaCBzcGVjaWVzIGRpc3RyaWJ1dGlvbiAjIyMjDQpkYXRhIDwtIHVuaWZpZWRzaHAucmVwdGlsZXMuZ2FyZCANCm5hbWVzKGRhdGEpICN1c2VkIHRvIHZlcmlmeSB0aGUgY29sdW1uIHRoYXQgY29udGFpbmVkIHRoZSBzcGVjaWVzIG5hbWVzDQp1bmlxdWUuZ2FyZCA8LSBzb3J0KHVuaXF1ZShkYXRhQGRhdGEkQmlub21pYWwpKSAjYWNjZXNzaW5nICdiaW5vbWlhbCcsIGNvbHVtbiB0aGF0IGNvbnRhaW4gdGhlIHNwZWNpZXMgbmFtZXMsIGFuZCBzYXZpbmcgdGhlc2UgbmFtZXMgcmVtb3ZpbmcgdGhlIGR1cGxpY2F0ZSBvbmVzDQpmb3IgKGkgaW4gMTpsZW5ndGgodW5pcXVlLmdhcmQpKSB7DQogIHRtcCA8LSBkYXRhW2RhdGEkQmlub21pYWw9PSB1bmlxdWUuZ2FyZFtpXSwgXSANCiAgd3JpdGVPR1IodG1wLCBkc249Z2V0d2QoKSwgdW5pcXVlLmdhcmRbaV0sIGRyaXZlcj0iRVNSSSBTaGFwZWZpbGUiLA0KICAgICAgICAgICBvdmVyd3JpdGVfbGF5ZXI9VFJVRSkNCn0NCg0KIyMjIyBTVEVQIDIgLSBDcmVhdGluZyBhbiBpbmRpdmlkdWFsIHJhc3RlciBmb3IgZWFjaCBzcGVjaWVzIGRpc3RyaWJ1dGlvbiAjIyMjIA0KZXh0ZW50KGxhbmRjb3Zlci5jYWF0KSAjIHRoaXMgaW5mb3JtYXRpb24gd2FzIHVzZWQgdG8gY3JlYXRlIGEgcmVmZXJlbmNlIHJhc3RlciwgdGhlIGNob3NlbiByZXNvbHV0aW9uIHdhcyAzMDBtICgwLjAwMjc3Nzc3OCB4IDAuMDAyNzc3Nzc4IGRlZ3JlZSkNCnIwLjA1IDwtIHJhc3RlcihyZXM9MC4wNSwgeG1uPS00NC41MDgzOCwgeG14PS0zNS4xNzE3NCwgeW1uPS0xNi4wODg1NiwgeW14PS0yLjc4Mzg3OSkNCnIwLjA1W10gPC0gMTpuY2VsbChyMC4wNSkNCnIwLjA1ICAjIDI2NiwgMTg3LCA0OTc0MiAobnJvdywgbmNvbCwgbmNlbGwpIA0KDQpsaXN0YS5zcHAuci5nYXJkIDwtIGxpc3QuZmlsZXMocGF0dGVybiA9ICIuc2hwJCIsIGZ1bGwubmFtZXMgPSBGKSAjbGlzdCBvZiByZXB0aWxlcyBzaGFwZWZpbGVzIA0KDQojIFByZXNlbmNlL0Fic2VuY2UNCmZvciAoaSBpbiAxOmxlbmd0aChsaXN0YS5zcHAuci5nYXJkKSkgew0KICBzcHAgPC0gc2hhcGVmaWxlKGxpc3RhLnNwcC5yLmdhcmRbaV0pDQogIHNwcC5yIDwtIHJhc3Rlcml6ZShzcHAsIHIwLjA1LCBiYWNrZ3JvdW5kID0gMCwgZ2V0Q292ZXI9VCkNCiAgc3BwLnJbc3BwLnIgPiAxXSA8LSAxDQogIHNwcC5jIDwtIGNyb3Aoc3BwLnIsIHNoYXBlLmNhYXQpIA0KICBzcHAubSA8LSBtYXNrKHNwcC5jLCBzaGFwZS5jYWF0KQ0KICBlIDwtIGV4dGVudCgtNDQuOCwgLTM0LjgsIC0xNi42LCAtMi4yKSANCiAgc3BwLmUgPC0gZXh0ZW5kKHNwcC5tLGUpDQogIG5vbWUgPC0gdW5pcXVlLmdhcmRbaV0NCiAgd3JpdGVSYXN0ZXIoc3BwLmUsIHBhc3RlKG5vbWUsICIwLjA1Zy5hc2MiLCBzZXAgPSAiIiksIGZvcm1hdCA9ICJhc2NpaSIsIG92ZXJ3cml0ZSA9IFQpDQp9IA0KDQojIyMjIFNURVAgMyAtIE9idGFpbmluZyBpbmZvcm1hdGlvbiBvZiBoYWJpdGF0IHNwZWNpZXMgcHJlZmVyZW5jZXMgZGF0YSBmcm9tIElVQ04gIyMjIyANCnNwcC5ub21lcy5nYXJkIDwtIGFzLm1hdHJpeCh1bmlxdWUuZ2FyZCkgI3JlbWVtYmVyOiB0aGUgJ3VuaXF1ZS5nYXJkJyBvYmplY3Qgd2FzIGNyZWF0ZWQgaW4gdGhlIFNURVAgMQ0KZi5oYWIucmVkbGlzdCA8LSBmdW5jdGlvbihzcHAubm9tZXMuZ2FyZCl7DQogIGhhYi5zcHAgPC0gcmxfaGFiaXRhdHMoc3BwLm5vbWVzLmdhcmQsIGtleT0iOTlhZDk0MDE1ZWVjY2QzNWFiZTlmODY5YzYzM2UxZWJhZDY3ZWUzOTBhYjQ0NDkwMmVlYWQwYzMwOWM4MDBkNSIpDQogIHJldHVybihoYWIuc3BwJHJlc3VsdCRjb2RlKQ0KfQ0KbGlzdGEuaGFiLmdhcmQgPC0gbGFwcGx5KDE6bGVuZ3RoKHNwcC5ub21lcy5nYXJkKSxmdW5jdGlvbihmMCkgZi5oYWIucmVkbGlzdChzcHAubm9tZXMuZ2FyZFtmMF0pKSAjY3JlYXRpbmcgYW4gb2JqZWN0IHdpdGggYWxsIGluZm9ybWF0aW9uIGFib3V0IHNwZWNpZXMgaGFiaXRhdCBwcmVmZXJlbmNlIHJ1bm5pbmcgJ2hhYl9yZWRsaXN0JyBmdW5jdGlvbiB3aGljaCByZXR1cm5zIG9ubHkgdGhlIGluZm9ybWF0aW9uIGFib3V0IHByZWZlcmVuY2UsIHdoaWxlICdybF9oYWJpdGF0cycgZnVuY3Rpb24gcmV0dXJucyBpbiBhZGRpdGlvbiB0aGUgbmFtZSBvZiB0aGUgc3BlY2llcyAgDQpzcHAuY29kZXMuZ2FyZCA8LSBkby5jYWxsKCJyYmluZCIsIGxpc3RhLmhhYi5nYXJkKSAjIHRyYW5zZm9ybWluZyB0aGUgbGlzdCB3aXRoIGhhYml0YXQgY29kZXMgb2Ygc3BlY2llcyBpbiBhIG1hdHJpeCBvZiBoYWJpdGF0IGNvZGVzIA0Kc3BwLmNvZGVzLmdhcmQNCg0KIyMjIyBTVEVQIDQgLSBFZGl0aW5nIHRoZSBoYWJpdGF0IHByZWZlcmVuY2UgdG8gb2JhdGluIGNvcnJlc3BvbmRlbmNlIHdpdGggdGhlIGxhbmRjb3ZlciBjYXRlZ29yaWVzICMjIyMgDQojIyBQcmVwYXJpbmcgdGhlIG1hdHJpeA0KcHJlZi5oYWIuc3BwICMgICBjIHRhYmxlIGFscmVhZHkgbG9hZGVkIHdpdGggZmlsZSAuUkRhdGENCmhhYi5wcmVmLmdhcmQgPC0gYXMubWF0cml4KHByZWYuaGFiLnNwcCkNCiMgVGhlcmUgYXJlIElVQ04gZGF0YSBvbmx5IGZvciA4MiBzcHAuIA0KIyBSZW1vdmUgbGluZXMgZm9yIHdoaWNoIHRoZXJlIGlzIG5vIGF2YWlsYWJsZSBJVUNOIGRhdGEgKHZlcmlmeSB0aGlzIG9uIG9iamV0YyAnbGlzdGEuaGFiLmdhcmQnKSANCmhhYi5wcmVmLmdhcmQxIDwtIGhhYi5wcmVmLmdhcmRbLWMoMSwzOjYsODoxMCwxNDoxNiwgMTgsIDIwOjMwLCAzMjozMywgMzY6NDksIDUxOjYxLCA2Mzo2NSwgNjgsIDcwLCA3Mjo3NCwgNzgsIDgwLCA4Mjo4NSwgODc6MTAwLCAxMDM6MTA0LCAxMDcsIDEwOSwgMTEzOjExNSwgMTE3OjEyMiwgMTI3OjE0MCwgMTQyLCAxNDUsIDE0OCwgMTUwOjE1MiwgMTU1OjE2MCwgMTYyOjE3NCwgMTc3OjE4OCwgMTkwOjE5MiwgMTk0OjE5NiwgMTk4OjIwOCwgMjExLCAyMTM6MjE1LCAyMTc6MjE5LCAyMjIsIDIyNjoyMjksIDIzMjoyMzYsIDI0MDoyNDEsIDI0NzoyNTEsIDI1MywgMjU1OjI1OCwgMjYwOjI2MSwgMjY0LCAyNjY6MjY4LCAyNzAsIDI3NjoyODEsIDI4MywgMjg1KSxdICMgdGhlIG51bWJlciBoZXJlIGNvcnJlc3BvbmRzIHRvIGxpbmVzIGNvcnJlc3BvbmRpbmcgdG8gYSBzcGVjaWVzIGZvciB3aGljaCB0aGUgaGFiaXRhdCBwcmVmZXJlbmNlIGluZm9ybWF0aW9uIHdhcyBlbXB0eSwgdGhhdCBpcywgdGhlcmUgd2Fzbid0IGhhYml0YXQgcHJlZmVyZW5jZSBpbmZvcm1hdGlvbiBmb3IgdGhpcyBzcGVjaWVzIGluIElVQ04gZGF0YWJhc2UgIA0KaGFiLnByZWYuZ2FyZDEgPC0gcmVwbGFjZShoYWIucHJlZi5nYXJkMSwgbGlzdCA9IGlzLm5hKGhhYi5wcmVmLmdhcmQxKSwgdmFsdWVzID0gMCkgI3JlcGxhY2UgTkEgZm9yIDANCiMjIFVzaW5nIHRoZSBmdW5jdGlvbiAodG8gZmlsbCB0aGUgbWF0cml4IHdpdGggSVVDTiBkYXRhIGZvciA4MiBzcHApDQpoYWIucHJlZi5nYXJkMiA8LSBmLnByZWYuaGFiIChoYWIucHJlZi5nYXJkMSwgc3BwLmNvZGVzLmdhcmQpICNjcmVhdGluZyBhbiBtYXRyaXggd2l0aCBoYWJpdGF0IHByZWZlcmVuY2VzIG9mIGVhY2ggc3BlY2llcyB3aXRoIGZ1bmN0aW9uIGYucHJlZi5oYWINCiMjIElkZW50aWZpbmcgdGhlIHNwcC4gd2hpY2ggSVVDTiBkYXRhIGhhdmUgbm8gZXF1aXZhbGVuY2Ugd2l0aCBMVUxDIGNsYXNzIChpLmUuIHdpdGggZGF0YSB0aGF0IG5vdCBtYXRjaCB3aXRoIGFydGlmaWNpYWwsIG9wZW4gYW5kIGZvcmVzdCBhcmVhcyBmcm9tIE1BUEJJT01BUykNCmhhYi5wcmVmLmdhcmQzIDwtIGhhYi5wcmVmLmdhcmQyW2hhYi5wcmVmLmdhcmQyWyw0XSA9PSAwICYgaGFiLnByZWYuZ2FyZDJbLDVdID09IDAgJiBoYWIucHJlZi5nYXJkMlssNl0gPT0gMCwgXQ0KaGFiLnByZWYuZ2FyZDNbLDQ6Nl0gPC0gMSAjIHJlcGxhY2UgTkEgZGF0YSBmb3IgMSAodG8gbWFpbnRhaW4gb3JpZ2luYWwgZGlzdHJpYnV0aW9uIG9uIGFydGlmaWNpYWwsIG9wZW4gYXJlYXMgYW5kIGZvcmVzdCBhcmVhcyBmb3Igc3BlY2llcyB3aXRob3V0IElVQ04gZGF0YSkNCmZpbHRybzE9bWF0Y2goYXMuY2hhcmFjdGVyKGhhYi5wcmVmLmdhcmQzWywyXSksYXMuY2hhcmFjdGVyKGhhYi5wcmVmLmdhcmQyWywyXSkpIA0KaGFiLnByZWYuZ2FyZDI9aGFiLnByZWYuZ2FyZDJbLWMoZmlsdHJvMSksXQ0KaGFiLnByZWYuZ2FyZDINCiMjIEVkaXRpbmcgdGhlIG1hdHJpeCBvZiBzcHAuIHdpdGhvdXQgSVVDTiBkYXRhICgyMDMgc3BwKQ0KaGFiLnByZWYuZ2FyZDQgPC0gaGFiLnByZWYuZ2FyZFtjKDEsMzo2LDg6MTAsMTQ6MTYsIDE4LCAyMDozMCwgMzI6MzMsIDM2OjQ5LCA1MTo2MSwgNjM6NjUsIDY4LCA3MCwgNzI6NzQsIDc4LCA4MCwgODI6ODUsIDg3OjEwMCwgMTAzOjEwNCwgMTA3LCAxMDksIDExMzoxMTUsIDExNzoxMjIsIDEyNzoxNDAsIDE0MiwgMTQ1LCAxNDgsIDE1MDoxNTIsIDE1NToxNjAsIDE2MjoxNzQsIDE3NzoxODgsIDE5MDoxOTIsIDE5NDoxOTYsIDE5ODoyMDgsIDIxMSwgMjEzOjIxNSwgMjE3OjIxOSwgMjIyLCAyMjY6MjI5LCAyMzI6MjM2LCAyNDA6MjQxLCAyNDc6MjUxLCAyNTMsIDI1NToyNTgsIDI2MDoyNjEsIDI2NCwgMjY2OjI2OCwgMjcwLCAyNzY6MjgxLCAyODMsIDI4NSksXSANCmhhYi5wcmVmLmdhcmQ0IDwtIHJlcGxhY2UoaGFiLnByZWYuZ2FyZDQsIGxpc3QgPSBpcy5uYShoYWIucHJlZi5nYXJkNCksIHZhbHVlcyA9IDEpICMgcmVwbGFjZSBOQSBkYXRhIGZvciAxICh0byBtYWludGFpbiBvcmlnaW5hbCBkaXN0cmlidXRpb24gb24gYXJ0aWZpY2lhbCwgb3BlbiBhcmVhcyBhbmQgZm9yZXN0IGFyZWFzIGZvciBzcGVjaWVzIHdpdGhvdXQgSVVDTiBkYXRhKQ0KIyBDcmVhdGluZyBhIGZpbmFsICh0b3RhbCkgbWF0cml4IA0KcHJlZi5oYWIuc3BwLnJnYXJkIDwtIHJiaW5kKGhhYi5wcmVmLmdhcmQyLCBoYWIucHJlZi5nYXJkMywgaGFiLnByZWYuZ2FyZDQpICMgSm9pbiB0aGUgdHdvIG1hdHJpeCAoc3BwIHdpdGggYW5kIHdpdGhvdXQgSVVDTiBkYXRhKQ0KcHJlZi5oYWIuc3BwLnJnYXJkIDwtIHByZWYuaGFiLnNwcC5yZ2FyZFtvcmRlcihhcy5udW1lcmljKGFzLmZhY3RvcihwcmVmLmhhYi5zcHAucmdhcmRbLDJdKSkpLF0gIyByZW9yZGVyIGluIGFscGhhYmV0aWNhbCBvcmRlcg0KIyMgU2F2aW5nIHRoZSBoYWJpdGF0IG1hdHJpeCBvZiBzcGVjaWVzDQp3cml0ZS50YWJsZShwcmVmLmhhYi5zcHAucmdhcmQsIGZpbGU9IkhhYk1hdF9SZXBzR2FyZF9GSUxMRUQuY3N2Iiwgc2VwPSI7IikgI3NhdmluZyB0aGUgbWF0cml4IHdpdGggaGFiaXRhdCBwcmVmZXJlbmNlcyBvZiBlYWNoIHNwZWNpZXMNCg0KIyMjIyBTVEVQIDUgLSBSZWNsYXNzaWZ5aW5nIHRoZSBsYW5kIGNvdmVyIHRvIG9idGFpbiBlcXVpdmFsZW5jZSB3aXRoIHRoZSBJVUNOIGhhYml0YXQgY2F0ZWdvcmllcyAjIyMjIA0KbGFuZGNvdmVyLnJlY2xhc3MgPC0gcmVjbGFzc2lmeShsYW5kY292ZXIuY2FhdCwgdGFiLnJlY2xhc3MpDQp3cml0ZVJhc3RlcihsYW5kY292ZXIucmVjbGFzcywgZmlsZW5hbWU9ImxhbmRjb3ZlclJlY2xhc3MudGlmIikNCnBsb3QobGFuZGNvdmVyLnJlY2xhc3MpDQoNCiMjIyMgU1RFUCA2IC0gUmVmaW5pbmcgdGhlIGluZGl2aWR1YWwgcmFzdGVyIGFjY29yZGluZyB0byB0aGUgbGFuZCBjb3ZlciBhbmQgaGFiaXRhdCBwcmVmZXJlbmNlcyAjIyMjIyANCiMjIFByZXBhcmluZyBkYXRhDQpjYXRlZ3MucHJlZiA8LSBjKDAsMSwyLDMsNCkNCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJhc3RlcigibGFuZGNvdmVyUmVjbGFzcy50aWYiKSAjIHVwbG9hZGluZyB0aGUgcmVjbGFzc2lmaWVkIGxhbmQgY292ZXINCmVsZXYuY2FhdCAjIHJhc3RlciBhbHJlYWR5IGxvYWRlZCB3aXRoIGZpbGUgLlJEYXRhDQplbGV2LmNhYXQgPC0gcmFzdGVyKCJUb3BvQ2FhdF9yZXNhbXBsZS5hc2MiKQ0KcHJlZi5oYWIuc3BwLnJnYXJkICAjIHRhYmxlIGNyZWF0ZWQgaW4gdGhlIFNURVAgNA0KcHJlZi5lbGV2LnNwcCAjIHJlbmFtZWQgb2JqZWN0IGZyb20gdGFibGUgYWxyZWFkeSBsb2FkZWQgd2l0aCBmaWxlIC5SRGF0YSANCnByZWYuZWxldi5zcHAucmdhcmQgPC0gYXMubWF0cml4KHByZWYuZWxldi5zcHApDQpwcmVmLmVsZXYuc3BwLnJnYXJkIDwtIG1hdHJpeChhcy5udW1lcmljKHByZWYuZWxldi5zcHAucmdhcmRbLDM6NF0pLCBuY29sID0gMiApDQpyYW5nZS5vcmlnLnJnYXJkIDwtIHJhc3Rlcjo6c3RhY2sobGlzdC5maWxlcyhwYXRoPSIgICAiLCBwYXR0ZXJuID0gImFzYyQiLCBmdWxsLm5hbWVzID0gRikpICNyYXN0ZXIgZmlsZXMgY3JlYXRlZCBpbiB0aGUgU1RFUCAyDQoNCiMjIFVzaW5nIHRoZSBmdW5jdGlvbg0KZi5oYWJlbGV2LnJlZmdhcmQocmFuZ2Uub3JpZy5yZ2FyZCwgcHJlZi5oYWIuc3BwLnJnYXJkLCBwcmVmLmVsZXYuc3BwLnJnYXJkKQ0KYGBgDQoNCiMjIEFWRVMNCiMjIyBMb2FkaW5nIGRhdGENCmBgYHtyfQ0KYXZkYXRhIDwtICJodHRwczovL2dpdGh1Yi5jb20vdGhhaXNkb3JpYS9URF9UaGVzaXNfQ2hhcHRlcjFfUkRhdGEvYmxvYi9tYXN0ZXIvRG9yaWFUQUZfQXZlcy5SRGF0YT9yYXc9dHJ1ZSINCmxvYWQodXJsKGF2ZGF0YSkpICMgUkRhdGEgZmlsZSBjcmVhdGVkIG9uIDIwMTcvMjAxOCBjb250YWluaW5nIHRoZSBmaWxlcyBsaXN0ZWQgYmVsbG93Og0KDQojIyMjIFJ1bm5pbmcgc3RlcHMgIyMjIw0KdW5pZmllZHNocC5hdmVzIDwtIEFWRVNDQUFUICNhdmVzIHNwZWNpZXMgZGlzdHJpYnV0aW9uIChJVUNOIGRhdGEpDQpzaGFwZS5jYWF0IDwtIGNhYXRpbmdhICNzaGFwZWZpbGUgb2YgdGhlIHJlZ2lvbiANCmxhbmRjb3Zlci5jYWF0IDwtIG1wYmlvLm9yaSAjcmFzdGVyIG9mIHRoZSByZWdpb24gbGFuZGNvdmVyIA0KdGFiLnJlY2xhc3MgPC0gdGFiLnJlY2xhc3MgI3RhYmxlIGZvciByZWNsYXNzaWZ5IGxhbmRjb3ZlciBmdW5jdGlvbg0KcHJlZi5oYWIuc3BwIDwtIGhhYm51bGwuYXYgI3RhYmxlIGNvbnRhaW5pbmcgb25seSBwcmVmZXJlbmNlcyBkYXRhIG9uIHdhdGVyIGJvZGllcyAobWFudWFsbHkgYXNzaWduZWQgZm9sbG93aW5nIHNwZWNpZmljIGNyaXRlcmlhKSB0aGF0IHdpbGwgYmUgZmlsbGVkIHdpdGggaGFiaXRhdCBwcmVmZXJlbmNlcyBmcm9tIElVQ04NCmVsZXYuY2FhdCA8LSBlbGV2LnJlc2FtcGxlICNyYXN0ZXIgb2YgdGhlIHJlZ2lvbiBlbGV2YXRpb24NCnByZWYuZWxldi5zcHAgPC0gZWxldnNwcC5hdiAjdGFibGUgd2l0aCB0aGUgaW50ZXJ2YWwgb2YgZWxldmF0aW9uIGluIHdoaWNoIGVhY2ggc3BlY2llcyBvY2N1cnMgKGRhdGEgY29tcGlsZWQgbWFudWFsbHkgZnJvbSBJVUNOIGRhdGFiYXNlKQ0KYGBgDQpBVFRFTlRJT046IHBvbHlnb25zIGZyb20gSVVDTiB3ZXJlIGRvd25sb2FkZWQgYXQgMjAxNy8yMDE4IGFuZCBhZnRlciB0aGF0IHRoZSBnZW51cyBvZiBzcGVjaWUgKiJQaGFsYWNyb2NvcmF4IGJyYXNpbGlhbnVzIiogd2FzIG1vZGlmaWVkIGZvciAqIk5hbm5vcHRlcnVtIiouIFRodXMsIHRvIHJ1biB0aGlzIHNjcmlwdCBhZGVxdWF0ZWx5IGFuZCBvYnRhaW4gY29ycmVjdCBjb3JyZXNwb25kZW5jZXMgYmV0d2VlbiB0aGUgc3BlY2llcyBuYW1lcyBmcm9tIHNoYXBlZmlsZXMgYW5kIHRoZSBtb3N0IHJlY2VudCBoYWJpdGF0IGRhdGEgZnJvbSBJVUNOIGRhdGFiYXNlIGl0IGlzIHJlY29tbWVuZGVkIHRvIGRvd25sb2FkIHRoZSB1cC1kYXRlZCBwb2x5Z29ucyBhbmQgbm90IHVzZSB0aG9zZSBhdmFpbGFibGUgaW4gdGhpcyBSRGF0YS4NCg0KIyMjIFJ1bm5pbmcgc3RlcHMNCmBgYHtyLCBldmFsPUZ9DQoNCiMjIyMgU1RFUCAxIC0gQ3JlYXRpbmcgYW4gaW5kaXZpZHVhbCBzaGFwZWZpbGUgZm9yIGVhY2ggc3BlY2llcyBkaXN0cmlidXRpb24gIyMjIw0KZGF0YSA8LSB1bmlmaWVkc2hwLmF2ZXMgIyByZXBlYXQgdGhpcyBzdGVwIHRvIGFsbCBncm91cHMgKGFtcGhpYmlhbnMsIHJlcHRpbGVzLCBtYW1tYWxzLCBhdmVzKQ0KbmFtZXMoZGF0YSkgI3VzZWQgdG8gdmVyaWZ5IHRoZSBjb2x1bW4gdGhhdCBjb250YWluZWQgdGhlIHNwZWNpZXMgbmFtZXMNCnVuaXF1ZSA8LSBzb3J0KHVuaXF1ZShkYXRhQGRhdGEkU0NJTkFNRSkpICNhY2Nlc3NpbmcgJ1NDSU5BTUUnLCBjb2x1bW4gdGhhdCBjb250YWluIHRoZSBzcGVjaWVzIG5hbWVzLCBhbmQgc2F2aW5nIHRoZXNlIG5hbWVzIHJlbW92aW5nIHRoZSBkdXBsaWNhdGUgb25lcw0KZm9yIChpIGluIDE6bGVuZ3RoKHVuaXF1ZSkpIHsNCiAgdG1wIDwtIGRhdGFbZGF0YSRTQ0lOQU1FPT0gdW5pcXVlW2ldLCBdIA0KICB3cml0ZU9HUih0bXAsIGRzbj1nZXR3ZCgpLCB1bmlxdWVbaV0sIGRyaXZlcj0iRVNSSSBTaGFwZWZpbGUiLA0KICAgICAgICAgICBvdmVyd3JpdGVfbGF5ZXI9VFJVRSkNCn0NCg0KIyMjIyBTVEVQIDIgLSBDcmVhdGluZyBhbiBpbmRpdmlkdWFsIHJhc3RlciBmb3IgZWFjaCBzcGVjaWVzIGRpc3RyaWJ1dGlvbiAjIyMjIA0KZXh0ZW50KGxhbmRjb3Zlci5jYWF0KSAjIHRoaXMgaW5mb3JtYXRpb24gd2FzIHVzZWQgdG8gY3JlYXRlIGEgcmVmZXJlbmNlIHJhc3RlciwgdGhlIGNob3NlbiByZXNvbHV0aW9uIHdhcyAzMDBtICgwLjAwMjc3Nzc3OCB4IDAuMDAyNzc3Nzc4IGRlZ3JlZSkNCnIwLjA1IDwtIHJhc3RlcihyZXM9MC4wNSwgeG1uPS00NC41MDgzOCwgeG14PS0zNS4xNzE3NCwgeW1uPS0xNi4wODg1NiwgeW14PS0yLjc4Mzg3OSkNCnIwLjA1W10gPC0gMTpuY2VsbChyMC4wNSkNCnIwLjA1ICMgMjY2LCAxODcsIDQ5NzQyIChucm93LCBuY29sLCBuY2VsbCkgDQoNCmxpc3RhLnNwcC5hdiA8LSBsaXN0LmZpbGVzKHBhdHRlcm4gPSAiLnNocCQiLCBmdWxsLm5hbWVzID0gRikgIyBsaXN0IG9mIGF2ZXMgc2hhcGVmaWxlcyANCg0KIyBQcmVzZW5jZS9BYnNlbmNlDQpmb3IgKGkgaW4gMTpsZW5ndGgobGlzdGEuc3BwLmF2KSkgew0KICBzcHAgPC0gc2hhcGVmaWxlKGxpc3RhLnNwcC5hdltpXSkNCiAgc3BwLnIgPC0gcmFzdGVyaXplKHNwcCwgcjAuMDUsIGJhY2tncm91bmQgPSAwLCBnZXRDb3Zlcj1UKQ0KICBzcHAucltzcHAuciA+IDFdIDwtIDENCiAgc3BwLmMgPC0gY3JvcChzcHAuciwgc2hhcGUuY2FhdCkgDQogIHNwcC5tIDwtIG1hc2soc3BwLmMsIHNoYXBlLmNhYXQpDQogIGUgPC0gZXh0ZW50KC00NC44LCAtMzQuOCwgLTE2LjYsIC0yLjIpIA0KICBzcHAuZSA8LSBleHRlbmQoc3BwLm0sZSkNCiAgbm9tZSA8LSB1bmlxdWVbaV0NCiAgd3JpdGVSYXN0ZXIoc3BwLmUsIHBhc3RlKG5vbWUsICIwLjA1LmFzYyIsIHNlcCA9ICIiKSwgZm9ybWF0ID0gImFzY2lpIiwgb3ZlcndyaXRlID0gVCkNCn0gDQoNCiMjIyMgU1RFUCAzIC0gT2J0YWluaW5nIGluZm9ybWF0aW9uIG9mIGhhYml0YXQgc3BlY2llcyBwcmVmZXJlbmNlcyBkYXRhIGZyb20gSVVDTiAjIyMjIA0Kc3BwLm5vbWVzIDwtIGFzLm1hdHJpeCh1bmlxdWUpICNyZW1lbWJlcjogdGhlICd1bmlxdWUnIG9iamVjdCB3YXMgY3JlYXRlZCBpbiB0aGUgU1RFUCAxDQpmLmhhYi5yZWRsaXN0IDwtIGZ1bmN0aW9uKHNwcC5ub21lcyl7DQogIGhhYi5zcHAgPC0gcmxfaGFiaXRhdHMoc3BwLm5vbWVzLCBrZXk9Ijk5YWQ5NDAxNWVlY2NkMzVhYmU5Zjg2OWM2MzNlMWViYWQ2N2VlMzkwYWI0NDQ5MDJlZWFkMGMzMDljODAwZDUiKQ0KICByZXR1cm4oaGFiLnNwcCRyZXN1bHQkY29kZSkNCn0NCg0KbGlzdGEuaGFiIDwtIGxhcHBseSgxOmxlbmd0aChzcHAubm9tZXMpLGZ1bmN0aW9uKGYwKSBmLmhhYi5yZWRsaXN0KHNwcC5ub21lc1tmMF0pKSAjY3JlYXRpbmcgYW4gb2JqZWN0IHdpdGggYWxsIGluZm9ybWF0aW9uIGFib3V0IHNwZWNpZXMgaGFiaXRhdCBwcmVmZXJlbmNlIHJ1bm5pbmcgJ2hhYl9yZWRsaXN0JyBmdW5jdGlvbiB3aGljaCByZXR1cm5zIG9ubHkgdGhlIGluZm9ybWF0aW9uIGFib3V0IHByZWZlcmVuY2UsIHdoaWxlICdybF9oYWJpdGF0cycgZnVuY3Rpb24gcmV0dXJucyBpbiBhZGRpdGlvbiB0aGUgbmFtZSBvZiB0aGUgc3BlY2llcyAgDQpzcHAuY29kZXMgPC0gZG8uY2FsbChyYmluZCwgbGlzdGEuaGFiKSAjIHRyYW5zZm9ybWluZyB0aGUgbGlzdCB3aXRoIGhhYml0YXQgY29kZXMgb2Ygc3BlY2llcyBpbiBhIG1hdHJpeCBvZiBoYWJpdGF0IGNvZGVzIA0KDQojIyMjIFNURVAgNCAtIEVkaXRpbmcgdGhlIGhhYml0YXQgcHJlZmVyZW5jZSB0byBvYmF0aW4gY29ycmVzcG9uZGVuY2Ugd2l0aCB0aGUgbGFuZGNvdmVyIGNhdGVnb3JpZXMgIyMjIyANCiMjIFByZXBhcmluZyB0aGUgbWF0cml4DQpwcmVmLmhhYi5zcHAgIyB0YWJsZSBhbHJlYWR5IGxvYWRlZCB3aXRoIGZpbGUgLlJEYXRhDQpoYWIucHJlZiA8LSBhcy5tYXRyaXgocHJlZi5oYWIuc3BwKQ0KIyBUaGVyZSBhcmUgSVVDTiBkYXRhIGZvciBhbGwgYXZlcyBzcGVjaWVzDQpoYWIucHJlZjEgPC0gcmVwbGFjZShoYWIucHJlZiwgbGlzdCA9IGlzLm5hKGhhYi5wcmVmKSwgdmFsdWVzID0gMCkgI3JlcGxhY2UgTkEgZm9yIDANCiMjIFVzaW5nIHRoZSBmdW5jdGlvbg0KaGFiLnByZWYyIDwtIGYucHJlZi5oYWIgKGhhYi5wcmVmMSwgc3BwLmNvZGVzKSAjY3JlYXRpbmcgYW4gbWF0cml4IHdpdGggaGFiaXRhdCBwcmVmZXJlbmNlcyBvZiBlYWNoIHNwZWNpZXMgd2l0aCBmdW5jdGlvbiBmLnByZWYuaGFiDQojIyBJZGVudGlmaW5nIHRoZSBzcHAuIHdoaWNoIElVQ04gZGF0YSBoYXZlIG5vIGVxdWl2YWxlbmNlIHdpdGggTFVMQyBjbGFzcyAoaS5lLiB3aXRoIGRhdGEgdGhhdCBub3QgbWF0Y2ggd2l0aCBhcnRpZmljaWFsLCBvcGVuIGFuZCBmb3Jlc3QgYXJlYXMgZnJvbSBNQVBCSU9NQVMpDQpoYWIucHJlZjMgPC0gaGFiLnByZWYyW2hhYi5wcmVmMlssNF0gPT0gMCAmIGhhYi5wcmVmMlssNV0gPT0gMCAmIGhhYi5wcmVmMlssNl0gPT0gMCwgXQ0KaGFiLnByZWYzWyw0OjZdIDwtIDEgIyByZXBsYWNlIE5BIGRhdGEgZm9yIDEgKHRvIG1haW50YWluIG9yaWdpbmFsIGRpc3RyaWJ1dGlvbiBvbiBhcnRpZmljaWFsLCBvcGVuIGFyZWFzIGFuZCBmb3Jlc3QgYXJlYXMgZm9yIHNwZWNpZXMgd2l0aG91dCBJVUNOIGRhdGEpDQpmaWx0cm8xPW1hdGNoKGFzLmNoYXJhY3RlcihoYWIucHJlZjNbLDJdKSxhcy5jaGFyYWN0ZXIoaGFiLnByZWYyWywyXSkpIA0KaGFiLnByZWYyPWhhYi5wcmVmMlstYyhmaWx0cm8xKSxdDQojIENyZWF0aW5nIGEgZmluYWwgKHRvdGFsKSBtYXRyaXggDQpwcmVmLmhhYi5zcHAuYXYgPC0gcmJpbmQoaGFiLnByZWYyLCBoYWIucHJlZjMpICMgSm9pbiB0aGUgdHdvIG1hdHJpeCAoc3BwIHdpdGggYW5kIHdpdGhvdXQgSVVDTiBkYXRhKQ0KcHJlZi5oYWIuc3BwLmF2IDwtIHByZWYuaGFiLnNwcC5hdltvcmRlcihhcy5udW1lcmljKGFzLmZhY3RvcihwcmVmLmhhYi5zcHAuYXZbLDJdKSkpLF0gIyByZW9yZGVyIGluIGFscGhhYmV0aWNhbCBvcmRlcg0KIyMgU2F2aW5nIHRoZSBoYWJpdGF0IG1hdHJpeCBvZiBzcGVjaWVzDQp3cml0ZS50YWJsZShwcmVmLmhhYi5zcHAubSwgZmlsZT0iSGFiTWF0X0F2ZXNfRklMTEVELmNzdiIsIHNlcD0iOyIpICNzYXZpbmcgdGhlIG1hdHJpeCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgb2YgZWFjaCBzcGVjaWVzDQoNCiMjIyMgU1RFUCA1IC0gUmVjbGFzc2lmeWluZyB0aGUgbGFuZCBjb3ZlciB0byBvYnRhaW4gZXF1aXZhbGVuY2Ugd2l0aCB0aGUgSVVDTiBoYWJpdGF0IGNhdGVnb3JpZXMgIyMjIyANCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJlY2xhc3NpZnkobGFuZGNvdmVyLmNhYXQsIHRhYi5yZWNsYXNzKQ0Kd3JpdGVSYXN0ZXIobGFuZGNvdmVyLnJlY2xhc3MsIGZpbGVuYW1lPSJsYW5kY292ZXJSZWNsYXNzLnRpZiIpDQoNCiMjIyMgU1RFUCA2IC0gUmVmaW5pbmcgdGhlIGluZGl2aWR1YWwgcmFzdGVyIGFjY29yZGluZyB0byB0aGUgbGFuZCBjb3ZlciBhbmQgaGFiaXRhdCBwcmVmZXJlbmNlcyAjIyMjIyANCiMjIFByZXBhcmluZyBkYXRhDQpjYXRlZ3MucHJlZiA8LSBjKDAsMSwyLDMsNCkNCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJhc3RlcigibGFuZGNvdmVyUmVjbGFzcy50aWYiKSAjIHVwbG9hZGluZyB0aGUgcmVjbGFzc2lmaWVkIGxhbmQgY292ZXIgDQplbGV2LmNhYXQgI3Jhc3RlciBhbHJlYWR5IGxvYWRlZCB3aXRoIGZpbGUgLlJEYXRhDQpwcmVmLmhhYi5zcHAuYXYgICMgdGFibGUgY3JlYXRlZCBpbiB0aGUgU1RFUCA0DQpwcmVmLmVsZXYuc3BwICMgcmVuYW1lZCBvYmplY3QgZnJvbSB0YWJsZSBhbHJlYWR5IGxvYWRlZCB3aXRoIGZpbGUgLlJEYXRhIA0KcHJlZi5lbGV2LnNwcC5hdiA8LSBhcy5tYXRyaXgocHJlZi5lbGV2LnNwcCkNCnByZWYuZWxldi5zcHAuYXYgPC0gbWF0cml4KGFzLm51bWVyaWMocHJlZi5lbGV2LnNwcC5hdlssIDI6M10pLCBuY29sID0gMiApDQpyYW5nZS5vcmlnLmF2IDwtIHJhc3Rlcjo6c3RhY2sobGlzdC5maWxlcyhwYXRoPSIgICIsIHBhdHRlcm4gPSAiYXNjJCIsIGZ1bGwubmFtZXMgPSBGKSkNCg0KIyMgVXNpbmcgdGhlIGZ1bmN0aW9uDQpmLmhhYmVsZXYucmVmKHJhbmdlLm9yaWcuYXYsIHByZWYuaGFiLnNwcC5hdiwgcHJlZi5lbGV2LnNwcC5hdikNCmBgYA0KDQojIyBNQU1NQUxJQQ0KIyMjIExvYWRpbmcgZGF0YQ0KYGBge3J9DQptZGF0YSA8LSAiaHR0cHM6Ly9naXRodWIuY29tL3RoYWlzZG9yaWEvVERfVGhlc2lzX0NoYXB0ZXIxX1JEYXRhL2Jsb2IvbWFzdGVyL0RvcmlhVEFGX01hbW1hbHMuUkRhdGE/cmF3PXRydWUiDQpsb2FkKHVybChtZGF0YSkpICMgUkRhdGEgZmlsZSBjcmVhdGVkIG9uIDIwMTcvMjAxOCBjb250YWluaW5nIHRoZSBmaWxlcyBsaXN0ZWQgYmVsbG93Og0KDQp1bmlmaWVkc2hwLm1hbW1hbHMgPC0gTUFNSUNBQVQgI21hbW1hbHMgc3BlY2llcyBkaXN0cmlidXRpb24gKElVQ04gZGF0YSkNCnNoYXBlLmNhYXQgPC0gY2FhdGluZ2EgI3NoYXBlZmlsZSBvZiB0aGUgcmVnaW9uIA0KbGFuZGNvdmVyLmNhYXQgPC0gbXBiaW8ub3JpICNyYXN0ZXIgb2YgdGhlIHJlZ2lvbiBsYW5kY292ZXIgDQp0YWIucmVjbGFzcyA8LSB0YWIucmVjbGFzcyAjdGFibGUgZm9yIHJlY2xhc3NpZnkgbGFuZGNvdmVyIGZ1bmN0aW9uDQpwcmVmLmhhYi5zcHAgPC0gaGFibnVsbC5tICN0YWJsZSBjb250YWluaW5nIG9ubHkgcHJlZmVyZW5jZXMgZGF0YSBvbiB3YXRlciBib2RpZXMgKG1hbnVhbGx5IGFzc2lnbmVkIGZvbGxvd2luZyBzcGVjaWZpYyBjcml0ZXJpYSkgdGhhdCB3aWxsIGJlIGZpbGxlZCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgZnJvbSBJVUNODQplbGV2LmNhYXQgPC0gZWxldi5yZXNhbXBsZSAjcmFzdGVyIG9mIHRoZSByZWdpb24gZWxldmF0aW9uDQpwcmVmLmVsZXYuc3BwIDwtIGVsZXZzcHAubSAjdGFibGUgd2l0aCB0aGUgaW50ZXJ2YWwgb2YgZWxldmF0aW9uIGluIHdoaWNoIGVhY2ggc3BlY2llcyBvY2N1cnMgKGRhdGEgY29tcGlsZWQgbWFudWFsbHkgZnJvbSBJVUNOIGRhdGFiYXNlKQ0KYGBgDQpBVFRFTlRJT046IGFmdGVyIHRoZSBwb2x5Z29ucyBmcm9tIElVQ04gd2VyZSBkb3dubG9hZGVkIGF0IDIwMTcvMjAxOCBhbmQgYWZ0ZXIgdGhhdCB0aGUgZ2VudXMgb2Ygc3BlY2llICoiTWltb24gY3JlbnVsYXR1bSIqIHdhcyBtb2RpZmllZCBmb3IgKiJHYXJkbmVyeWN0ZXJpcyIqLiBUaHVzLCB0byBydW4gdGhpcyBzY3JpcHQgYWRlcXVhdGVseSBhbmQgb2J0YWluIGNvcnJlY3QgY29ycmVzcG9uZGVuY2VzIGJldHdlZW4gdGhlIHNwZWNpZXMgbmFtZXMgZnJvbSBzaGFwZWZpbGVzIGFuZCB0aGUgbW9zdCByZWNlbnQgaGFiaXRhdCBkYXRhIGZyb20gSVVDTiBkYXRhYmFzZSBpdCBpcyByZWNvbW1lbmRlZCB0byBkb3dubG9hZCB0aGUgdXAtZGF0ZWQgcG9seWdvbnMgYW5kIG5vdCB1c2UgdGhvc2UgYXZhaWxhYmxlIGluIHRoaXMgUkRhdGEuICANCg0KIyMjIFJ1bm5pbmcgc3RlcHMNCmBgYHtyLCBldmFsPUZ9DQojIyMjIFNURVAgMSAtIENyZWF0aW5nIGFuIGluZGl2aWR1YWwgc2hhcGVmaWxlIGZvciBlYWNoIHNwZWNpZXMgZGlzdHJpYnV0aW9uICMjIyMNCmRhdGEgPC0gdW5pZmllZHNocC5tYW1tYWxzIA0KbmFtZXMoZGF0YSkgI3VzZWQgdG8gdmVyaWZ5IHRoZSBjb2x1bW4gdGhhdCBjb250YWluZWQgdGhlIHNwZWNpZXMgbmFtZXMNCnVuaXF1ZSA8LSBzb3J0KHVuaXF1ZShkYXRhQGRhdGEkYmlub21pYWwpKSAjYWNjZXNzaW5nICdiaW5vbWlhbCcsIGNvbHVtbiB0aGF0IGNvbnRhaW4gdGhlIHNwZWNpZXMgbmFtZXMsIGFuZCBzYXZpbmcgdGhlc2UgbmFtZXMgcmVtb3ZpbmcgdGhlIGR1cGxpY2F0ZSBvbmVzDQpmb3IgKGkgaW4gMTpsZW5ndGgodW5pcXVlKSkgew0KICB0bXAgPC0gZGF0YVtkYXRhJGJpbm9taWFsPT0gdW5pcXVlW2ldLCBdIA0KICB3cml0ZU9HUih0bXAsIGRzbj1nZXR3ZCgpLCB1bmlxdWVbaV0sIGRyaXZlcj0iRVNSSSBTaGFwZWZpbGUiLA0KICAgICAgICAgICBvdmVyd3JpdGVfbGF5ZXI9VFJVRSkNCn0NCg0KIyMjIyBTVEVQIDIgLSBDcmVhdGluZyBhbiBpbmRpdmlkdWFsIHJhc3RlciBmb3IgZWFjaCBzcGVjaWVzIGRpc3RyaWJ1dGlvbiAjIyMjIA0KZXh0ZW50KGxhbmRjb3Zlci5jYWF0KSAjIHRoaXMgaW5mb3JtYXRpb24gd2FzIHVzZWQgdG8gY3JlYXRlIGEgcmVmZXJlbmNlIHJhc3RlciwgdGhlIGNob3NlbiByZXNvbHV0aW9uIHdhcyAzMDBtICgwLjAwMjc3Nzc3OCB4IDAuMDAyNzc3Nzc4IGRlZ3JlZSkNCnIwLjA1IDwtIHJhc3RlcihyZXM9MC4wNSwgeG1uPS00NC41MDgzOCwgeG14PS0zNS4xNzE3NCwgeW1uPS0xNi4wODg1NiwgeW14PS0yLjc4Mzg3OSkNCnIwLjA1W10gPC0gMTpuY2VsbChyMC4wNSkNCnIwLjA1ICAjIDI2NiwgMTg3LCA0OTc0MiAobnJvdywgbmNvbCwgbmNlbGwpIA0KDQpsaXN0YS5zcHAubSA8LSBsaXN0LmZpbGVzKHBhdHRlcm4gPSAiLnNocCQiLCBmdWxsLm5hbWVzID0gRikgI2xpc3Qgb2YgbWFtbWFscyBzaGFwZWZpbGVzIA0KDQojIFByZXNlbmNlL0Fic2VuY2UNCmZvciAoaSBpbiAxOmxlbmd0aChsaXN0YS5zcHAubSkpIHsNCiAgc3BwIDwtIHNoYXBlZmlsZShsaXN0YS5zcHAubVtpXSkNCiAgc3BwLnIgPC0gcmFzdGVyaXplKHNwcCwgcjAuMDUsIGJhY2tncm91bmQgPSAwLCBnZXRDb3Zlcj1UKQ0KICBzcHAucltzcHAuciA+IDFdIDwtIDENCiAgc3BwLmMgPC0gY3JvcChzcHAuciwgc2hhcGUuY2FhdCkgDQogIHNwcC5tIDwtIG1hc2soc3BwLmMsIHNoYXBlLmNhYXQpDQogIGUgPC0gZXh0ZW50KC00NC44LCAtMzQuOCwgLTE2LjYsIC0yLjIpIA0KICBzcHAuZSA8LSBleHRlbmQoc3BwLm0sZSkNCiAgbm9tZSA8LSB1bmlxdWVbaV0NCiAgd3JpdGVSYXN0ZXIoc3BwLmUsIHBhc3RlKG5vbWUsICIwLjA1LmFzYyIsIHNlcCA9ICIiKSwgZm9ybWF0ID0gImFzY2lpIiwgb3ZlcndyaXRlID0gVCkNCn0gDQoNCiMjIyMgU1RFUCAzIC0gT2J0YWluaW5nIGluZm9ybWF0aW9uIG9mIGhhYml0YXQgc3BlY2llcyBwcmVmZXJlbmNlcyBkYXRhIGZyb20gSVVDTiAjIyMjIA0Kc3BwLm5vbWVzIDwtIGFzLm1hdHJpeCh1bmlxdWUpICNyZW1lbWJlcjogdGhlICd1bmlxdWUnIG9iamVjdCB3YXMgY3JlYXRlZCBpbiB0aGUgU1RFUCAxDQpmLmhhYi5yZWRsaXN0IDwtIGZ1bmN0aW9uKHNwcC5ub21lcyl7DQogIGhhYi5zcHAgPC0gcmxfaGFiaXRhdHMoc3BwLm5vbWVzLCBrZXk9Ijk5YWQ5NDAxNWVlY2NkMzVhYmU5Zjg2OWM2MzNlMWViYWQ2N2VlMzkwYWI0NDQ5MDJlZWFkMGMzMDljODAwZDUiKQ0KICByZXR1cm4oaGFiLnNwcCRyZXN1bHQkY29kZSkNCn0NCg0KbGlzdGEuaGFiIDwtIGxhcHBseSgxOmxlbmd0aChzcHAubm9tZXMpLGZ1bmN0aW9uKGYwKSBmLmhhYi5yZWRsaXN0KHNwcC5ub21lc1tmMF0pKSAjY3JlYXRpbmcgYW4gb2JqZWN0IHdpdGggYWxsIGluZm9ybWF0aW9uIGFib3V0IHNwZWNpZXMgaGFiaXRhdCBwcmVmZXJlbmNlIHJ1bm5pbmcgJ2hhYl9yZWRsaXN0JyBmdW5jdGlvbiB3aGljaCByZXR1cm5zIG9ubHkgdGhlIGluZm9ybWF0aW9uIGFib3V0IHByZWZlcmVuY2UsIHdoaWxlICdybF9oYWJpdGF0cycgZnVuY3Rpb24gcmV0dXJucyBpbiBhZGRpdGlvbiB0aGUgbmFtZSBvZiB0aGUgc3BlY2llcyAgDQpzcHAuY29kZXMgPC0gZG8uY2FsbChyYmluZCwgbGlzdGEuaGFiKSAjIHRyYW5zZm9ybWluZyB0aGUgbGlzdCB3aXRoIGhhYml0YXQgY29kZXMgb2Ygc3BlY2llcyBpbiBhIG1hdHJpeCBvZiBoYWJpdGF0IGNvZGVzDQoNCiMjIyMgU1RFUCA0IC0gRWRpdGluZyB0aGUgaGFiaXRhdCBwcmVmZXJlbmNlIHRvIG9iYXRpbiBjb3JyZXNwb25kZW5jZSB3aXRoIHRoZSBsYW5kY292ZXIgY2F0ZWdvcmllcyAjIyMjIA0KIyMgUHJlcGFyaW5nIHRoZSBtYXRyaXgNCnByZWYuaGFiLnNwcCAjIHRhYmxlIGFscmVhZHkgbG9hZGVkIHdpdGggZmlsZSAuUkRhdGENCmhhYi5wcmVmIDwtIGFzLm1hdHJpeChwcmVmLmhhYi5zcHApDQojIFRoZXJlIGFyZSBJVUNOIGRhdGEgZm9yIGFsbCAyMDggbWFtbWFscw0KaGFiLnByZWYxIDwtIHJlcGxhY2UoaGFiLnByZWYsIGxpc3QgPSBpcy5uYShoYWIucHJlZiksIHZhbHVlcyA9IDApICNyZXBsYWNlIE5BIGZvciAwDQojIyBVc2luZyB0aGUgZnVuY3Rpb24NCmhhYi5wcmVmMiA8LSBmLnByZWYuaGFiIChoYWIucHJlZjEsIHNwcC5jb2RlcykgI2NyZWF0aW5nIGFuIG1hdHJpeCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgb2YgZWFjaCBzcGVjaWVzIHdpdGggZnVuY3Rpb24gZi5wcmVmLmhhYg0KIyMgSWRlbnRpZmluZyB0aGUgc3BwLiB3aGljaCBJVUNOIGRhdGEgaGF2ZSBubyBlcXVpdmFsZW5jZSB3aXRoIExVTEMgY2xhc3MgKGkuZS4gd2l0aCBkYXRhIHRoYXQgbm90IG1hdGNoIHdpdGggYXJ0aWZpY2lhbCwgb3BlbiBhbmQgZm9yZXN0IGFyZWFzIGZyb20gTUFQQklPTUFTKQ0KaGFiLnByZWYzIDwtIGhhYi5wcmVmMltoYWIucHJlZjJbLDRdID09IDAgJiBoYWIucHJlZjJbLDVdID09IDAgJiBoYWIucHJlZjJbLDZdID09IDAsIF0NCmhhYi5wcmVmM1ssNDo2XSA8LSAxICMgcmVwbGFjZSBOQSBkYXRhIGZvciAxICh0byBtYWludGFpbiBvcmlnaW5hbCBkaXN0cmlidXRpb24gb24gYXJ0aWZpY2lhbCwgb3BlbiBhcmVhcyBhbmQgZm9yZXN0IGFyZWFzIGZvciBzcGVjaWVzIHdpdGhvdXQgSVVDTiBkYXRhKQ0KZmlsdHJvMT1tYXRjaChhcy5jaGFyYWN0ZXIoaGFiLnByZWYzWywyXSksYXMuY2hhcmFjdGVyKGhhYi5wcmVmMlssMl0pKSANCmhhYi5wcmVmMj1oYWIucHJlZjJbLWMoZmlsdHJvMSksXQ0KIyBDcmVhdGluZyBhIGZpbmFsICh0b3RhbCkgbWF0cml4IA0KcHJlZi5oYWIuc3BwLm0gPC0gcmJpbmQoaGFiLnByZWYyLCBoYWIucHJlZjMpICMgSm9pbiB0aGUgdHdvIG1hdHJpeCAoc3BwIHdpdGggYW5kIHdpdGhvdXQgSVVDTiBkYXRhKQ0KcHJlZi5oYWIuc3BwLm0gPC0gcHJlZi5oYWIuc3BwLm1bb3JkZXIoYXMubnVtZXJpYyhhcy5mYWN0b3IocHJlZi5oYWIuc3BwLm1bLDJdKSkpLF0gIyByZW9yZGVyIGluIGFscGhhYmV0aWNhbCBvcmRlcg0KIyMgU2F2aW5nIHRoZSBoYWJpdGF0IG1hdHJpeCBvZiBzcGVjaWVzDQp3cml0ZS50YWJsZShwcmVmLmhhYi5zcHAubSwgZmlsZT0iSGFiTWF0X01hbW1hbHNfRklMTEVELmNzdiIsIHNlcD0iOyIpICNzYXZpbmcgdGhlIG1hdHJpeCB3aXRoIGhhYml0YXQgcHJlZmVyZW5jZXMgb2YgZWFjaCBzcGVjaWVzDQoNCiMjIyMgU1RFUCA1IC0gUmVjbGFzc2lmeWluZyB0aGUgbGFuZCBjb3ZlciB0byBvYnRhaW4gZXF1aXZhbGVuY2Ugd2l0aCB0aGUgSVVDTiBoYWJpdGF0IGNhdGVnb3JpZXMgIyMjIyANCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJlY2xhc3NpZnkobGFuZGNvdmVyLmNhYXQsIHRhYi5yZWNsYXNzKQ0Kd3JpdGVSYXN0ZXIobGFuZGNvdmVyLnJlY2xhc3MsIGZpbGVuYW1lPSJsYW5kY292ZXJSZWNsYXNzLnRpZiIpDQoNCiMjIyMgU1RFUCA2IC0gUmVmaW5pbmcgdGhlIGluZGl2aWR1YWwgcmFzdGVyIGFjY29yZGluZyB0byB0aGUgbGFuZCBjb3ZlciBhbmQgaGFiaXRhdCBwcmVmZXJlbmNlcyAjIyMjIyANCiMjIFByZXBhcmluZyBkYXRhDQpjYXRlZ3MucHJlZiA8LSBjKDAsMSwyLDMsNCkNCmxhbmRjb3Zlci5yZWNsYXNzIDwtIHJhc3RlcigibGFuZGNvdmVyUmVjbGFzcy50aWYiKSAjIHVwbG9hZGluZyB0aGUgcmVjbGFzc2lmaWVkIGxhbmQgY292ZXINCmVsZXYuY2FhdCAjIHJhc3RlciBhbHJlYWR5IGxvYWRlZCB3aXRoIGZpbGUgLlJEYXRhDQpwcmVmLmhhYi5zcHAubSAgIyB0YWJsZSBjcmVhdGVkIGluIHRoZSBTVEVQIDQNCnByZWYuZWxldi5zcHAgIyByZW5hbWVkIG9iamVjdCBmcm9tIHRhYmxlIGFscmVhZHkgbG9hZGVkIHdpdGggZmlsZSAuUkRhdGEgDQpwcmVmLmVsZXYuc3BwLm0gPC0gYXMubWF0cml4KHByZWYuZWxldi5zcHApDQpwcmVmLmVsZXYuc3BwLm0gPC0gbWF0cml4KGFzLm51bWVyaWMocHJlZi5lbGV2LnNwcC5tWywgMjozXSksIG5jb2wgPSAyICkNCnJhbmdlLm9yaWcubSA8LSByYXN0ZXI6OnN0YWNrKGxpc3QuZmlsZXMocGF0aD0iICAgIiwgcGF0dGVybiA9ICJhc2MkIiwgZnVsbC5uYW1lcyA9IEYpKSAjcmFzdGVyIGZpbGVzIGNyZWF0ZWQgaW4gdGhlIFNURVAgMg0KDQojIFVzaW5nIHRoZSBmdW5jdGlvbg0KZi5oYWJlbGV2LnJlZihyYW5nZS5vcmlnLm0sIHByZWYuaGFiLnNwcC5tLCBwcmVmLmVsZXYuc3BwLm0pDQpgYGANCg0KDQoNCg==