1 DESCRIPTION

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

2 REQUIREMENTS

2.1 Install and load the required packages

install.packages('rgdal')
install.packages('dismo')
install.packages('sp')
install.packages('raster')
install.packages('plyr')
library(rgdal)
library(dismo)
library(sp)
library(raster)
library(plyr)

2.2 Load the required functions

    1. To download occurrences data (long and lat) from GBIF database for all species listed
f.gbif <- function (spp){
  for (i in 1:length(spp)) {
    p.occ=gbif(spp, ext=ex, geo=T, download=T)
    return(as.data.frame(cbind(p.occ$species, p.occ$lon, p.occ$lat))) # returning only coordinates and name of species
  }}
#Notes:
#'spp' in this function is list with specie's names (as.character) 
#'ex' in this function is the spatial extension (xmin, xmax, ymin, ymax)  that should be considered to delimit the search (vector)

ATTENTION: run this function with ‘lapply’ to generate a list containing the results of downloaded data for each spp “lista.gbif <- lapply(1:length(spp), function(f0) f.gbif(spp[f0]))”

    1. To clean the results by removal of species with no records downloaded from GBIF and generate a list with the remaining species for which there are records (occurrences data)
f.clean <- function(lista.gbif){
if (length(lista.gbif[[1]]) != 0){
  DF=NULL
for (i in 1:length(lista.gbif)) {
  DF=rbind(DF, lista.gbif[[i]])
  x=as.numeric(as.matrix(DF[,2]))
  y=as.numeric(as.matrix(DF[,3]))
  coord=cbind(x, y)
  }
  spp=as.data.frame(DF[,1])
  all=as.data.frame(cbind(spp, coord))
  list.clean=split(all, all$`DF[, 1]`)
  return(list.clean)}
  
if (length(lista.gbif[[1]]) == 0) {
  DF=NULL
for (i in 2:length(lista.gbif)){
  DF=rbind(DF, lista.gbif[[i]])
  x=as.numeric(as.matrix(DF[,2]))
  y=as.numeric(as.matrix(DF[,3]))
  coord=cbind(x, y)
  }
  spp=as.data.frame(DF[,1])
  all=as.data.frame(cbind(spp, coord))
  list.clean=split(all, all$`DF[, 1]`)
  return(list.clean)}}
    1. To rasterize the occurrence points of species cleaned after download records from GBIF
f.ras <- function (list.clean){
  for (i in 1:length(list.clean)){
    r <- rasterize(as.data.frame(list.clean[[i]][,2:3]), r0.05, background=0, fun='count') # each coordinate point present in each cell is counted  
    r[r > 1] <- 1 # to remove duplicates considering as one all coordinates that is present in the same cell
    c <- crop(r, caatinga) 
    m <- mask(c, caatinga)
    e <- extent(-44.8, -34.8, -16.6, -2.2) 
    r.occu.caat <- extend(m,e)
    nome <- names(list.clean[i]) 
    writeRaster(r.occu.caat,paste(nome,"GBIFPoints.asc",sep = ""),format="ascii", overwrite=T)
  }} 

3 PERFORMING THE ANALYSIS

3.0.0.1 Loading data

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

3.0.0.2 AMPHIBIA (102 models) - Download of these data was made at 19-20 May 2019.

3.0.1 Running steps

### STEP 1 - Running function 1 (f.gbif) to download occurrences records and generate a list with all results gathered from GBIF for each species inside the extension specified (including species with no records) 
lista.gbif.amph <- lapply(1:length(amph.aoh), function(f0) f.gbif(amph.aoh[f0])) 
### STEP 2 - Running function 2 (f.clean) to clean the first object and obtain a list only with species for which there are records
lista.clean.amph <- f.clean(lista.gbif.amph) 
### STEP 3 - Running the function 3 (f.ras) to rasterize the occurrences downloaded for each species and save the raster file GBIFPoints
f.ras(lista.clean.amph) 

