This script extracts reflectance spectra of a set of art pigments from a VISNIR hyperspectral image acquired with a demo unit of the Specim IQ system in HSILab premises on 20180918:

Pigment samples were provided by

defaultW <- getOption("warn")
options(warn=-1)
#Paths
RSpectDir       <- "/home/alobo/owncloudRSpect/RSpect"
datadir         <- "/home/alobo/WORK/HSI_Lab/Tests_Hypercams/SpecimIQ/SpecimIQTest_2018-09-18/2018-10-18_008"
imadir          <- file.path(datadir,"results")
vecdir          <- file.path(datadir,"QGIS/Polygons")
#libraries
library(reshape2, quietly = TRUE)
library(plyr, quietly = TRUE)
library(ggplot2, quietly = TRUE)
library(plotly, quietly = TRUE)
library(terra, quietly = TRUE)
library(tidyterra, quietly = TRUE)
library(gridExtra, quietly = TRUE)
library(directlabels, quietly = TRUE)

#Required information
load(file.path(RSpectDir,"IQbands.rda")) #Bands Specim IQ system

1. Read images and polygons

1.1 RGB image

A conventional RGB image in png format is automatically recorded by a dedicated CCD in Specim IQ. By some reason, this image is rotated vs. the hyperspectral one, so the code rotates it.

options(warn=-1)
r <- rast(file.path(imadir,"RGBBACKGROUND_2018-10-18_008.png"))
r
class       : SpatRaster 
dimensions  : 645, 645, 4  (nrow, ncol, nlyr)
resolution  : 1, 1  (x, y)
extent      : 0, 645, 0, 645  (xmin, xmax, ymin, ymax)
coord. ref. :  
source      : RGBBACKGROUND_2018-10-18_008.png 
names       : RGBBACK~8_008_1, RGBBACK~8_008_2, RGBBACK~8_008_3, RGBBACK~8_008_4 
#plotRGB(r, stretch="lin")
plotRGB(flip(t(r)))

1.2 Reflectance image

The reflectance image was calculated by the Specim IQ system itself using the average value of the White reference

class       : SpatRaster 
dimensions  : 512, 512, 204  (nrow, ncol, nlyr)
resolution  : 1, 1  (x, y)
extent      : 0, 512, 0, 512  (xmin, xmax, ymin, ymax)
coord. ref. :  
source      : REFLECTANCE_2018-10-18_008.dat 
names       : 397.32, 400.20, 403.09, 405.97, 408.85, 411.74, ... 

We display both RGB and CIR color composites with the following spectral bands:

RGB

Band Wavelength
70 Band070 598.60
53 Band053 548.55
19 Band019 449.35

CIR

Band Wavelength
203 Band203 1000.49
70 Band070 598.60
53 Band053 548.55
  crs(s) <- ""
  #suppressMessages(
  ggRad <- ggplot() + 
  geom_spatraster_rgb(data = stretch(subset(s, subset=defbands), minq=0.02, maxq=0.85)) +
    coord_fixed(ratio = 1) +
    theme_void() + theme(plot.title = element_text(hjust = 0.5)) +
    ggtitle(paste0("\nBands ", paste(defbands,collapse=", ")))
  ggRad2 <- ggplot() + 
    geom_spatraster_rgb(data = stretch(subset(s, subset=defbands2), minq=0.02, maxq=0.85)) +
    coord_fixed(ratio = 1) +
    theme_void() + theme(plot.title = element_text(hjust = 0.5)) +
    ggtitle(paste0("\nBands ", paste(defbands2,collapse=", ")))
  #)
  grid.arrange(ggRad, ggRad2, ncol=2)

1.3 Polygon vector

Relevant parts of each object were interactively digitized as polygons in QGIS and used to select spectra from the image.

options(warn=-1)
nompoly <- "008.shp"
p <- vect(file.path(vecdir,nompoly))
#values(p)$Name <- c("White", "Ochre", "Ochre_800",
#                    "Red", "Red_800",
#                    "GreenTerra", "GreenTerra_800")
p
 class       : SpatVector 
 geometry    : polygons 
 dimensions  : 7, 7  (geometries, attributes)
 extent      : 72.29907, 382.5495, -352.3252, -180.9495  (xmin, xmax, ymin, ymax)
 source      : 008.shp
 coord. ref. : ETRS89 / UTM zone 31N (EPSG:25831) 
