if(!require(drc)) install.packages("drc")
if(!require(geoR)) install.packages("geoR")
if(!require(stats)) install.packages("stats")
if(!require(ggplot2)) install.packages("ggplot2")
if(!require(ggrepel)) install.packages("ggrepel")
if(!require(nortest)) install.packages("nortest")
if(!require(graphics)) install.packages("graphics")
if(!require(EnvStats)) install.packages("EnvStats")
if(!require(rcompanion)) install.packages("rcompanion")
if(!require(aomisc)) install_github("OnofriAndreaPG/aomisc")
Neste documento, utilizamos um modelo de regressão usando uma função base não-linear. Para isto, primeiramente aplicamos a transformação de Box-Cox na variável resposta do nosso problema, ou seja, nos valores de complexidade estatística:
HC.BP = data.frame("H" = numeric(20800),
"C" = numeric(20800),
"D" = numeric(20800),
"t" = numeric(20800),
"N" = numeric(20800),
"index" = numeric(20800),
stringsAsFactors=FALSE)
HC.BP$index = as.factor(c(1:20800))
HC.BP$N = as.factor(rep(c(rep(1e+04, 400), rep(2e+04, 100), rep(3e+04, 100), rep(4e+04, 100), rep(5e+04, 100), rep(6e+04, 100), rep(7e+04, 100), rep(8e+04, 100), rep(9e+04, 100), rep(1e+05, 100)), 16))
HC.BP$t = as.factor(rep(c(rep(1,1300), rep(2,1300), rep(3,1300), rep(4,1300)), 4))
file.csv = data.frame(read.csv("../Data/HC_series_fk0.csv"))
HC.BP$H = file.csv[,1]
HC.BP$C = file.csv[,2]
HC.BP$D= as.factor(file.csv[,3])
parameters = boxcoxfit(HC.BP$C, lambda2 = F)
lambda = parameters$lambda[1]
if(lambda==0){T.norm = log(HC.BP$C)}
if(lambda!=0){T.norm = ((HC.BP$C)^lambda - 1)/lambda}
T.norm = (T.norm - min(T.norm))/(max(T.norm) - min(T.norm))
n.obs = sum(!is.na(T.norm))
T.norm = (T.norm * (n.obs - 1) + 0.5)/n.obs
HC.BP$C = T.norm
plot(x = HC.BP$H, y = HC.BP$C)