# Identifying the spp. with occurrence in, at least, 5 cells
amphs.gbif <- raster::stack(list.files(path=getwd(), pattern = "GBIFPoints.asc$", full.names = F))
amphs.5=sort(cellStats(amphs.gbif, sum)>=5)
amphs.5 # return the species that not meet this criteria

3.0.1.1 REPTILIA (230 models) - Download of these data was made at 19-20 May 2019.

3.0.2 Running steps

### STEP 1 - Running function 1 (f.gbif) to download occurrences records and generate a list with all results gathered from GBIF for each species inside the extension specified (including species with no records) 
lista.gbif.repg <- lapply(1:length(repg.aoh), function(f0) f.gbif(repg.aoh[f0])) 
### STEP 2 - Running function 2 (f.clean) to clean the first object and obtain a list only with species for which there are records
lista.clean.repg <- f.clean(lista.gbif.repg) 
### STEP 3 - Running the function 3 (f.ras) to rasterize the occurrences downloaded for each species and save the raster file GBIFPoints
f.ras(lista.clean.repg) 

# Identifying the spp. with occurrence in, at least, 5 cells
reps.gbif <- raster::stack(list.files(path=getwd(), pattern = "GBIFPoints.asc$", full.names = F))
reps.5=sort(cellStats(reps.gbif, sum)>=5)
reps.5 # return the species that not meet this criteria

3.0.2.1 AVES (494 models) - Download of these data was made at 19-20 May 2019.

3.0.3 Running steps

### STEP 1 - Running function 1 (f.gbif) to download occurrences records and generate a list with all results gathered from GBIF for each species inside the extension specified (including species with no records) 
lista.gbif.aves <- lapply(1:length(avi.aoh), function(f0) f.gbif(avi.aoh[f0])) 
### STEP 2 - Running function 2 (f.clean) to clean the first object and obtain a list only with species for which there are records
lista.clean.aves <- f.clean(lista.gbif.aves) 
### STEP 3 - Running the function 3 (f.ras) to rasterize the occurrences downloaded for each species and save the raster file GBIFPoints
f.ras(lista.clean.aves) 

# Identifying the spp. with occurrence in, at least, 5 cells
aves.gbif <- raster::stack(list.files(path=getwd(), pattern = "GBIFPoints.asc$", full.names = F))
aves.5=sort(cellStats(aves.gbif, sum)>=5)
aves.5 # return the species that not meet this criteria

3.0.3.1 MAMMALIA (188 models) - Download of these data was made at 19-20 May 2019.

3.0.4 Running steps

### STEP 1 - Running function 1 (f.gbif) to download occurrences records and generate a list with all results gathered from GBIF for each species inside the extension specified (including species with no records) 
lista.gbif.mams <- lapply(1:length(mam.aoh), function(f0) f.gbif(mam.aoh[f0])) 
### STEP 2 - Running function 2 (f.clean) to clean the first object and obtain a list only with species for which there are records
lista.clean.mams <- f.clean(lista.gbif.mams) 
### STEP 3 - Running the function 3 (f.ras) to rasterize the occurrences downloaded for each species and save the raster file GBIFPoints
f.ras(lista.clean.mams) 