#knitr::kable(p)

2. Make consistent geometry

In case the CRS is not defined in the image (and this is the unfortunate case of Specim hdr files), R and QGIS assume, by default, different geometry for the same tiff. As a consequence, polygons digitized in QGIS do not overlay the raster image in R. The code must account for this fact, making a consistent geometry for raster and vector layer, which we confirm in the RGB display with bands 70 (598 nm), 53 (548 nm) and 19 (449 nm).

crs(s) <- crs(p)
ext(s) <- c(0,512,-512,0)
#plotRGB(stretch(s[[defbands2]], smin=0.0, smax=0.85), ext=c(50,400,-360,-100))
plotRGB(stretch(s[[defbands]], smin=0.0, smax=0.4), ext=c(50,200,-400,-150),
        main=IQbands[defbands,])
lines(p, xlim=c(50,200))
text(p,5,cex=0.9,font=2)
text(p,5,cex=0.9,font=1,col=0)

#lines(p,ext=c(50,400,-360,-100)) # "ext" is not a graphical parameter
#lines(ext(c(50,400,-360,-100)))

3. Object spectra

Finally, using the polygons as templates, we extract the reflectance values from the hyperspectral image and plot.

options(warn=-1)
refldat.m  <- terra::extract(s, p, fun=mean, na.rm=TRUE)
refldat.sd <- terra::extract(s, p, fun=sd, na.rm=TRUE)

refldatm <- as.data.frame(t(refldat.m[,-1]))
#colnames(refldatm) <- values(p)$Name
colnames(refldatm) <- values(p)$Acronym
refldatm$Band <- IQbands$Band
refldatm$Wavelength <- IQbands$Wavelength
#head(refldatm)
refldatsd <- as.data.frame(t(refldat.sd[,-1]))
colnames(refldatsd) <- values(p)$Name
#head(refldatsd)

refldat <- melt(refldatm,id.vars = c("Band", "Wavelength"))
#head(refldat)
names(refldat)[3:4] <- c("Sample", "Reflectance")

a <- melt(refldatsd,id.vars=NULL)
#head(a)
refldat$sd <- a$value
refldat$Treatment <- mapvalues(refldat$Sample, from=values(p)$Acronym, to=values(p)$Treatment)
refldat$Pigment <- mapvalues(refldat$Sample, from=values(p)$Acronym, to=values(p)$Pigment)
#head(refldat)

#gg1 <- plothcavspect(datainRef,title="")
#Default colors
ggplotColours <- function(n = 6, h = c(0, 360) + 15){
  if ((diff(h) %% 360) < 1) h[2] <- h[2] - 360/n
  hcl(h = (seq(h[1], h[2], length = n)), c = 100, l = 65)
}

micolor <- ggplotColours(n=36)
#scales:::show_col(micolor)
#https://www.nceas.ucsb.edu/sites/default/files/2020-04/colorPaletteCheatsheet.pdf
micolor <- c("W"="grey70", 
             "Och"="coral4",
             "Och_800"="chocolate4",
             "R"="red",
             "R_800"="red4",
             "G"="olivedrab3",
             "G_800"="olivedrab")

gg1 <- ggplot(data=refldat) +
  geom_point(aes(x=Wavelength, y=Reflectance, color=Sample, shape=Treatment),size=0.5) +
  geom_line(aes(x=Wavelength, y=Reflectance, color=Sample, linetype=Treatment)) +
  geom_errorbar(aes(x=Wavelength,ymin=Reflectance-sd, ymax=Reflectance+sd, color=Sample),
                width=.001, alpha=0.5,
                position=position_dodge(0.05)) +
  xlab("Wavelength (nm)") +
  theme(legend.position = "bottom") +
  ggtitle("Art pigments")

  gg1 + facet_wrap(~Pigment) + scale_color_manual(values=micolor) + geom_dl(aes(x=Wavelength, y=Reflectance, label = Sample), method = list(dl.combine("first.points", "last.points"), cex = 0.7)) + xlim(c(300, 1100))