Para selecionar o kernel que melhor ajuste o nosso modelo adotamos uma abordagem empírica. Ou seja, analisamos o problema, plotamos os dados e observamos que eles seguem um certo padrão. Um problema com a regressão não linear é que ele funciona iterativamente: precisamos fornecer suposições iniciais para os parâmetros do modelo e o algoritmo os ajusta passo a passo, até que (esperemos) converja na solução aproximada de mínimos quadrados. Portanto, usamos as funções do R, incluindo as rotinas de autoinicialização adequadas, o que simplificou bastante o processo de ajuste. Usamos como suporte o pacote ‘aomisc’.
Sabemos por testes anteriores que tais dados obtiveram resultados positivos utilizando a função de link loglog utilizada na regressão beta. Desse modo, baseando-se em tal usamos a seguinte parametrização:
\[
C' = a + b \times log(-log(1-H))
\]
Uma vez que \(\lim_{H \to 1}a + b \times log(-log(1-H)) = - \infty\), a seguinte modificação foi feita:
\[
C_i' = a + b \times (1_{(H_i < 1)}log(-log(1-H)) + 1_{(H_i = 1)})
\]
"LinkFunctionDuda" <- function(fixed = c(NA, NA), names = c("a", "b"))
{
## Checking arguments
numParm <- 2
if (!is.character(names) | !(length(names) == numParm)) {stop("Not correct 'names' argument")}
if (!(length(fixed) == numParm)) {stop("Not correct 'fixed' argument")}
## Fixing parameters (using argument 'fixed')
notFixed <- is.na(fixed)
parmVec <- rep(0, numParm)
parmVec[!notFixed] <- fixed[!notFixed]
## Defining the non-linear function
fct <- function(x, parm)
{
parmMat <- matrix(parmVec, nrow(parm), numParm, byrow = TRUE)
parmMat[, notFixed] <- parm
a <- parmMat[, 1]; b <- parmMat[, 2]
y = rep(0, length(x))
for(i in 1:length(x)){
if(x[i] == 1)
y[i] = a[i] + b[i]
else
y[i] = a[i] + b[i]*log(-log(1-x[i]))
}
y
}
## Defining self starter function
ssfct <- function(dataf)
{
x <- dataf[, 1]
y <- dataf[, 2]
#regression on pseudo y values
pseudoY <- (y)
pseudoX <- (x)
coefs <- coef( lm(pseudoY ~ pseudoX) )
a <- (coefs[1])
b <- (coefs[2])
return(c(a, b)[notFixed])
}
## Defining names
pnames <- names[notFixed]
## Defining descriptive text
text <- "Power curve (Freundlich equation)"
## Returning the function with self starter and names
returnList <- list(fct = fct, ssfct = ssfct, names = pnames, text = text, noParm = sum(is.na(fixed)))
class(returnList) <- "drcMean"
invisible(returnList)
}
Calculando a regressão como uma função apenas de H
dimension = c(3,4,5,6)
lm.fit = array(list(), 40)
N = c(1e+04, 2e+04, 3e+04, 4e+04, 5e+04, 6e+04, 7e+04, 8e+04, 9e+04, 1e+05)
HC.test.1 = data.frame("H" = numeric(1600),
"C" = numeric(1600),
"D" = numeric(1600),
"N" = numeric(1600),
"index" = numeric(1600),
stringsAsFactors=FALSE)
HC.test = data.frame("H" = numeric(400),
"C" = numeric(400),
"D" = numeric(400),
"N" = numeric(400),
"index" = numeric(400),
stringsAsFactors=FALSE)
b = cc = 0
for(i in 1:length(dimension)){
for(j in 1:length(N)){
if(i*j == 1){
cc = cc + 1
a = c((((j - 1) * 400) + 1):(j * 400))
elements = c(a + b, a + b + 1300, a + b + 2600, a + b + 3900)
HC.test.1$H = HC.BP$H[elements]
HC.test.1$C = HC.BP$C[elements]
HC.test.1$D = HC.BP$D[elements]
HC.test.1$N = HC.BP$N[elements]
HC.test.1$index = HC.BP$index[elements]
lm.fit[[cc]] = drm(C ~ H, data = HC.test.1, fct = LinkFunctionDuda())
}
else{
cc = cc + 1
a = c((((j - 1) * 100) + 1):(j * 100))
elements = c(a + b, a + b + 1300, a + b + 2600, a + b + 3900)
HC.test$H = HC.BP$H[elements]
HC.test$C = HC.BP$C[elements]
HC.test$D = HC.BP$D[elements]
HC.test$N = HC.BP$N[elements]
HC.test$index = HC.BP$index[elements]
lm.fit[[cc]] = drm(C ~ H, data = HC.test, fct = LinkFunctionDuda())
}
}
b = b + 5200
}
a = c(1:400)
elements = c(a, a + 1300, a + 2600, a + 3900)
HC.test.1$H = HC.BP$H[elements]
HC.test.1$C = HC.BP$C[elements]
HC.test.1$D = HC.BP$D[elements]
HC.test.1$N = HC.BP$N[elements]
HC.test.1$index = HC.BP$index[elements]
model = drm(C ~ H, data = HC.test.1, fct = LinkFunctionDuda())
ggplot(HC.test.1, aes(x = H, y = C)) +
geom_point() +
scale_fill_grey() +
geom_line(aes(y = predict(model, HC.test.1, type = 'response')))

No entanto, como podemos verificar abaixo, tal problemática ainda não é solucionada pois para valores muito próximos de \(1\) os resíduos ainda continuam aumentando:
plot(fitted(model), residuals(model, typeRes = "standard"))