# Identifying the spp. with occurrence in, at least, 5 cells
mams.gbif <- raster::stack(list.files(path=getwd(), pattern = "GBIFPoints.asc$", full.names = F))
mams.5=sort(cellStats(mams.gbif, sum)>=5)
mams.5 # return the species that not meet this criteria
LS0tDQp0aXRsZTogIlJDb2Rlc19BT0hWYWxpZGF0aW9uIg0KYXV0aG9yOiAiVGhhw61zIEFuZHJhZGUgRmVycmVpcmEgRMOzcmlhIg0KZGF0ZTogJ2ByIFN5cy5EYXRlKClgJw0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCi0tLQ0KDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyBERVNDUklQVElPTg0KDQpUaGlzIFIgTWFya2Rvd24gZG9jdW1lbnQgcHJlc2VudHMgdGhlIGZ1bmN0aW9ucyBhbmQgdGhlIHN0ZXBzIHVzZWQgdG8gdmFsaWRhdGUgc3BlY2llcycgQXJlYSBvZiBIYWJpdGF0IChBT0gpIGFzIGRlc2NyaWJlZCBieSBEw7NyaWEgJiBEb2Jyb3ZvbHNraSAoMjAyMCkgaW4gdGhlIHdvcmsgKioqIkltcHJvdmluZyBwb3N0LTIwMjAgY29uc2VydmF0aW9uIG9mIHRlcnJlc3RyaWFsIHZlcnRlYnJhdGVzIGluIENhYXRpbmdhIioqKi4NCg0KDQojIFJFUVVJUkVNRU5UUw0KDQojIyBJbnN0YWxsIGFuZCBsb2FkIHRoZSByZXF1aXJlZCBwYWNrYWdlcw0KDQpgYGB7ciBldmFsPUZ9DQppbnN0YWxsLnBhY2thZ2VzKCdyZ2RhbCcpDQppbnN0YWxsLnBhY2thZ2VzKCdkaXNtbycpDQppbnN0YWxsLnBhY2thZ2VzKCdzcCcpDQppbnN0YWxsLnBhY2thZ2VzKCdyYXN0ZXInKQ0KaW5zdGFsbC5wYWNrYWdlcygncGx5cicpDQpgYGANCg0KYGBge3IgZXZhbD1GfQ0KbGlicmFyeShyZ2RhbCkNCmxpYnJhcnkoZGlzbW8pDQpsaWJyYXJ5KHNwKQ0KbGlicmFyeShyYXN0ZXIpDQpsaWJyYXJ5KHBseXIpDQpgYGANCg0KDQojIyBMb2FkIHRoZSByZXF1aXJlZCBmdW5jdGlvbnMNCiogMS4gVG8gZG93bmxvYWQgb2NjdXJyZW5jZXMgZGF0YSAobG9uZyBhbmQgbGF0KSBmcm9tIEdCSUYgZGF0YWJhc2UgZm9yIGFsbCBzcGVjaWVzIGxpc3RlZA0KYGBge3J9DQpmLmdiaWYgPC0gZnVuY3Rpb24gKHNwcCl7DQogIGZvciAoaSBpbiAxOmxlbmd0aChzcHApKSB7DQogICAgcC5vY2M9Z2JpZihzcHAsIGV4dD1leCwgZ2VvPVQsIGRvd25sb2FkPVQpDQogICAgcmV0dXJuKGFzLmRhdGEuZnJhbWUoY2JpbmQocC5vY2Mkc3BlY2llcywgcC5vY2MkbG9uLCBwLm9jYyRsYXQpKSkgIyByZXR1cm5pbmcgb25seSBjb29yZGluYXRlcyBhbmQgbmFtZSBvZiBzcGVjaWVzDQogIH19DQojTm90ZXM6DQojJ3NwcCcgaW4gdGhpcyBmdW5jdGlvbiBpcyBsaXN0IHdpdGggc3BlY2llJ3MgbmFtZXMgKGFzLmNoYXJhY3RlcikgDQojJ2V4JyBpbiB0aGlzIGZ1bmN0aW9uIGlzIHRoZSBzcGF0aWFsIGV4dGVuc2lvbiAoeG1pbiwgeG1heCwgeW1pbiwgeW1heCkgIHRoYXQgc2hvdWxkIGJlIGNvbnNpZGVyZWQgdG8gZGVsaW1pdCB0aGUgc2VhcmNoICh2ZWN0b3IpDQpgYGANCkFUVEVOVElPTjogcnVuIHRoaXMgZnVuY3Rpb24gd2l0aCAnbGFwcGx5JyB0byBnZW5lcmF0ZSBhIGxpc3QgY29udGFpbmluZyB0aGUgcmVzdWx0cyBvZiBkb3dubG9hZGVkIGRhdGEgZm9yIGVhY2ggc3BwICJsaXN0YS5nYmlmIDwtIGxhcHBseSgxOmxlbmd0aChzcHApLCBmdW5jdGlvbihmMCkgZi5nYmlmKHNwcFtmMF0pKSINCg0KKiAyLiBUbyBjbGVhbiB0aGUgcmVzdWx0cyBieSByZW1vdmFsIG9mIHNwZWNpZXMgd2l0aCBubyByZWNvcmRzIGRvd25sb2FkZWQgZnJvbSBHQklGIGFuZCBnZW5lcmF0ZSBhIGxpc3Qgd2l0aCB0aGUgcmVtYWluaW5nIHNwZWNpZXMgZm9yIHdoaWNoIHRoZXJlIGFyZSByZWNvcmRzIChvY2N1cnJlbmNlcyBkYXRhKQ0KYGBge3J9DQpmLmNsZWFuIDwtIGZ1bmN0aW9uKGxpc3RhLmdiaWYpew0KaWYgKGxlbmd0aChsaXN0YS5nYmlmW1sxXV0pICE9IDApew0KICBERj1OVUxMDQpmb3IgKGkgaW4gMTpsZW5ndGgobGlzdGEuZ2JpZikpIHsNCiAgREY9cmJpbmQoREYsIGxpc3RhLmdiaWZbW2ldXSkNCiAgeD1hcy5udW1lcmljKGFzLm1hdHJpeChERlssMl0pKQ0KICB5PWFzLm51bWVyaWMoYXMubWF0cml4KERGWywzXSkpDQogIGNvb3JkPWNiaW5kKHgsIHkpDQogIH0NCiAgc3BwPWFzLmRhdGEuZnJhbWUoREZbLDFdKQ0KICBhbGw9YXMuZGF0YS5mcmFtZShjYmluZChzcHAsIGNvb3JkKSkNCiAgbGlzdC5jbGVhbj1zcGxpdChhbGwsIGFsbCRgREZbLCAxXWApDQogIHJldHVybihsaXN0LmNsZWFuKX0NCiAgDQppZiAobGVuZ3RoKGxpc3RhLmdiaWZbWzFdXSkgPT0gMCkgew0KICBERj1OVUxMDQpmb3IgKGkgaW4gMjpsZW5ndGgobGlzdGEuZ2JpZikpew0KICBERj1yYmluZChERiwgbGlzdGEuZ2JpZltbaV1dKQ0KICB4PWFzLm51bWVyaWMoYXMubWF0cml4KERGWywyXSkpDQogIHk9YXMubnVtZXJpYyhhcy5tYXRyaXgoREZbLDNdKSkNCiAgY29vcmQ9Y2JpbmQoeCwgeSkNCiAgfQ0KICBzcHA9YXMuZGF0YS5mcmFtZShERlssMV0pDQogIGFsbD1hcy5kYXRhLmZyYW1lKGNiaW5kKHNwcCwgY29vcmQpKQ0KICBsaXN0LmNsZWFuPXNwbGl0KGFsbCwgYWxsJGBERlssIDFdYCkNCiAgcmV0dXJuKGxpc3QuY2xlYW4pfX0NCmBgYA0KDQoqIDMuIFRvIHJhc3Rlcml6ZSB0aGUgb2NjdXJyZW5jZSBwb2ludHMgb2Ygc3BlY2llcyBjbGVhbmVkIGFmdGVyIGRvd25sb2FkIHJlY29yZHMgZnJvbSBHQklGDQpgYGB7cn0NCmYucmFzIDwtIGZ1bmN0aW9uIChsaXN0LmNsZWFuKXsNCiAgZm9yIChpIGluIDE6bGVuZ3RoKGxpc3QuY2xlYW4pKXsNCiAgICByIDwtIHJhc3Rlcml6ZShhcy5kYXRhLmZyYW1lKGxpc3QuY2xlYW5bW2ldXVssMjozXSksIHIwLjA1LCBiYWNrZ3JvdW5kPTAsIGZ1bj0nY291bnQnKSAjIGVhY2ggY29vcmRpbmF0ZSBwb2ludCBwcmVzZW50IGluIGVhY2ggY2VsbCBpcyBjb3VudGVkICANCiAgICByW3IgPiAxXSA8LSAxICMgdG8gcmVtb3ZlIGR1cGxpY2F0ZXMgY29uc2lkZXJpbmcgYXMgb25lIGFsbCBjb29yZGluYXRlcyB0aGF0IGlzIHByZXNlbnQgaW4gdGhlIHNhbWUgY2VsbA0KICAgIGMgPC0gY3JvcChyLCBjYWF0aW5nYSkgDQogICAgbSA8LSBtYXNrKGMsIGNhYXRpbmdhKQ0KICAgIGUgPC0gZXh0ZW50KC00NC44LCAtMzQuOCwgLTE2LjYsIC0yLjIpIA0KICAgIHIub2NjdS5jYWF0IDwtIGV4dGVuZChtLGUpDQogICAgbm9tZSA8LSBuYW1lcyhsaXN0LmNsZWFuW2ldKSANCiAgICB3cml0ZVJhc3RlcihyLm9jY3UuY2FhdCxwYXN0ZShub21lLCJHQklGUG9pbnRzLmFzYyIsc2VwID0gIiIpLGZvcm1hdD0iYXNjaWkiLCBvdmVyd3JpdGU9VCkNCiAgfX0gDQpgYGANCg0KIyBQRVJGT1JNSU5HIFRIRSBBTkFMWVNJUyAjDQoNCiMjIyMgTG9hZGluZyBkYXRhDQpgYGB7cn0gDQpldmFsZGF0YSA8LSAiaHR0cHM6Ly9naXRodWIuY29tL3RoYWlzZG9yaWEvVERfVGhlc2lzX0NoYXB0ZXIxX1JEYXRhL2Jsb2IvbWFzdGVyL0RvcmlhVEFGX0FPSHNWQUxJREFUSU9OLlJEYXRhP3Jhdz10cnVlIg0KbG9hZCh1cmwoZXZhbGRhdGEpKSAjIFJEYXRhIGZpbGUgY3JlYXRlZCBvbiAyMDE3LzIwMTggY29udGFpbmluZyB0aGUgZmlsZXMgbGlzdGVkIGJlbGxvdw0KYGBgDQoNCg0KIyMjIyBBTVBISUJJQSAoMTAyIG1vZGVscykgLSBEb3dubG9hZCBvZiB0aGVzZSBkYXRhIHdhcyBtYWRlIGF0IDE5LTIwIE1heSAyMDE5LiANCiMjIyBSdW5uaW5nIHN0ZXBzDQpgYGB7ciwgZXZhbD1GfQ0KIyMjIFNURVAgMSAtIFJ1bm5pbmcgZnVuY3Rpb24gMSAoZi5nYmlmKSB0byBkb3dubG9hZCBvY2N1cnJlbmNlcyByZWNvcmRzIGFuZCBnZW5lcmF0ZSBhIGxpc3Qgd2l0aCBhbGwgcmVzdWx0cyBnYXRoZXJlZCBmcm9tIEdCSUYgZm9yIGVhY2ggc3BlY2llcyBpbnNpZGUgdGhlIGV4dGVuc2lvbiBzcGVjaWZpZWQgKGluY2x1ZGluZyBzcGVjaWVzIHdpdGggbm8gcmVjb3JkcykgDQpsaXN0YS5nYmlmLmFtcGggPC0gbGFwcGx5KDE6bGVuZ3RoKGFtcGguYW9oKSwgZnVuY3Rpb24oZjApIGYuZ2JpZihhbXBoLmFvaFtmMF0pKSANCiMjIyBTVEVQIDIgLSBSdW5uaW5nIGZ1bmN0aW9uIDIgKGYuY2xlYW4pIHRvIGNsZWFuIHRoZSBmaXJzdCBvYmplY3QgYW5kIG9idGFpbiBhIGxpc3Qgb25seSB3aXRoIHNwZWNpZXMgZm9yIHdoaWNoIHRoZXJlIGFyZSByZWNvcmRzDQpsaXN0YS5jbGVhbi5hbXBoIDwtIGYuY2xlYW4obGlzdGEuZ2JpZi5hbXBoKSANCiMjIyBTVEVQIDMgLSBSdW5uaW5nIHRoZSBmdW5jdGlvbiAzIChmLnJhcykgdG8gcmFzdGVyaXplIHRoZSBvY2N1cnJlbmNlcyBkb3dubG9hZGVkIGZvciBlYWNoIHNwZWNpZXMgYW5kIHNhdmUgdGhlIHJhc3RlciBmaWxlIEdCSUZQb2ludHMNCmYucmFzKGxpc3RhLmNsZWFuLmFtcGgpIA0KDQojIElkZW50aWZ5aW5nIHRoZSBzcHAuIHdpdGggb2NjdXJyZW5jZSBpbiwgYXQgbGVhc3QsIDUgY2VsbHMNCmFtcGhzLmdiaWYgPC0gcmFzdGVyOjpzdGFjayhsaXN0LmZpbGVzKHBhdGg9Z2V0d2QoKSwgcGF0dGVybiA9ICJHQklGUG9pbnRzLmFzYyQiLCBmdWxsLm5hbWVzID0gRikpDQphbXBocy41PXNvcnQoY2VsbFN0YXRzKGFtcGhzLmdiaWYsIHN1bSk+PTUpDQphbXBocy41ICMgcmV0dXJuIHRoZSBzcGVjaWVzIHRoYXQgbm90IG1lZXQgdGhpcyBjcml0ZXJpYQ0KYGBgDQoNCiMjIyMgUkVQVElMSUEgKDIzMCBtb2RlbHMpIC0gRG93bmxvYWQgb2YgdGhlc2UgZGF0YSB3YXMgbWFkZSBhdCAxOS0yMCBNYXkgMjAxOS4gDQojIyMgUnVubmluZyBzdGVwcw0KYGBge3IsIGV2YWw9Rn0NCiMjIyBTVEVQIDEgLSBSdW5uaW5nIGZ1bmN0aW9uIDEgKGYuZ2JpZikgdG8gZG93bmxvYWQgb2NjdXJyZW5jZXMgcmVjb3JkcyBhbmQgZ2VuZXJhdGUgYSBsaXN0IHdpdGggYWxsIHJlc3VsdHMgZ2F0aGVyZWQgZnJvbSBHQklGIGZvciBlYWNoIHNwZWNpZXMgaW5zaWRlIHRoZSBleHRlbnNpb24gc3BlY2lmaWVkIChpbmNsdWRpbmcgc3BlY2llcyB3aXRoIG5vIHJlY29yZHMpIA0KbGlzdGEuZ2JpZi5yZXBnIDwtIGxhcHBseSgxOmxlbmd0aChyZXBnLmFvaCksIGZ1bmN0aW9uKGYwKSBmLmdiaWYocmVwZy5hb2hbZjBdKSkgDQojIyMgU1RFUCAyIC0gUnVubmluZyBmdW5jdGlvbiAyIChmLmNsZWFuKSB0byBjbGVhbiB0aGUgZmlyc3Qgb2JqZWN0IGFuZCBvYnRhaW4gYSBsaXN0IG9ubHkgd2l0aCBzcGVjaWVzIGZvciB3aGljaCB0aGVyZSBhcmUgcmVjb3Jkcw0KbGlzdGEuY2xlYW4ucmVwZyA8LSBmLmNsZWFuKGxpc3RhLmdiaWYucmVwZykgDQojIyMgU1RFUCAzIC0gUnVubmluZyB0aGUgZnVuY3Rpb24gMyAoZi5yYXMpIHRvIHJhc3Rlcml6ZSB0aGUgb2NjdXJyZW5jZXMgZG93bmxvYWRlZCBmb3IgZWFjaCBzcGVjaWVzIGFuZCBzYXZlIHRoZSByYXN0ZXIgZmlsZSBHQklGUG9pbnRzDQpmLnJhcyhsaXN0YS5jbGVhbi5yZXBnKSANCg0KIyBJZGVudGlmeWluZyB0aGUgc3BwLiB3aXRoIG9jY3VycmVuY2UgaW4sIGF0IGxlYXN0LCA1IGNlbGxzDQpyZXBzLmdiaWYgPC0gcmFzdGVyOjpzdGFjayhsaXN0LmZpbGVzKHBhdGg9Z2V0d2QoKSwgcGF0dGVybiA9ICJHQklGUG9pbnRzLmFzYyQiLCBmdWxsLm5hbWVzID0gRikpDQpyZXBzLjU9c29ydChjZWxsU3RhdHMocmVwcy5nYmlmLCBzdW0pPj01KQ0KcmVwcy41ICMgcmV0dXJuIHRoZSBzcGVjaWVzIHRoYXQgbm90IG1lZXQgdGhpcyBjcml0ZXJpYQ0KYGBgDQoNCiMjIyMgQVZFUyAoNDk0IG1vZGVscykgLSBEb3dubG9hZCBvZiB0aGVzZSBkYXRhIHdhcyBtYWRlIGF0IDE5LTIwIE1heSAyMDE5LiANCiMjIyBSdW5uaW5nIHN0ZXBzDQpgYGB7ciwgZXZhbD1GfQ0KIyMjIFNURVAgMSAtIFJ1bm5pbmcgZnVuY3Rpb24gMSAoZi5nYmlmKSB0byBkb3dubG9hZCBvY2N1cnJlbmNlcyByZWNvcmRzIGFuZCBnZW5lcmF0ZSBhIGxpc3Qgd2l0aCBhbGwgcmVzdWx0cyBnYXRoZXJlZCBmcm9tIEdCSUYgZm9yIGVhY2ggc3BlY2llcyBpbnNpZGUgdGhlIGV4dGVuc2lvbiBzcGVjaWZpZWQgKGluY2x1ZGluZyBzcGVjaWVzIHdpdGggbm8gcmVjb3JkcykgDQpsaXN0YS5nYmlmLmF2ZXMgPC0gbGFwcGx5KDE6bGVuZ3RoKGF2aS5hb2gpLCBmdW5jdGlvbihmMCkgZi5nYmlmKGF2aS5hb2hbZjBdKSkgDQojIyMgU1RFUCAyIC0gUnVubmluZyBmdW5jdGlvbiAyIChmLmNsZWFuKSB0byBjbGVhbiB0aGUgZmlyc3Qgb2JqZWN0IGFuZCBvYnRhaW4gYSBsaXN0IG9ubHkgd2l0aCBzcGVjaWVzIGZvciB3aGljaCB0aGVyZSBhcmUgcmVjb3Jkcw0KbGlzdGEuY2xlYW4uYXZlcyA8LSBmLmNsZWFuKGxpc3RhLmdiaWYuYXZlcykgDQojIyMgU1RFUCAzIC0gUnVubmluZyB0aGUgZnVuY3Rpb24gMyAoZi5yYXMpIHRvIHJhc3Rlcml6ZSB0aGUgb2NjdXJyZW5jZXMgZG93bmxvYWRlZCBmb3IgZWFjaCBzcGVjaWVzIGFuZCBzYXZlIHRoZSByYXN0ZXIgZmlsZSBHQklGUG9pbnRzDQpmLnJhcyhsaXN0YS5jbGVhbi5hdmVzKSANCg0KIyBJZGVudGlmeWluZyB0aGUgc3BwLiB3aXRoIG9jY3VycmVuY2UgaW4sIGF0IGxlYXN0LCA1IGNlbGxzDQphdmVzLmdiaWYgPC0gcmFzdGVyOjpzdGFjayhsaXN0LmZpbGVzKHBhdGg9Z2V0d2QoKSwgcGF0dGVybiA9ICJHQklGUG9pbnRzLmFzYyQiLCBmdWxsLm5hbWVzID0gRikpDQphdmVzLjU9c29ydChjZWxsU3RhdHMoYXZlcy5nYmlmLCBzdW0pPj01KQ0KYXZlcy41ICMgcmV0dXJuIHRoZSBzcGVjaWVzIHRoYXQgbm90IG1lZXQgdGhpcyBjcml0ZXJpYQ0KYGBgDQoNCg0KIyMjIyBNQU1NQUxJQSAoMTg4IG1vZGVscykgLSBEb3dubG9hZCBvZiB0aGVzZSBkYXRhIHdhcyBtYWRlIGF0IDE5LTIwIE1heSAyMDE5LiANCiMjIyBSdW5uaW5nIHN0ZXBzDQpgYGB7ciwgZXZhbD1GfQ0KIyMjIFNURVAgMSAtIFJ1bm5pbmcgZnVuY3Rpb24gMSAoZi5nYmlmKSB0byBkb3dubG9hZCBvY2N1cnJlbmNlcyByZWNvcmRzIGFuZCBnZW5lcmF0ZSBhIGxpc3Qgd2l0aCBhbGwgcmVzdWx0cyBnYXRoZXJlZCBmcm9tIEdCSUYgZm9yIGVhY2ggc3BlY2llcyBpbnNpZGUgdGhlIGV4dGVuc2lvbiBzcGVjaWZpZWQgKGluY2x1ZGluZyBzcGVjaWVzIHdpdGggbm8gcmVjb3JkcykgDQpsaXN0YS5nYmlmLm1hbXMgPC0gbGFwcGx5KDE6bGVuZ3RoKG1hbS5hb2gpLCBmdW5jdGlvbihmMCkgZi5nYmlmKG1hbS5hb2hbZjBdKSkgDQojIyMgU1RFUCAyIC0gUnVubmluZyBmdW5jdGlvbiAyIChmLmNsZWFuKSB0byBjbGVhbiB0aGUgZmlyc3Qgb2JqZWN0IGFuZCBvYnRhaW4gYSBsaXN0IG9ubHkgd2l0aCBzcGVjaWVzIGZvciB3aGljaCB0aGVyZSBhcmUgcmVjb3Jkcw0KbGlzdGEuY2xlYW4ubWFtcyA8LSBmLmNsZWFuKGxpc3RhLmdiaWYubWFtcykgDQojIyMgU1RFUCAzIC0gUnVubmluZyB0aGUgZnVuY3Rpb24gMyAoZi5yYXMpIHRvIHJhc3Rlcml6ZSB0aGUgb2NjdXJyZW5jZXMgZG93bmxvYWRlZCBmb3IgZWFjaCBzcGVjaWVzIGFuZCBzYXZlIHRoZSByYXN0ZXIgZmlsZSBHQklGUG9pbnRzDQpmLnJhcyhsaXN0YS5jbGVhbi5tYW1zKSANCg0KIyBJZGVudGlmeWluZyB0aGUgc3BwLiB3aXRoIG9jY3VycmVuY2UgaW4sIGF0IGxlYXN0LCA1IGNlbGxzDQptYW1zLmdiaWYgPC0gcmFzdGVyOjpzdGFjayhsaXN0LmZpbGVzKHBhdGg9Z2V0d2QoKSwgcGF0dGVybiA9ICJHQklGUG9pbnRzLmFzYyQiLCBmdWxsLm5hbWVzID0gRikpDQptYW1zLjU9c29ydChjZWxsU3RhdHMobWFtcy5nYmlmLCBzdW0pPj01KQ0KbWFtcy41ICMgcmV0dXJuIHRoZSBzcGVjaWVzIHRoYXQgbm90IG1lZXQgdGhpcyBjcml0ZXJpYQ0KYGBgDQoNCg==