#gg1 + xlim(c(450, 1000))  + 
#  facet_wrap(~State) + geom_dl(aes(x=Wavelength, y=Reflectance, label = Sample), method = list(dl.combine("first.points", "last.points"), cex = 0.7)) 
#gg2 <- gg1 + 

ggplotly(gg1,
         tooltip=c("Band","Wavelength",Reflectance="Reflectance","Sample"),
         height=600, width=900)
NA
LS0tCnRpdGxlOiAiU3BlY2ltIElRIHRlc3Qgd2l0aCBhcnQgcGlnbWVudHMgKDIwMTgwOTE4KSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIGZpZ19jYXB0aW9uOiBUUlVFCi0tLQoKPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KICAudGFibGUge3dpZHRoOiAyNSU7fQo8L3N0eWxlPgoKKiBBZ3VzdGluLkxvYm9AZ2VvM2Jjbi5jc2ljLmVzCiogMjAyMjExMDgKClRoaXMgc2NyaXB0IGV4dHJhY3RzIHJlZmxlY3RhbmNlIHNwZWN0cmEgb2YgYSBzZXQgb2YgYXJ0IHBpZ21lbnRzIGZyb20gYSBWSVNOSVIgaHlwZXJzcGVjdHJhbCBpbWFnZSBhY3F1aXJlZCB3aXRoIGEgZGVtbyB1bml0IG9mIHRoZSBTcGVjaW0gSVEgc3lzdGVtIGluIEhTSUxhYiBwcmVtaXNlcyBvbiAyMDE4MDkxODoKCiogU3BlY2ltIElRIHdoaXRlIHJlZmVyZW5jZSBjYXJkLgoqIE9jcmUgKE9jaHJlKQoqIE9jcmUgODAwIChPY2hyZSBoZWF0ZWQgdG8gODAwwrpDKQoqIFZlcm1lbGwgKElyb24gT3hpZGUgUmVkKQoqIFJlZCA4MDAgKElyb24gT3hpZGUgUmVkIGhlYXRlZCB0byA4MDDCukMpCiogVGVycmEgIChncmVlbiBlYXJ0aCkKKiBUZXJyYSBWZXJkYSA4MDAgKGdyZWVuIGVhcnRoIGhlYXRlZCB0byA4MDDCukMpCgpQaWdtZW50IHNhbXBsZXMgd2VyZSBwcm92aWRlZCBieSBqaWJhbmV6QGdlbzNiY24uY3NpYy5lcwoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRlZmF1bHRXIDwtIGdldE9wdGlvbigid2FybiIpCm9wdGlvbnMod2Fybj0tMSkKI1BhdGhzClJTcGVjdERpciAgICAgICA8LSAiL2hvbWUvYWxvYm8vb3duY2xvdWRSU3BlY3QvUlNwZWN0IgpkYXRhZGlyICAgICAgICAgPC0gIi9ob21lL2Fsb2JvL1dPUksvSFNJX0xhYi9UZXN0c19IeXBlcmNhbXMvU3BlY2ltSVEvU3BlY2ltSVFUZXN0XzIwMTgtMDktMTgvMjAxOC0xMC0xOF8wMDgiCmltYWRpciAgICAgICAgICA8LSBmaWxlLnBhdGgoZGF0YWRpciwicmVzdWx0cyIpCnZlY2RpciAgICAgICAgICA8LSBmaWxlLnBhdGgoZGF0YWRpciwiUUdJUy9Qb2x5Z29ucyIpCiNsaWJyYXJpZXMKbGlicmFyeShyZXNoYXBlMiwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkocGx5ciwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkoZ2dwbG90MiwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkocGxvdGx5LCBxdWlldGx5ID0gVFJVRSkKbGlicmFyeSh0ZXJyYSwgcXVpZXRseSA9IFRSVUUpCmxpYnJhcnkodGlkeXRlcnJhLCBxdWlldGx5ID0gVFJVRSkKbGlicmFyeShncmlkRXh0cmEsIHF1aWV0bHkgPSBUUlVFKQpsaWJyYXJ5KGRpcmVjdGxhYmVscywgcXVpZXRseSA9IFRSVUUpCgojUmVxdWlyZWQgaW5mb3JtYXRpb24KbG9hZChmaWxlLnBhdGgoUlNwZWN0RGlyLCJJUWJhbmRzLnJkYSIpKSAjQmFuZHMgU3BlY2ltIElRIHN5c3RlbQpgYGAKCiMjIDEuIFJlYWQgaW1hZ2VzIGFuZCBwb2x5Z29ucwojIyMgMS4xIFJHQiBpbWFnZQpBIGNvbnZlbnRpb25hbCBSR0IgaW1hZ2UgaW4gcG5nIGZvcm1hdCBpcyBhdXRvbWF0aWNhbGx5IHJlY29yZGVkIGJ5IGEgZGVkaWNhdGVkIENDRCBpbiBTcGVjaW0gSVEuIEJ5IHNvbWUgcmVhc29uLCB0aGlzIGltYWdlIGlzIHJvdGF0ZWQgdnMuIHRoZSBoeXBlcnNwZWN0cmFsIG9uZSwgc28gdGhlIGNvZGUgcm90YXRlcyBpdC4KYGBge3IgfQpvcHRpb25zKHdhcm49LTEpCnIgPC0gcmFzdChmaWxlLnBhdGgoaW1hZGlyLCJSR0JCQUNLR1JPVU5EXzIwMTgtMTAtMThfMDA4LnBuZyIpKQpyCiNwbG90UkdCKHIsIHN0cmV0Y2g9ImxpbiIpCnBsb3RSR0IoZmxpcCh0KHIpKSkKYGBgCgojIyMgMS4yIFJlZmxlY3RhbmNlIGltYWdlClRoZSByZWZsZWN0YW5jZSBpbWFnZSB3YXMgY2FsY3VsYXRlZCBieSB0aGUgU3BlY2ltIElRIHN5c3RlbSBpdHNlbGYgdXNpbmcgdGhlIGF2ZXJhZ2UgdmFsdWUgb2YgdGhlIFdoaXRlIHJlZmVyZW5jZSAKYGBge3IgZWNobz1PRkZ9Cm9wdGlvbnMod2Fybj0tMSkKbm9tcmVmIDwtIGxpc3QuZmlsZXMoaW1hZGlyLHBhdHQ9J2RhdCcpWzFdCnMgPC0gcmFzdChmaWxlLnBhdGgoaW1hZGlyLG5vbXJlZikpCnMKYGBgCldlIGRpc3BsYXkgYm90aCBSR0IgYW5kIENJUiBjb2xvciBjb21wb3NpdGVzIHdpdGggdGhlIGZvbGxvd2luZyBzcGVjdHJhbCBiYW5kczoKYGBge3IgZWNobz1PRkYsIHJlc3VsdHM9ImFzaXMifQogIGRlZmJhbmRzIDwtIGMoNzAsNTMsMTkpCiAgZGVmYmFuZHMyIDwtIGMoMjAzLDcwLDUzKQogIGNhdCgiUkdCIikKICBrbml0cjo6a2FibGUoSVFiYW5kc1tkZWZiYW5kcyxdKQogIGNhdCgiQ0lSIikKICBrbml0cjo6a2FibGUoSVFiYW5kc1tkZWZiYW5kczIsXSkKYGBgCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQogIGNycyhzKSA8LSAiIgogICNzdXBwcmVzc01lc3NhZ2VzKAogIGdnUmFkIDwtIGdncGxvdCgpICsgCiAgZ2VvbV9zcGF0cmFzdGVyX3JnYihkYXRhID0gc3RyZXRjaChzdWJzZXQocywgc3Vic2V0PWRlZmJhbmRzKSwgbWlucT0wLjAyLCBtYXhxPTAuODUpKSArCiAgICBjb29yZF9maXhlZChyYXRpbyA9IDEpICsKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgICBnZ3RpdGxlKHBhc3RlMCgiXG5CYW5kcyAiLCBwYXN0ZShkZWZiYW5kcyxjb2xsYXBzZT0iLCAiKSkpCiAgZ2dSYWQyIDwtIGdncGxvdCgpICsgCiAgICBnZW9tX3NwYXRyYXN0ZXJfcmdiKGRhdGEgPSBzdHJldGNoKHN1YnNldChzLCBzdWJzZXQ9ZGVmYmFuZHMyKSwgbWlucT0wLjAyLCBtYXhxPTAuODUpKSArCiAgICBjb29yZF9maXhlZChyYXRpbyA9IDEpICsKICAgIHRoZW1lX3ZvaWQoKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgICBnZ3RpdGxlKHBhc3RlMCgiXG5CYW5kcyAiLCBwYXN0ZShkZWZiYW5kczIsY29sbGFwc2U9IiwgIikpKQogICMpCiAgZ3JpZC5hcnJhbmdlKGdnUmFkLCBnZ1JhZDIsIG5jb2w9MikKYGBgCgojIyMgMS4zIFBvbHlnb24gdmVjdG9yClJlbGV2YW50IHBhcnRzIG9mIGVhY2ggb2JqZWN0IHdlcmUgaW50ZXJhY3RpdmVseSBkaWdpdGl6ZWQgYXMgcG9seWdvbnMgaW4gUUdJUyBhbmQgdXNlZCB0byBzZWxlY3Qgc3BlY3RyYSBmcm9tIHRoZSBpbWFnZS4KYGBge3IgfQpvcHRpb25zKHdhcm49LTEpCm5vbXBvbHkgPC0gIjAwOC5zaHAiCnAgPC0gdmVjdChmaWxlLnBhdGgodmVjZGlyLG5vbXBvbHkpKQojdmFsdWVzKHApJE5hbWUgPC0gYygiV2hpdGUiLCAiT2NocmUiLCAiT2NocmVfODAwIiwKIyAgICAgICAgICAgICAgICAgICAgIlJlZCIsICJSZWRfODAwIiwKIyAgICAgICAgICAgICAgICAgICAgIkdyZWVuVGVycmEiLCAiR3JlZW5UZXJyYV84MDAiKQpwCiNrbml0cjo6a2FibGUocCkKYGBgCgojIyAyLiBNYWtlIGNvbnNpc3RlbnQgZ2VvbWV0cnkKSW4gY2FzZSB0aGUgQ1JTIGlzIG5vdCBkZWZpbmVkIGluIHRoZSBpbWFnZSAoYW5kIHRoaXMgaXMgdGhlIHVuZm9ydHVuYXRlIGNhc2Ugb2YgU3BlY2ltIGhkciBmaWxlcyksIFIgYW5kIFFHSVMgYXNzdW1lLCBieSBkZWZhdWx0LCBkaWZmZXJlbnQgZ2VvbWV0cnkgZm9yIHRoZSBzYW1lIHRpZmYuIEFzIGEgY29uc2VxdWVuY2UsIHBvbHlnb25zIGRpZ2l0aXplZCBpbiBRR0lTIGRvIG5vdCBvdmVybGF5IHRoZSByYXN0ZXIgaW1hZ2UgaW4gUi4KVGhlIGNvZGUgbXVzdCBhY2NvdW50IGZvciB0aGlzIGZhY3QsIG1ha2luZyBhIGNvbnNpc3RlbnQgZ2VvbWV0cnkgZm9yIHJhc3RlciBhbmQgdmVjdG9yIGxheWVyLCB3aGljaCB3ZSBjb25maXJtIGluIHRoZSBSR0IgZGlzcGxheSB3aXRoIGJhbmRzIDcwICg1OTggbm0pLCA1MyAoNTQ4IG5tKSBhbmQgMTkgKDQ0OSBubSkuCgpgYGB7ciB9CmNycyhzKSA8LSBjcnMocCkKZXh0KHMpIDwtIGMoMCw1MTIsLTUxMiwwKQojcGxvdFJHQihzdHJldGNoKHNbW2RlZmJhbmRzMl1dLCBzbWluPTAuMCwgc21heD0wLjg1KSwgZXh0PWMoNTAsNDAwLC0zNjAsLTEwMCkpCnBsb3RSR0Ioc3RyZXRjaChzW1tkZWZiYW5kc11dLCBzbWluPTAuMCwgc21heD0wLjQpLCBleHQ9Yyg1MCwyMDAsLTQwMCwtMTUwKSwKICAgICAgICBtYWluPUlRYmFuZHNbZGVmYmFuZHMsXSkKbGluZXMocCwgeGxpbT1jKDUwLDIwMCkpCnRleHQocCw1LGNleD0wLjksZm9udD0yKQp0ZXh0KHAsNSxjZXg9MC45LGZvbnQ9MSxjb2w9MCkKI2xpbmVzKHAsZXh0PWMoNTAsNDAwLC0zNjAsLTEwMCkpICMgImV4dCIgaXMgbm90IGEgZ3JhcGhpY2FsIHBhcmFtZXRlcgojbGluZXMoZXh0KGMoNTAsNDAwLC0zNjAsLTEwMCkpKQpgYGAKCiMjIDMuIE9iamVjdCBzcGVjdHJhCkZpbmFsbHksIHVzaW5nIHRoZSBwb2x5Z29ucyBhcyB0ZW1wbGF0ZXMsIHdlIGV4dHJhY3QgdGhlIHJlZmxlY3RhbmNlIHZhbHVlcyBmcm9tIHRoZSBoeXBlcnNwZWN0cmFsIGltYWdlIGFuZCBwbG90LgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQpvcHRpb25zKHdhcm49LTEpCnJlZmxkYXQubSAgPC0gdGVycmE6OmV4dHJhY3QocywgcCwgZnVuPW1lYW4sIG5hLnJtPVRSVUUpCnJlZmxkYXQuc2QgPC0gdGVycmE6OmV4dHJhY3QocywgcCwgZnVuPXNkLCBuYS5ybT1UUlVFKQoKcmVmbGRhdG0gPC0gYXMuZGF0YS5mcmFtZSh0KHJlZmxkYXQubVssLTFdKSkKI2NvbG5hbWVzKHJlZmxkYXRtKSA8LSB2YWx1ZXMocCkkTmFtZQpjb2xuYW1lcyhyZWZsZGF0bSkgPC0gdmFsdWVzKHApJEFjcm9ueW0KcmVmbGRhdG0kQmFuZCA8LSBJUWJhbmRzJEJhbmQKcmVmbGRhdG0kV2F2ZWxlbmd0aCA8LSBJUWJhbmRzJFdhdmVsZW5ndGgKI2hlYWQocmVmbGRhdG0pCnJlZmxkYXRzZCA8LSBhcy5kYXRhLmZyYW1lKHQocmVmbGRhdC5zZFssLTFdKSkKY29sbmFtZXMocmVmbGRhdHNkKSA8LSB2YWx1ZXMocCkkTmFtZQojaGVhZChyZWZsZGF0c2QpCgpyZWZsZGF0IDwtIG1lbHQocmVmbGRhdG0saWQudmFycyA9IGMoIkJhbmQiLCAiV2F2ZWxlbmd0aCIpKQojaGVhZChyZWZsZGF0KQpuYW1lcyhyZWZsZGF0KVszOjRdIDwtIGMoIlNhbXBsZSIsICJSZWZsZWN0YW5jZSIpCgphIDwtIG1lbHQocmVmbGRhdHNkLGlkLnZhcnM9TlVMTCkKI2hlYWQoYSkKcmVmbGRhdCRzZCA8LSBhJHZhbHVlCnJlZmxkYXQkVHJlYXRtZW50IDwtIG1hcHZhbHVlcyhyZWZsZGF0JFNhbXBsZSwgZnJvbT12YWx1ZXMocCkkQWNyb255bSwgdG89dmFsdWVzKHApJFRyZWF0bWVudCkKcmVmbGRhdCRQaWdtZW50IDwtIG1hcHZhbHVlcyhyZWZsZGF0JFNhbXBsZSwgZnJvbT12YWx1ZXMocCkkQWNyb255bSwgdG89dmFsdWVzKHApJFBpZ21lbnQpCiNoZWFkKHJlZmxkYXQpCgojZ2cxIDwtIHBsb3RoY2F2c3BlY3QoZGF0YWluUmVmLHRpdGxlPSIiKQojRGVmYXVsdCBjb2xvcnMKZ2dwbG90Q29sb3VycyA8LSBmdW5jdGlvbihuID0gNiwgaCA9IGMoMCwgMzYwKSArIDE1KXsKICBpZiAoKGRpZmYoaCkgJSUgMzYwKSA8IDEpIGhbMl0gPC0gaFsyXSAtIDM2MC9uCiAgaGNsKGggPSAoc2VxKGhbMV0sIGhbMl0sIGxlbmd0aCA9IG4pKSwgYyA9IDEwMCwgbCA9IDY1KQp9CgptaWNvbG9yIDwtIGdncGxvdENvbG91cnMobj0zNikKI3NjYWxlczo6OnNob3dfY29sKG1pY29sb3IpCiNodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9zaXRlcy9kZWZhdWx0L2ZpbGVzLzIwMjAtMDQvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYKbWljb2xvciA8LSBjKCJXIj0iZ3JleTcwIiwgCiAgICAgICAgICAgICAiT2NoIj0iY29yYWw0IiwKICAgICAgICAgICAgICJPY2hfODAwIj0iY2hvY29sYXRlNCIsCiAgICAgICAgICAgICAiUiI9InJlZCIsCiAgICAgICAgICAgICAiUl84MDAiPSJyZWQ0IiwKICAgICAgICAgICAgICJHIj0ib2xpdmVkcmFiMyIsCiAgICAgICAgICAgICAiR184MDAiPSJvbGl2ZWRyYWIiKQoKZ2cxIDwtIGdncGxvdChkYXRhPXJlZmxkYXQpICsKICBnZW9tX3BvaW50KGFlcyh4PVdhdmVsZW5ndGgsIHk9UmVmbGVjdGFuY2UsIGNvbG9yPVNhbXBsZSwgc2hhcGU9VHJlYXRtZW50KSxzaXplPTAuNSkgKwogIGdlb21fbGluZShhZXMoeD1XYXZlbGVuZ3RoLCB5PVJlZmxlY3RhbmNlLCBjb2xvcj1TYW1wbGUsIGxpbmV0eXBlPVRyZWF0bWVudCkpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh4PVdhdmVsZW5ndGgseW1pbj1SZWZsZWN0YW5jZS1zZCwgeW1heD1SZWZsZWN0YW5jZStzZCwgY29sb3I9U2FtcGxlKSwKICAgICAgICAgICAgICAgIHdpZHRoPS4wMDEsIGFscGhhPTAuNSwKICAgICAgICAgICAgICAgIHBvc2l0aW9uPXBvc2l0aW9uX2RvZGdlKDAuMDUpKSArCiAgeGxhYigiV2F2ZWxlbmd0aCAobm0pIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgZ2d0aXRsZSgiQXJ0IHBpZ21lbnRzIikKCiAgZ2cxICsgZmFjZXRfd3JhcCh+UGlnbWVudCkgKyBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPW1pY29sb3IpICsgZ2VvbV9kbChhZXMoeD1XYXZlbGVuZ3RoLCB5PVJlZmxlY3RhbmNlLCBsYWJlbCA9IFNhbXBsZSksIG1ldGhvZCA9IGxpc3QoZGwuY29tYmluZSgiZmlyc3QucG9pbnRzIiwgImxhc3QucG9pbnRzIiksIGNleCA9IDAuNykpICsgeGxpbShjKDMwMCwgMTEwMCkpCgojZ2cxICsgeGxpbShjKDQ1MCwgMTAwMCkpICArIAojICBmYWNldF93cmFwKH5TdGF0ZSkgKyBnZW9tX2RsKGFlcyh4PVdhdmVsZW5ndGgsIHk9UmVmbGVjdGFuY2UsIGxhYmVsID0gU2FtcGxlKSwgbWV0aG9kID0gbGlzdChkbC5jb21iaW5lKCJmaXJzdC5wb2ludHMiLCAibGFzdC5wb2ludHMiKSwgY2V4ID0gMC43KSkgCiNnZzIgPC0gZ2cxICsgCgpnZ3Bsb3RseShnZzEsCiAgICAgICAgIHRvb2x0aXA9YygiQmFuZCIsIldhdmVsZW5ndGgiLFJlZmxlY3RhbmNlPSJSZWZsZWN0YW5jZSIsIlNhbXBsZSIpLAogICAgICAgICBoZWlnaHQ9NjAwLCB3aWR0aD05MDApCgpgYGAKCg==