pr = predict(model, HC.test.1, type = 'response')
ggplot(HC.test.1, aes(x = H, y = C - pr)) +
geom_point() +
scale_fill_grey() +
geom_line(aes(y =0))

\(Q-Qplot\) dos resíduos
qqPlot(residuals(model, typeRes = "standard"))
[1] 1591 1126

Histograma dos resíduos
plotNormalHistogram(residuals(model, typeRes = "standard"))

hist(residuals(model, typeRes = "standard"), breaks = 200)

Logo, concluímos que com o kernel não-linear proposto conseguimos obter resíduos menos variantes e um bom ajuste em relação aos dados transformados. Entretanto, tal função ainda apresenta problemas quando \(H \to 1\).
LS0tCnRpdGxlOiAiUmVwb3J0IDUgLSBEdWRhIGtlcm5lbCBmdW5jdGlvbiIKYXV0aG9yOiAiRWR1YXJkYSBDaGFnYXMiCmRhdGU6ICJNYXkgNSwgMjAyMCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpgYGB7cn0KaWYoIXJlcXVpcmUoZHJjKSkgaW5zdGFsbC5wYWNrYWdlcygiZHJjIikKaWYoIXJlcXVpcmUoZ2VvUikpIGluc3RhbGwucGFja2FnZXMoImdlb1IiKQppZighcmVxdWlyZShzdGF0cykpIGluc3RhbGwucGFja2FnZXMoInN0YXRzIikKaWYoIXJlcXVpcmUoZ2dwbG90MikpIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQppZighcmVxdWlyZShnZ3JlcGVsKSkgaW5zdGFsbC5wYWNrYWdlcygiZ2dyZXBlbCIpCmlmKCFyZXF1aXJlKG5vcnRlc3QpKSBpbnN0YWxsLnBhY2thZ2VzKCJub3J0ZXN0IikKaWYoIXJlcXVpcmUoZ3JhcGhpY3MpKSBpbnN0YWxsLnBhY2thZ2VzKCJncmFwaGljcyIpCmlmKCFyZXF1aXJlKEVudlN0YXRzKSkgaW5zdGFsbC5wYWNrYWdlcygiRW52U3RhdHMiKQppZighcmVxdWlyZShyY29tcGFuaW9uKSkgaW5zdGFsbC5wYWNrYWdlcygicmNvbXBhbmlvbiIpCmlmKCFyZXF1aXJlKGFvbWlzYykpIGluc3RhbGxfZ2l0aHViKCJPbm9mcmlBbmRyZWFQRy9hb21pc2MiKQpgYGAKCk5lc3RlIGRvY3VtZW50bywgdXRpbGl6YW1vcyB1bSBtb2RlbG8gZGUgcmVncmVzc8OjbyB1c2FuZG8gdW1hIGZ1bsOnw6NvIGJhc2UgbsOjby1saW5lYXIuIApQYXJhIGlzdG8sIHByaW1laXJhbWVudGUgYXBsaWNhbW9zIGEgdHJhbnNmb3JtYcOnw6NvIGRlIEJveC1Db3ggbmEgdmFyacOhdmVsIHJlc3Bvc3RhIGRvIG5vc3NvIHByb2JsZW1hLCBvdSBzZWphLCBub3MgdmFsb3JlcyBkZSBjb21wbGV4aWRhZGUgZXN0YXTDrXN0aWNhOgoKYGBge3J9CkhDLkJQID0gZGF0YS5mcmFtZSgiSCIgPSBudW1lcmljKDIwODAwKSwgCiAgICAgICAgICAgICAgICAgICAiQyIgPSBudW1lcmljKDIwODAwKSwKICAgICAgICAgICAgICAgICAgICJEIiA9IG51bWVyaWMoMjA4MDApLAogICAgICAgICAgICAgICAgICAgInQiID0gbnVtZXJpYygyMDgwMCksIAogICAgICAgICAgICAgICAgICAgIk4iID0gbnVtZXJpYygyMDgwMCksCiAgICAgICAgICAgICAgICAgICAiaW5kZXgiID0gbnVtZXJpYygyMDgwMCksCiAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQoKCkhDLkJQJGluZGV4ID0gYXMuZmFjdG9yKGMoMToyMDgwMCkpCkhDLkJQJE4gPSBhcy5mYWN0b3IocmVwKGMocmVwKDFlKzA0LCA0MDApLCByZXAoMmUrMDQsIDEwMCksIHJlcCgzZSswNCwgMTAwKSwgcmVwKDRlKzA0LCAxMDApLCByZXAoNWUrMDQsIDEwMCksIHJlcCg2ZSswNCwgMTAwKSwgcmVwKDdlKzA0LCAxMDApLCByZXAoOGUrMDQsIDEwMCksIHJlcCg5ZSswNCwgMTAwKSwgcmVwKDFlKzA1LCAxMDApKSwgMTYpKQoKSEMuQlAkdCA9IGFzLmZhY3RvcihyZXAoYyhyZXAoMSwxMzAwKSwgcmVwKDIsMTMwMCksIHJlcCgzLDEzMDApLCByZXAoNCwxMzAwKSksIDQpKQoKZmlsZS5jc3YgPSBkYXRhLmZyYW1lKHJlYWQuY3N2KCIuLi9EYXRhL0hDX3Nlcmllc19mazAuY3N2IikpCgpIQy5CUCRIID0gZmlsZS5jc3ZbLDFdCkhDLkJQJEMgPSBmaWxlLmNzdlssMl0KSEMuQlAkRD0gYXMuZmFjdG9yKGZpbGUuY3N2WywzXSkKCgpwYXJhbWV0ZXJzID0gYm94Y294Zml0KEhDLkJQJEMsIGxhbWJkYTIgPSBGKQpsYW1iZGEgPSBwYXJhbWV0ZXJzJGxhbWJkYVsxXQppZihsYW1iZGE9PTApe1Qubm9ybSA9IGxvZyhIQy5CUCRDKX0KaWYobGFtYmRhIT0wKXtULm5vcm0gPSAoKEhDLkJQJEMpXmxhbWJkYSAtIDEpL2xhbWJkYX0KVC5ub3JtID0gKFQubm9ybSAtIG1pbihULm5vcm0pKS8obWF4KFQubm9ybSkgLSBtaW4oVC5ub3JtKSkKbi5vYnMgPSBzdW0oIWlzLm5hKFQubm9ybSkpClQubm9ybSA9IChULm5vcm0gKiAobi5vYnMgLSAxKSArIDAuNSkvbi5vYnMKCkhDLkJQJEMgPSBULm5vcm0KcGxvdCh4ID0gSEMuQlAkSCwgeSA9IEhDLkJQJEMpCmBgYAoKUGFyYSBzZWxlY2lvbmFyIG8ga2VybmVsIHF1ZSBtZWxob3IgYWp1c3RlIG8gbm9zc28gbW9kZWxvIGFkb3RhbW9zIHVtYSBhYm9yZGFnZW0gZW1ww61yaWNhLiBPdSBzZWphLCBhbmFsaXNhbW9zIG8gcHJvYmxlbWEsIHBsb3RhbW9zIG9zIGRhZG9zIGUgb2JzZXJ2YW1vcyBxdWUgZWxlcyBzZWd1ZW0gdW0gY2VydG8gcGFkcsOjby4gVW0gcHJvYmxlbWEgY29tIGEgcmVncmVzc8OjbyBuw6NvIGxpbmVhciDDqSBxdWUgZWxlIGZ1bmNpb25hIGl0ZXJhdGl2YW1lbnRlOiBwcmVjaXNhbW9zIGZvcm5lY2VyIHN1cG9zacOnw7VlcyBpbmljaWFpcyBwYXJhIG9zIHBhcsOibWV0cm9zIGRvIG1vZGVsbyBlIG8gYWxnb3JpdG1vIG9zIGFqdXN0YSBwYXNzbyBhIHBhc3NvLCBhdMOpIHF1ZSAoZXNwZXJlbW9zKSBjb252ZXJqYSBuYSBzb2x1w6fDo28gYXByb3hpbWFkYSBkZSBtw61uaW1vcyBxdWFkcmFkb3MuIFBvcnRhbnRvLCB1c2Ftb3MgYXMgZnVuw6fDtWVzIGRvIFIsIGluY2x1aW5kbyBhcyByb3RpbmFzIGRlIGF1dG9pbmljaWFsaXphw6fDo28gYWRlcXVhZGFzLCBvIHF1ZSBzaW1wbGlmaWNvdSBiYXN0YW50ZSBvIHByb2Nlc3NvIGRlIGFqdXN0ZS4gVXNhbW9zIGNvbW8gc3Vwb3J0ZSBvIHBhY290ZSAnYW9taXNjJy4KClNhYmVtb3MgcG9yIHRlc3RlcyBhbnRlcmlvcmVzIHF1ZSB0YWlzIGRhZG9zIG9idGl2ZXJhbSByZXN1bHRhZG9zIHBvc2l0aXZvcyB1dGlsaXphbmRvIGEgZnVuw6fDo28gZGUgbGluayBsb2dsb2cgdXRpbGl6YWRhIG5hIHJlZ3Jlc3PDo28gYmV0YS4gRGVzc2UgbW9kbywgYmFzZWFuZG8tc2UgZW0gdGFsIHVzYW1vcyBhIHNlZ3VpbnRlIHBhcmFtZXRyaXphw6fDo286CgokJApDJyA9IGEgKyBiIFx0aW1lcyBsb2coLWxvZygxLUgpKQokJAoKVW1hIHZleiBxdWUgJFxsaW1fe0ggXHRvIDF9YSArIGIgXHRpbWVzIGxvZygtbG9nKDEtSCkpID0gLSBcaW5mdHkkLCBhIHNlZ3VpbnRlIG1vZGlmaWNhw6fDo28gZm9pIGZlaXRhOgoKJCQKQ19pJyA9IGEgKyBiIFx0aW1lcyAoMV97KEhfaSA8IDEpfWxvZygtbG9nKDEtSCkpICsgMV97KEhfaSA9IDEpfSkKJCQKCmBgYHtyfQoiTGlua0Z1bmN0aW9uRHVkYSIgPC0gZnVuY3Rpb24oZml4ZWQgPSBjKE5BLCBOQSksIG5hbWVzID0gYygiYSIsICJiIikpCnsKICAjIyBDaGVja2luZyBhcmd1bWVudHMKICBudW1QYXJtIDwtIDIKICBpZiAoIWlzLmNoYXJhY3RlcihuYW1lcykgfCAhKGxlbmd0aChuYW1lcykgPT0gbnVtUGFybSkpIHtzdG9wKCJOb3QgY29ycmVjdCAnbmFtZXMnIGFyZ3VtZW50Iil9CiAgaWYgKCEobGVuZ3RoKGZpeGVkKSA9PSBudW1QYXJtKSkge3N0b3AoIk5vdCBjb3JyZWN0ICdmaXhlZCcgYXJndW1lbnQiKX0KICAKICAjIyBGaXhpbmcgcGFyYW1ldGVycyAodXNpbmcgYXJndW1lbnQgJ2ZpeGVkJykKICBub3RGaXhlZCA8LSBpcy5uYShmaXhlZCkKICBwYXJtVmVjIDwtIHJlcCgwLCBudW1QYXJtKQogIHBhcm1WZWNbIW5vdEZpeGVkXSA8LSBmaXhlZFshbm90Rml4ZWRdCiAgCiAgIyMgRGVmaW5pbmcgdGhlIG5vbi1saW5lYXIgZnVuY3Rpb24KICBmY3QgPC0gZnVuY3Rpb24oeCwgcGFybSkKICB7CiAgICAKICAgIAogICAgcGFybU1hdCA8LSBtYXRyaXgocGFybVZlYywgbnJvdyhwYXJtKSwgbnVtUGFybSwgYnlyb3cgPSBUUlVFKQogICAgcGFybU1hdFssIG5vdEZpeGVkXSA8LSBwYXJtCiAgICAKICAgIGEgPC0gcGFybU1hdFssIDFdOyBiIDwtIHBhcm1NYXRbLCAyXQogICAgeSA9IHJlcCgwLCBsZW5ndGgoeCkpCiAgICBmb3IoaSBpbiAxOmxlbmd0aCh4KSl7CiAgICAgIGlmKHhbaV0gPT0gMSkKICAgICAgIHlbaV0gPSBhW2ldICsgYltpXQogICAgICBlbHNlCiAgICAgICB5W2ldID0gYVtpXSArIGJbaV0qbG9nKC1sb2coMS14W2ldKSkKICAgIH0KICAgIHkKICB9CiAgCiAgIyMgRGVmaW5pbmcgc2VsZiBzdGFydGVyIGZ1bmN0aW9uCiAgc3NmY3QgPC0gZnVuY3Rpb24oZGF0YWYpCiAgewogICAgeCA8LSBkYXRhZlssIDFdCiAgICB5IDwtIGRhdGFmWywgMl0KICAgIAogICAgI3JlZ3Jlc3Npb24gb24gcHNldWRvIHkgdmFsdWVzCiAgICBwc2V1ZG9ZIDwtICh5KQogICAgcHNldWRvWCA8LSAoeCkKICAgIGNvZWZzIDwtIGNvZWYoIGxtKHBzZXVkb1kgfiBwc2V1ZG9YKSApCiAgICBhIDwtIChjb2Vmc1sxXSkKICAgIGIgPC0gKGNvZWZzWzJdKQogICAgcmV0dXJuKGMoYSwgYilbbm90Rml4ZWRdKQogIH0KICAKICAjIyBEZWZpbmluZyBuYW1lcwogIHBuYW1lcyA8LSBuYW1lc1tub3RGaXhlZF0KICAKICAjIyBEZWZpbmluZyBkZXNjcmlwdGl2ZSB0ZXh0CiAgdGV4dCA8LSAiUG93ZXIgY3VydmUgKEZyZXVuZGxpY2ggZXF1YXRpb24pIgogIAogICMjIFJldHVybmluZyB0aGUgZnVuY3Rpb24gd2l0aCBzZWxmIHN0YXJ0ZXIgYW5kIG5hbWVzCiAgcmV0dXJuTGlzdCA8LSBsaXN0KGZjdCA9IGZjdCwgc3NmY3QgPSBzc2ZjdCwgbmFtZXMgPSBwbmFtZXMsIHRleHQgPSB0ZXh0LCBub1Bhcm0gPSBzdW0oaXMubmEoZml4ZWQpKSkKICAKICBjbGFzcyhyZXR1cm5MaXN0KSA8LSAiZHJjTWVhbiIKICBpbnZpc2libGUocmV0dXJuTGlzdCkKfQoKYGBgCgojQ2FsY3VsYW5kbyBhIHJlZ3Jlc3PDo28gY29tbyB1bWEgZnVuw6fDo28gYXBlbmFzIGRlIEgKCmBgYHtyfQpkaW1lbnNpb24gPSBjKDMsNCw1LDYpCmxtLmZpdCA9IGFycmF5KGxpc3QoKSwgNDApCk4gPSBjKDFlKzA0LCAyZSswNCwgM2UrMDQsIDRlKzA0LCA1ZSswNCwgNmUrMDQsIDdlKzA0LCA4ZSswNCwgOWUrMDQsIDFlKzA1KQoKSEMudGVzdC4xID0gZGF0YS5mcmFtZSgiSCIgPSBudW1lcmljKDE2MDApLCAKICAgICAgICAgICAgICAgICAgICAgICJDIiA9IG51bWVyaWMoMTYwMCksCiAgICAgICAgICAgICAgICAgICAgICAiRCIgPSBudW1lcmljKDE2MDApLAogICAgICAgICAgICAgICAgICAgICAgIk4iID0gbnVtZXJpYygxNjAwKSwKICAgICAgICAgICAgICAgICAgICAgICJpbmRleCIgPSBudW1lcmljKDE2MDApLAogICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKCkhDLnRlc3QgPSBkYXRhLmZyYW1lKCJIIiA9IG51bWVyaWMoNDAwKSwgCiAgICAgICAgICAgICAgICAgICAgICJDIiA9IG51bWVyaWMoNDAwKSwKICAgICAgICAgICAgICAgICAgICAgIkQiID0gbnVtZXJpYyg0MDApLAogICAgICAgICAgICAgICAgICAgICAiTiIgPSBudW1lcmljKDQwMCksCiAgICAgICAgICAgICAgICAgICAgICJpbmRleCIgPSBudW1lcmljKDQwMCksCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCgpiID0gY2MgPSAwCmZvcihpIGluIDE6bGVuZ3RoKGRpbWVuc2lvbikpewogIGZvcihqIGluIDE6bGVuZ3RoKE4pKXsKICAgICAgCiAgICAgIGlmKGkqaiA9PSAxKXsKICAgICAgICBjYyA9IGNjICsgMQogICAgICAgIGEgPSBjKCgoKGogLSAxKSAqIDQwMCkgKyAxKTooaiAqIDQwMCkpCiAgICAgICAgZWxlbWVudHMgPSBjKGEgKyBiLCBhICsgYiArIDEzMDAsIGEgKyBiICsgMjYwMCwgYSArIGIgKyAzOTAwKQogICAgICAgIAogICAgICAgIEhDLnRlc3QuMSRIID0gSEMuQlAkSFtlbGVtZW50c10KICAgICAgICBIQy50ZXN0LjEkQyA9IEhDLkJQJENbZWxlbWVudHNdCiAgICAgICAgSEMudGVzdC4xJEQgPSBIQy5CUCREW2VsZW1lbnRzXQogICAgICAgIEhDLnRlc3QuMSROID0gSEMuQlAkTltlbGVtZW50c10KICAgICAgICBIQy50ZXN0LjEkaW5kZXggPSBIQy5CUCRpbmRleFtlbGVtZW50c10KICAgICAgICAKICAgICAgICBsbS5maXRbW2NjXV0gPSBkcm0oQyB+IEgsIGRhdGEgPSBIQy50ZXN0LjEsIGZjdCA9IExpbmtGdW5jdGlvbkR1ZGEoKSkKICAgICAgICAKICAgICAgfQogICAgICBlbHNlewogICAgICAgIGNjID0gY2MgKyAxCiAgICAgICAgYSA9IGMoKCgoaiAtIDEpICogMTAwKSArIDEpOihqICogMTAwKSkKICAgICAgICBlbGVtZW50cyA9IGMoYSArIGIsIGEgKyBiICsgMTMwMCwgYSArIGIgKyAyNjAwLCBhICsgYiArIDM5MDApCiAgICAgIAogICAgICAgIEhDLnRlc3QkSCA9IEhDLkJQJEhbZWxlbWVudHNdCiAgICAgICAgSEMudGVzdCRDID0gSEMuQlAkQ1tlbGVtZW50c10KICAgICAgICBIQy50ZXN0JEQgPSBIQy5CUCREW2VsZW1lbnRzXQogICAgICAgIEhDLnRlc3QkTiA9IEhDLkJQJE5bZWxlbWVudHNdCiAgICAgICAgSEMudGVzdCRpbmRleCA9IEhDLkJQJGluZGV4W2VsZW1lbnRzXQogICAgICAgIAogICAgICAgIGxtLmZpdFtbY2NdXSA9IGRybShDIH4gSCwgZGF0YSA9IEhDLnRlc3QsIGZjdCA9IExpbmtGdW5jdGlvbkR1ZGEoKSkKICAgICAgfQogIH0gCiAgYiA9IGIgKyA1MjAwCn0KCmEgPSBjKDE6NDAwKQplbGVtZW50cyA9IGMoYSwgYSArIDEzMDAsIGEgKyAyNjAwLCBhICsgMzkwMCkKSEMudGVzdC4xJEggPSBIQy5CUCRIW2VsZW1lbnRzXQpIQy50ZXN0LjEkQyA9IEhDLkJQJENbZWxlbWVudHNdCkhDLnRlc3QuMSREID0gSEMuQlAkRFtlbGVtZW50c10KSEMudGVzdC4xJE4gPSBIQy5CUCROW2VsZW1lbnRzXQpIQy50ZXN0LjEkaW5kZXggPSBIQy5CUCRpbmRleFtlbGVtZW50c10KCm1vZGVsID0gZHJtKEMgfiBILCBkYXRhID0gSEMudGVzdC4xLCBmY3QgPSBMaW5rRnVuY3Rpb25EdWRhKCkpCiAgICAgIApnZ3Bsb3QoSEMudGVzdC4xLCBhZXMoeCA9IEgsIHkgPSBDKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfZmlsbF9ncmV5KCkgKwogIGdlb21fbGluZShhZXMoeSA9IHByZWRpY3QobW9kZWwsIEhDLnRlc3QuMSwgdHlwZSA9ICdyZXNwb25zZScpKSkgCmBgYAoKTm8gZW50YW50bywgY29tbyBwb2RlbW9zIHZlcmlmaWNhciBhYmFpeG8sIHRhbCBwcm9ibGVtw6F0aWNhIGFpbmRhIG7Do28gw6kgc29sdWNpb25hZGEgcG9pcyBwYXJhIHZhbG9yZXMgbXVpdG8gcHLDs3hpbW9zIGRlICQxJCBvcyByZXPDrWR1b3MgYWluZGEgY29udGludWFtIGF1bWVudGFuZG86CgpgYGB7cn0KcGxvdChmaXR0ZWQobW9kZWwpLCByZXNpZHVhbHMobW9kZWwsIHR5cGVSZXMgPSAic3RhbmRhcmQiKSkKCnByID0gcHJlZGljdChtb2RlbCwgSEMudGVzdC4xLCB0eXBlID0gJ3Jlc3BvbnNlJykKZ2dwbG90KEhDLnRlc3QuMSwgYWVzKHggPSBILCB5ID0gQyAtIHByKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfZmlsbF9ncmV5KCkgKwogIGdlb21fbGluZShhZXMoeSA9MCkpIApgYGAKCiMjIyRRLVFwbG90JCBkb3MgcmVzw61kdW9zCgpgYGB7cn0KcXFQbG90KHJlc2lkdWFscyhtb2RlbCwgdHlwZVJlcyA9ICJzdGFuZGFyZCIpKSAKYGBgCgoKIyMjSGlzdG9ncmFtYSBkb3MgcmVzw61kdW9zCgpgYGB7cn0KcGxvdE5vcm1hbEhpc3RvZ3JhbShyZXNpZHVhbHMobW9kZWwsIHR5cGVSZXMgPSAic3RhbmRhcmQiKSkgCmhpc3QocmVzaWR1YWxzKG1vZGVsLCB0eXBlUmVzID0gInN0YW5kYXJkIiksIGJyZWFrcyA9IDIwMCkKYGBgCgpMb2dvLCBjb25jbHXDrW1vcyBxdWUgY29tIG8ga2VybmVsIG7Do28tbGluZWFyIHByb3Bvc3RvIGNvbnNlZ3VpbW9zIG9idGVyIHJlc8OtZHVvcyBtZW5vcyB2YXJpYW50ZXMgZSB1bSBib20gYWp1c3RlIGVtIHJlbGHDp8OjbyBhb3MgZGFkb3MgdHJhbnNmb3JtYWRvcy4gRW50cmV0YW50bywgdGFsIGZ1bsOnw6NvIGFpbmRhIGFwcmVzZW50YSBwcm9ibGVtYXMgcXVhbmRvICRIIFx0byAxJC4K